plexpy/data/interfaces/default/settings.html
2018-01-11 22:58:30 -08:00

2182 lines
122 KiB
HTML

<%inherit file="base.html"/>
<%!
import os
import sys
import plexpy
from plexpy import common, notifiers
from plexpy.helpers import anon_url
available_notification_agents = sorted(notifiers.available_notification_agents(), key=lambda k: k['label'])
%>
<%def name="headIncludes()">
</%def>
<%def name="headerIncludes()">
<link href="${http_root}css/selectize.bootstrap3.css" rel="stylesheet" />
<link href="${http_root}css/selectize.min.css" rel="stylesheet" />
</%def>
<%def name="body()">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class='card-back-full'>
<div class="header-bar">
<span><i class="fa fa-cogs"></i> Settings</span>
</div>
<div class="button-bar">
% if config['check_github']:
<button id="menu_link_update_check" class="btn btn-dark"><i class="fa fa-arrow-circle-up"></i> Check for Updates</button>
% endif
<button id="menu_link_restart" class="btn btn-dark"><i class="fa fa-refresh"></i> Restart</button>
<button id="menu_link_shutdown" class="btn btn-dark"><i class="fa fa-power-off"></i> Shut Down</button>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Nav tabs -->
<div class="col-md-3">
<ul class="nav-settings list-unstyled" role="tablist">
<li role="presentation" class="active"><a href="#tabs-help_info" aria-controls="tabs-help_info" role="tab" data-toggle="tab">Help & Info</a></li>
<li role="presentation"><a href="#tabs-general" aria-controls="tabs-general" role="tab" data-toggle="tab">General</a></li>
<li role="presentation"><a href="#tabs-homepage" aria-controls="tabs-homepage" role="tab" data-toggle="tab">Homepage</a></li>
<li role="presentation"><a href="#tabs-web_interface" aria-controls="tabs-web_interface" role="tab" data-toggle="tab">Web Interface</a></li>
<li role="presentation"><a href="#tabs-access_control" aria-controls="tabs-access_control" role="tab" data-toggle="tab">Access Control</a></li>
<li role="presentation"><a href="#tabs-plex_media_server" aria-controls="tabs-plex_media_server" role="tab" data-toggle="tab">Plex Media Server</a></li>
<li role="presentation"><a href="#tabs-plextv_account" aria-controls="tabs-plextv_account" role="tab" data-toggle="tab">Plex.tv Account</a></li>
<li role="presentation"><a href="#tabs-activity_monitoring" aria-controls="tabs-activity_monitoring" role="tab" data-toggle="tab">Activity Monitoring</a></li>
<li role="presentation"><a href="#tabs-notifications" aria-controls="tabs-notifications" role="tab" data-toggle="tab">Notifications</a></li>
<li role="presentation"><a href="#tabs-notification_agents" aria-controls="tabs-notification_agents" role="tab" data-toggle="tab">Notification Agents</a></li>
<li role="presentation"><a href="#tabs-extra_settings" aria-controls="tabs-extra_settings" role="tab" data-toggle="tab">Extra Settings</a></li>
<li role="presentation"><a href="#tabs-import_backups" aria-controls="tabs-import_backups" role="tab" data-toggle="tab">Import & Backups</a></li>
<li role="presentation"><a href="#tabs-android_app" aria-controls="tabs-android_app" role="tab" data-toggle="tab">Tautulli Remote Android App <sup><small>beta</small></sup></a></li>
</ul>
</div>
<div class="col-md-9">
<form action="configUpdate" method="post" class="form" id="configUpdate" data-parsley-validate>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tabs-help_info">
% if common.VERSION_NUMBER:
<div class="padded-header">
<h3>Version ${common.VERSION_NUMBER} <small><a id="changelog-modal-link" href="#"><i class="fa fa-info-circle"></i> Changelog</a></small></h3>
</div>
% endif
<div class="padded-header">
<h3>Tautulli Configuration</h3>
</div>
<div id="plexpy-configuration-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading configuration table...</div>
<br>
</div>
<div class="padded-header">
<h3>Tautulli Scheduled Tasks</h3>
</div>
<div id="plexpy-scheduler-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading scheduler table...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-general">
<div class="padded-header">
<h3>Display Settings</h3>
</div>
<div class="form-group">
<label for="date_format">Date Format</label>
<div class="row">
<div class="col-md-4">
<input type="text" class="form-control" id="date_format" name="date_format" value="${config['date_format']}" data-parsley-trigger="change" required>
</div>
</div>
<p class="help-block">Set your preferred date format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
</div>
<div class="form-group">
<label for="date_format">Time Format</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" id="time_format" name="time_format" value="${config['time_format']}" data-parsley-trigger="change" required>
</div>
</div>
<p class="help-block">Set your preferred time format. <a href="javascript:void(0)" data-target="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="week_start_monday" name="week_start_monday" value="1" ${config['week_start_monday']}> Week Starting on Monday
</label>
<p class="help-block">Change the "<em>Play by day of week</em>" graph to start on Monday. Default is start on Sunday.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="group_history_tables" name="group_history_tables" value="1" ${config['group_history_tables']}> Group Table and Watch Statistics History
</label>
<p class="help-block">Group successive play history by the same user as a single entry in the tables and watch statistics.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="history_table_activity" name="history_table_activity" value="1" ${config['history_table_activity']}> Current Activity in History Tables
</label>
<p class="help-block">Include current activity in the history tables. Statistics will not be counted until the stream has ended.</p>
</div>
<div class="form-group">
<label for="movie_watched_percent">Movie Watched Percent</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="movie_watched_percent" name="movie_watched_percent" value="${config['movie_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" data-parsley-errors-container="#movie_watched_percent_error" required>
</div>
<div id="movie_watched_percent_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Set the percentage for a movie to be considered as watched. Minimum 50, Maximum 95.</p>
</div>
<div class="form-group">
<label for="tv_watched_percent">TV Episode Watched Percent</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="tv_watched_percent" name="tv_watched_percent" value="${config['tv_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" data-parsley-errors-container="#tv_watched_percent_error" required>
</div>
<div id="tv_watched_percent_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Set the percentage for a TV episode to be considered as watched. Minimum 50, Maximum 95.</p>
</div>
<div class="form-group">
<label for="music_watched_percent">Music Listened Percent</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="music_watched_percent" name="music_watched_percent" value="${config['music_watched_percent']}" size="5" data-parsley-range="[50,95]" data-parsley-trigger="change" data-parsley-errors-container="#music_watched_percent_error" required>
</div>
<div id="music_watched_percent_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Set the percentage for a music track to be considered as listened. Minimum 50, Maximum 95.</p>
</div>
<div class="padded-header">
<h3>Updates</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="check_github" name="check_github" value="1" ${config['check_github']}> Enable Updates
</label>
<p class="help-block">Check for Tautulli updates periodically.</p>
</div>
<div id="git_update_options">
<div class="checkbox">
<label>
<input type="checkbox" id="plexpy_auto_update" name="plexpy_auto_update" value="1" ${config['plexpy_auto_update']}> Update Automatically
</label>
<p class="help-block">Update Tautulli automatically if an update is available.</p>
</div>
<div class="form-group">
<label for="git_token">GitHub API Token</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="git_token" name="git_token" value="${config['git_token']}" data-parsley-trigger="change">
</div>
</div>
<p class="help-block">Optional: Use your own GitHub API token when checking for updates.
</div>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-homepage">
<div class="padded-header">
<h3>Sections</h3>
</div>
<p class="help-block">
Select the sections to show on the homepage.
Drag the items below to reorder your homepage content.
</p>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled" id="sortable_home_sections" data-parsley-trigger="change">
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hsec-current_activity" name="hsec-current_activity" value="current_activity"> Current Activity
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hsec-watch_stats" name="hsec-watch_stats" value="watch_stats"> Watch Statistics
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hsec-library_stats" name="hsec-library_stats" value="library_stats"> Library Statistics
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hsec-recently_added" name="hsec-recently_added" value="recently_added"> Recently Added
</label>
</li>
</ul>
<input type="text" id="home_sections" name="home_sections" style="display: none;"/>
</div>
</div>
<div class="padded-header">
<h3>Watch Statistics</h3>
</div>
<div class="form-group">
<p class="help-block">
Select the cards to show in the watch statistics on the home page.
Drag the items below to reorder your homepage content.
</p>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled" id="sortable_home_stats_cards" data-parsley-trigger="change">
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_movies" name="hscard-top_movies" value="top_movies"> Most Watched Movie
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-popular_movies" name="hscard-popular_movies" value="popular_movies"> Most Popular Movie
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_tv" name="hscard-top_tv" value="top_tv"> Most Watched TV
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-popular_tv" name="hscard-popular_tv" value="popular_tv"> Most Popular TV
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_music" name="hscard-top_music" value="top_music"> Most Listened to Artist
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-popular_music" name="hscard-popular_music" value="popular_music"> Most Popular Artist
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-last_watched" name="hscard-last_watched" value="last_watched"> Last Watched
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_users" name="hscard-top_users" value="top_users"> Most Active User
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_platforms" name="hscard-top_platforms" value="top_platforms"> Most Active Platform
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-most_concurrent" name="hscard-most_concurrent" value="most_concurrent"> Most Concurrent Streams
</label>
</li>
</ul>
<input type="text" id="home_stats_cards" name="home_stats_cards" style="display: none;" />
</div>
</div>
</div>
<div class="padded-header">
<h3>Library Statistics</h3>
</div>
<div class="form-group">
<p class="help-block">
Select the cards to show in the library statistics on the home page.
Drag the items below to reorder your homepage content.
</p>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled" id="sortable_home_library_cards" data-parsley-trigger="change"></ul>
<input type="text" id="home_library_cards" name="home_library_cards" style="display: none;" />
</div>
</div>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-web_interface">
<div class="padded-header">
<h3>Web Interface</h3>
</div>
<p class="help-block">Note: Web interface changes require a restart.</p>
<div class="form-group">
<label for="http_host">HTTP Host</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="http_host" name="http_host" value="${config['http_host']}" data-parsley-trigger="change" required>
</div>
</div>
<p class="help-block">localhost or an IP address to bind the web server to. Default 0.0.0.0 to bind to all interfaces.</p>
</div>
<div class="form-group">
<label for="http_port">HTTP Port</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control http-settings" data-parsley-type="integer" id="http_port" name="http_port" value="${config['http_port']}" data-parsley-trigger="change" data-parsley-errors-container="#http_port_error" required>
</div>
<div id="http_port_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Port to bind web server to. Note that ports below 1024 may require root.</p>
</div>
<div class="form-group">
<label for="http_root">HTTP Root</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="http_root" name="http_root" value="${config['http_root']}" data-parsley-trigger="change">
</div>
</div>
<p class="help-block">The base URL of the web server. Used for reverse proxies.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" class="http-settings" name="http_proxy" id="http_proxy" value="1" ${config['http_proxy']}> Enable HTTP Proxy
</label>
<p class="help-block">Respect the X-Forwarded-Proto header. Used for reverse proxies with SSL.</p>
</div>
<br />
<div class="checkbox">
<label>
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" ${config['launch_browser']}> Launch Browser on Startup
</label>
<p class="help-block">Launch browser pointed to Tautulli on startup.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" class="http-settings" name="enable_https" id="enable_https" value="1" ${config['enable_https']} /> Enable HTTPS
</label>
<p class="help-block">Enable HTTPS for web server for encrypted communication.</p>
</div>
<div id="https_options">
<div class="checkbox">
<label>
<input type="checkbox" class="http-settings" name="https_create_cert" id="https_create_cert" value="1" ${config['https_create_cert']} /> Create Self-signed Certificate
</label>
<p class="help-block">Check to have Tautulli create a self-signed SSL certificate. Uncheck if you want to use your own certificate.</p>
</div>
<div id="https_options_self-signed">
<div class="form-group">
<label for="https_domain">HTTPS Domains</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="https_domain" name="https_domain" value="${config['https_domain']}">
</div>
</div>
<p class="help-block">The domain names used to access Tautulli, separated by commas (,).</p>
</div>
<div class="form-group">
<label for="https_ip">HTTPS IPs</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="https_ip" name="https_ip" value="${config['https_ip']}">
</div>
</div>
<p class="help-block">The IP addresses used to access Tautulli, separated by commas (,).</p>
</div>
</div>
<div class="form-group">
<label for="https_cert">HTTPS Certificate</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="https_cert" name="https_cert" value="${config['https_cert']}">
</div>
</div>
<p class="help-block">The location of the SSL certificate.</p>
</div>
<div class="form-group">
<label for="https_cert_chain">HTTPS Certificate Chain</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="https_cert_chain" name="https_cert_chain" value="${config['https_cert_chain']}">
</div>
</div>
<p class="help-block">The location of the SSL certificate chain.</p>
</div>
<div class="form-group">
<label for="https_key">HTTPS Key</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control http-settings" id="https_key" name="https_key" value="${config['https_key']}">
</div>
</div>
<p class="help-block">The location of the SSL key.</p>
</div>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-access_control">
<div class="padded-header">
<h3>Authentication</h3>
</div>
<p class="help-block">Authentication changes require a restart.</p>
<div class="form-group">
<label for="http_username">HTTP Username</label>
<div class="row">
<div class="col-md-4">
<input type="text" class="form-control auth-settings" id="http_username" name="http_username" value="${config['http_username']}" size="30">
</div>
</div>
<p class="help-block">Username for web server authentication. Leave empty to disable.</p>
</div>
<div class="form-group">
<label for="http_password">HTTP Password</label>
<div class="row">
<div class="col-md-4">
<input type="password" class="form-control auth-settings" id="http_password" name="http_password" value="${config['http_password']}" size="30" autocomplete="new-password">
</div>
<div id="http_hash_password_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Password for web server authentication. Leave empty to disable.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="http_hash_password" id="http_hash_password" value="1" ${config['http_hash_password']} data-parsley-trigger="change"> Hash Password in the Config File
</label>
<span id="hashPasswordCheck" style="color: #eb8600; padding-left: 10px;"></span>
<p class="help-block">Store a hashed password in the config file.<br />Warning: Your password cannot be recovered if forgotten!</p>
</div>
<input type="text" id="http_hashed_password" name="http_hashed_password" value="${config['http_hashed_password']}" style="display: none;" data-parsley-trigger="change" data-parsley-type="integer" data-parsley-range="[0, 1]"
data-parsley-errors-container="#http_hash_password_error" data-parsley-error-message="Cannot un-hash password, please set a new password." data-parsley-no-focus required>
<div class="checkbox">
<label>
<input type="checkbox" class="auth-settings" name="http_plex_admin" id="http_plex_admin" value="1" ${config['http_plex_admin']} data-parsley-trigger="change"> Allow Plex Admin
</label>
<span id="allowPlexCheck" style="color: #eb8600; padding-left: 10px;"></span>
<p class="help-block">Allow the Plex server admin to login as a Tautulli admin using their Plex.tv account.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" class="auth-settings" name="http_basic_auth" id="http_basic_auth" value="1" ${config['http_basic_auth']} data-parsley-trigger="change"> Use Basic Authentication
</label>
<p class="help-block">Use basic HTTP authentication instead of the HTML login form.</p>
</div>
<input type="checkbox" name="auth_changed" id="auth_changed" value="1" style="display: none;">
<div class="padded-header">
<h3>Guest Access</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="allow_guest_access" name="allow_guest_access" value="1" ${config['allow_guest_access']}> Allow Guest Access to Tautulli
</label>
<span id="allowGuestCheck" style="color: #eb8600; padding-left: 10px;"></span>
<p class="help-block">Allow shared users to login to Tautulli using their Plex.tv account. Individual user access needs to be enabled from Users > Edit Mode.</p>
</div>
<div class="padded-header">
<h3>API</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="api_enabled" name="api_enabled" value="1" ${config['api_enabled']}> Enable API
</label>
<p class="help-block">Allow remote applications to interface with Tautulli.</p>
</div>
<div id="apioptions">
<div class="form-group">
<label for="api_key">API key</label>
<div class="row">
<div class="col-md-6">
<div class="input-group">
<input class="form-control" type="text" name="api_key" id="api_key" value="${config['api_key']}" size="20" readonly>
<span class="input-group-btn">
<button class="btn btn-form" type="button" id="generate_api">Generate</button>
</span>
</div>
</div>
</div>
<p class="help-block">Current API key: <strong>${config['api_key']}</strong></p>
</div>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-plex_media_server">
<div class="padded-header">
<h3>Plex Media Server <small style="color: #fff;">Version <span id="pms_version">${config['pms_version']}</span></small></h3>
</div>
<div class="checkbox">
% if config['pms_is_cloud']:
<label>
<input type="checkbox" id="monitor_pms_updates" name="monitor_pms_updates" value="1" disabled> Monitor Plex Updates
</label>
<span style="color: #eb8600; padding-left: 10px;"> Not available for Plex Cloud servers.</span>
% else:
<label>
<input type="checkbox" id="monitor_pms_updates" name="monitor_pms_updates" value="1" ${config['monitor_pms_updates']}> Monitor Plex Updates
</label>
% endif
<p class="help-block">Enable to have Tautulli check if updates are available for the Plex Media Server.</p>
</div>
<div id="pms_update_options">
<div class="form-group">
<div class="row">
<div class="col-md-2">
<label for="pms_update_channel">Update Channel</label>
<select class="form-control" id="pms_update_channel" name="pms_update_channel">
<option value="public">Public</option>
</select>
</div>
<div class="col-md-5">
<label for="pms_update_distro_build">Release</label>
<select class="form-control" id="pms_update_distro_build" name="pms_update_distro_build">
</select>
<input type="hidden" class="form-control" id="pms_update_distro" name="pms_update_distro">
</div>
</div>
</div>
</div>
<div class="checkbox">
% if config['pms_is_cloud']:
<label>
<input type="checkbox" id="monitor_remote_access" name="monitor_remote_access" value="1" disabled> Monitor Plex Remote Access
</label>
<span style="color: #eb8600; padding-left: 10px;"> Not available for Plex Cloud servers.</span>
% else:
<label>
<input type="checkbox" id="monitor_remote_access" name="monitor_remote_access" value="1" ${config['monitor_remote_access']}> Monitor Plex Remote Access
</label>
<span id="remoteAccessCheck" style="color: #eb8600; padding-left: 10px;"></span>
% endif
<p class="help-block">Enable to have Tautulli check if remote access to the Plex Media Server goes down.</p>
</div>
<div class="form-group has-feedback" id="pms_ip_group">
<label for="pms_ip">Plex IP or Hostname</label>
<div class="row">
<div class="col-md-6">
<div class="input-group">
<input type="text" class="pms-settings form-control" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30" data-parsley-trigger="change" aria-describedby="server-verified" data-parsley-errors-container="#pms_ip_error" required>
<span class="input-group-btn">
<button class="btn btn-form" type="button" id="verify_server_button">Verify Server</button>
</span>
</div>
<span class="form-control-feedback" id="pms_verify" aria-hidden="true" style="display: none; right: 110px;"></span>
</div>
<div id="pms_ip_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">IP Address or hostname for Plex Media Server.</p>
</div>
<div class="form-group">
<label for="pms_port">Plex Port</label>
<div class="row">
<div class="col-md-2">
<input data-parsley-type="integer" class="pms-settings form-control" type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30" data-parsley-trigger="change" data-parsley-errors-container="#pms_port_error" required>
</div>
<div id="pms_port_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Port that Plex Media Server is listening on.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="pms_is_remote" name="pms_is_remote" value="1" ${config['pms_is_remote']}> Remote Server
</label>
<p class="help-block">Check this if your Plex Server is not on the same local network as Tautulli.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="pms_ssl" name="pms_ssl" value="1" ${config['pms_ssl']}> Use SSL
</label>
<p class="help-block">If you have secure connections enabled on your Plex Server, communicate with it securely.</p>
</div>
<div class="checkbox">
% if config['pms_is_cloud']:
<label>
<input type="checkbox" id="pms_url_manual" name="pms_url_manual" value="1" disabled> Manual Connection
</label>
<span style="color: #eb8600; padding-left: 10px;"> Not available for Plex Cloud servers.</span>
% else:
<label>
<input type="checkbox" id="pms_url_manual" name="pms_url_manual" value="1" ${config['pms_url_manual']}> Manual Connection
</label>
% endif
<p class="help-block">Use the user defined connection details. Do not retrieve the server connection URL automatically.</p>
</div>
<div class="form-group">
<label for="pms_logs_folder">Plex Web URL</label>
<div class="row">
<div class="col-md-6">
<div class="input-group">
<input type="text" class="form-control" id="pms_web_url" name="pms_web_url" value="${config['pms_web_url']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^https?:\/\/\S+$|^https:\/\/app.plex.tv\/desktop$" data-parsley-errors-container="#pms_web_url_error" data-parsley-error-message="Invalid Plex Web URL.">
<span class="input-group-btn">
<button class="btn btn-form" type="button" id="test_pms_web_button">Test URL</button>
</span>
</div>
</div>
<div id="pms_web_url_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">
Optional: Manually override the Plex Web URL used for click-through links on the media info pages and notifications. Default <strong>https://app.plex.tv/desktop</strong>.
</p>
</div>
<input type="hidden" id="pms_identifier" name="pms_identifier" value="${config['pms_identifier']}">
<input type="checkbox" name="server_changed" id="server_changed" value="1" style="display: none;">
<div class="padded-header">
<h3>Plex Logs</h3>
</div>
<div class="form-group">
<label for="pms_logs_folder">Logs Folder</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="pms_logs_folder" name="pms_logs_folder" value="${config['pms_logs_folder']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^[^\~\%]" data-parsley-errors-container="#pms_logs_folder_error" data-parsley-error-message="Shortcuts are not recognized.">
</div>
<div id="pms_logs_folder_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">
Optional: Set your Plex logs folder to use Tautulli as a log viewer. Plex logs are not needed for Tautulli to function.
A complete folder path is required, shortcuts are not recognized, and the logs must be accessible from the machine where Tautulli is installed.
<a href="${anon_url('https://support.plex.tv/hc/en-us/articles/200250417-Plex-Media-Server-Log-Files')}" target="_blank">Click here</a> for help.
</p>
</div>
<input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully">
</div>
<div role="tabpanel" class="tab-pane" id="tabs-plextv_account">
<div class="padded-header">
<h3>Plex.tv Authentication</h3>
</div>
<div class="form-group">
<label for="pms_token">Plex.tv Account Token</label>
<div class="row">
<div class="col-md-6">
<div class="input-group">
<input type="text" class="form-control" id="pms_token" name="pms_token" value="${config['pms_token']}" data-parsley-trigger="change" data-parsley-errors-container="#pms_token_error" required>
<span class="input-group-btn">
<button class="btn btn-form" type="button" data-toggle="modal" data-target="#pms-auth-modal">Fetch Token</button>
</span>
</div>
</div>
<div id="pms_token_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Token for Plex.tv authentication.</p>
</div>
<div class="padded-header">
<h3>Friends List</h3>
</div>
<div class="form-group">
<label for="refresh_users_interval">Users List Refresh Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_users_interval" name="refresh_users_interval" value="${config['refresh_users_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_users_interval_error" required>
</div>
<div id="refresh_users_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The interval (in hours) Tautulli will request an updated friends list from Plex.tv. Minimum 1, maximum 24, default 12.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="refresh_users_on_startup" name="refresh_users_on_startup" value="1" ${config['refresh_users_on_startup']}> Refresh Users List on Startup
</label>
<p class="help-block">Refresh the users list when Tautulli starts.</p>
</div>
<div class="padded-header">
<h3>Libraries List</h3>
</div>
<div class="form-group">
<label for="refresh_libraries_interval">Libraries List Refresh Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_libraries_interval" name="refresh_libraries_interval" value="${config['refresh_libraries_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_libraries_interval_error" required>
</div>
<div id="refresh_libraries_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The interval (in hours) Tautulli will request an updated libraries list from your Plex Media Server. Minimum 1, maximum 24, default 12.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="refresh_libraries_on_startup" name="refresh_libraries_on_startup" value="1" ${config['refresh_libraries_on_startup']}> Refresh Libraries List on Startup
</label>
<p class="help-block">Refresh the libraries list when Tautulli starts.</p>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-activity_monitoring">
<div class="padded-header">
<h3>History Logging</h3>
</div>
<div class="form-group">
<label for="logging_ignore_interval">Ignore Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="logging_ignore_interval" name="logging_ignore_interval" value="${config['logging_ignore_interval']}" size="5" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#logging_ignore_interval_error" required>
</div>
<div id="logging_ignore_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The interval (in seconds) an item must be in a playing state before logging it. 0 to disable.</p>
</div>
<div class="padded-header">
<h3>Buffer Warnings</h3>
</div>
<p class="help-block">Note: Buffer warnings only work on certain Plex clients. Android and Plex Web do not report buffer events accurately or at all.</p>
<div class="form-group">
<label for="buffer_threshold">Buffer Threshold</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="buffer_threshold" name="buffer_threshold" value="${config['buffer_threshold']}" data-parsley-range="[0,50]" data-parsley-trigger="change" data-parsley-errors-container="#buffer_threshold_error" required>
</div>
<div id="buffer_threshold_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">How many buffer events should we wait before triggering the first warning. Buffer events increment on each monitor ping if play state is buffering. 0 to disable buffer warnings.</p>
</div>
<div class="form-group">
<label for="buffer_wait">Buffer Wait</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="buffer_wait" name="buffer_wait" value="${config['buffer_wait']}" data-parsley-min="0" data-parsley-trigger="change" data-parsley-errors-container="#buffer_wait_error" required>
</div>
<div id="buffer_wait_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The value (in seconds) Tautulli should wait before triggering the next buffer warning. 0 to always trigger.</p>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-notifications">
<div class="padded-header">
<h3>Current Activity Notifications</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="notify_consecutive" id="notify_consecutive" value="1" ${config['notify_consecutive']}> Allow Consecutive Notifications
</label>
<p class="help-block">Enable to allow sending of consecutive notifications (i.e. both watched &amp; stopped notifications).</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="notify_concurrent_by_ip" id="notify_concurrent_by_ip" value="1" ${config['notify_concurrent_by_ip']}> User Concurrent Streams Notifications by IP Address
</label>
<p class="help-block">Enable to only send a notification of concurrent streams by a single user from different IP addresses.</p>
</div>
<div class="form-group">
<label for="notify_concurrent_threshold">User Concurrent Stream Threshold</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="notify_concurrent_threshold" name="notify_concurrent_threshold" value="${config['notify_concurrent_threshold']}" data-parsley-min="2" data-parsley-trigger="change" data-parsley-errors-container="#notify_concurrent_threshold_error" required>
</div>
<div id="notify_concurrent_threshold_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The number of concurrent streams by a single user for Tautulli to trigger a notification. Minimum 2.</p>
</div>
<div class="padded-header">
<h3>Recently Added Notifications</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="notify_group_recently_added_parent" id="notify_group_recently_added_parent" value="1" ${config['notify_group_recently_added_parent']}> Group Notifications by Season or Album
</label>
<p class="help-block">
Enable to only send one season or album notification when multiple episodes or tracks are added. Movies, single episodes, and single tracks are unaffected.<br />
Note: An episode/track range can be shown (e.g. 01-12), but all other episode/track metadata will be unavailable.
</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="notify_group_recently_added_grandparent" id="notify_group_recently_added_grandparent" value="1" ${config['notify_group_recently_added_grandparent']}> Group Notifications by TV Show or Artist
</label>
<p class="help-block">
Enable to only send one TV show or artist notification when multiple seasons or albums are added. Movies, single episodes, and single tracks are unaffected.<br />
Note: A season range can be shown (e.g. 1-3), but all other season/episode/album/track metadata will be unavailable.
</p>
</div>
<div class="form-group">
<label for="notify_recently_added_delay">Notification Delay</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="notify_recently_added_delay" name="notify_recently_added_delay" value="${config['notify_recently_added_delay']}" size="5" data-parsley-min="60" data-parsley-trigger="change" data-parsley-errors-container="#notify_recently_added_delay_error" required>
</div>
<div id="notify_recently_added_delay_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">Set the delay (in seconds) to wait for consecutive recently added items to group together and to allow metadata to be processed before sending the notification. Minimum 60 seconds.</p>
</div>
<!--<div class="checkbox">
<label>
<input type="checkbox" name="notify_recently_added_upgrade" id="notify_recently_added_upgrade" value="1" ${config['notify_recently_added_upgrade']}> Send a Notification for New Versions <span style="color: #eb8600; padding-left: 10px;">[Not working]</span>
</label>
<p class="help-block">
Enable to send another recently added notification when adding a new version of existing media.<br />
Note: If multiple versions are available, Tautulli will assume the higher quality one is newer.
</p>
</div>-->
<div class="padded-header">
<h3>3rd Party APIs</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="notify_upload_posters" id="notify_upload_posters" value="1" ${config['notify_upload_posters']}> Upload Posters to Imgur for Notifications
</label>
<p class="help-block">Enable to upload Plex posters to Imgur for notifications. Disable if posters are not being used to save bandwidth.</p>
</div>
<div id="imgur_upload_options">
<div class="form-group">
<label for="imgur_client_id">Imgur Client ID</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="imgur_client_id" name="imgur_client_id" value="${config['imgur_client_id']}" data-parsley-trigger="change">
</div>
</div>
<p class="help-block">
Enter your Imgur API client ID in order to upload posters.
You can register a new application <a href="${anon_url('https://api.imgur.com/oauth2/addclient')}" target="_blank">here</a>.<br />
</div>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="themoviedb_lookup" id="themoviedb_lookup" value="1" ${config['themoviedb_lookup']}> Lookup TheMovieDB Links
</label>
<p class="help-block">Enable to lookup links to TheMovieDB (and IMDb if needed) for movies and TV shows when available.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" name="tvmaze_lookup" id="tvmaze_lookup" value="1" ${config['tvmaze_lookup']}> Lookup TVmaze Links
</label>
<p class="help-block">Enable to lookup links to TVmaze (and IMDb if needed) for TV shows when available.</p>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-notification_agents">
<div class="padded-header">
<h3>Notification Agents</h3>
</div>
<p class="help-block">
Add a new notification agent, or configure an existing notification agent by clicking the settings icon on the right.
</p>
<br />
<div id="plexpy-notifiers-table">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading notification agents...</div>
<br>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-extra_settings">
<div class="padded-header">
<h3>Extra Settings</h3>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes
</label>
<p class="help-block">Enable if you want Tautulli to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="log_blacklist" name="log_blacklist" value="1" ${config['log_blacklist']}> Mask Sensitive Information in Logs
</label>
<p class="help-block">
Enable to mask passwords, access tokens, and public IP addresses with asterisks (*) in the logs.<br />
Note: Only logs from the time this setting is enabled will be masked. Do not post your logs publically without masking sensitive information!
</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="cache_images" name="cache_images" value="1" ${config['cache_images']}> Cache Plex Images
</label>
<p class="help-block">
Enable to cache images from Plex to reduce API calls and improve loading times.<br />
Note: Video preview thumbnails (BIF) are not cached.
</p>
</div>
<div class="form-group">
<label for="anon_redirect">Anonymous Redirect</label>
<div class="row">
<div class="col-md-4">
<input type="text" class="form-control" id="anon_redirect" name="anon_redirect" value="${config['anon_redirect']}" size="30">
</div>
</div>
<p class="help-block">Backlink protection via anonymizer service, must end in "?".</p>
</div>
<div class="form-group">
<label>Flush Temporary Sessions</label>
<p class="help-block">
Attempt to fix history logging by flushing out all of the temporary sessions in the database.<br />
Warning: This will reset all currently active sessions. For emergency use only when history logging is stuck!
</p>
<div class="row">
<div class="col-md-4">
<div class="btn-group">
<button class="btn btn-form" type="button" id="delete_temp_sessions">Flush</button>
</div>
</div>
</div>
</div>
% if plexpy.INSTALL_TYPE == 'git':
<div class="padded-header">
<h3>Git Settings</h3>
</div>
<div class="form-group">
<label for="git_branch">Git Remote / Branch</label>
<div class="row">
<div class="col-md-6">
<div class="input-group git-group">
<input type="text" class="form-control" id="git_remote" name="git_remote" value="${config['git_remote']}" data-parsley-trigger="change">
<select class="form-control" id="git_branch" name="git_branch">
<% branches = ('master', 'beta', 'nightly') %>
% for branch in branches:
<option value="${branch}" ${'selected' if config['git_branch'] == branch else ''}>${branch}</option>
% endfor
% if config['git_branch'] not in branches:
<option value="${config['git_branch']}" selected>${config['git_branch']}</option>
% endif
</select>
<span class="input-group-btn">
<button class="btn btn-form" type="button" id="switch_git_branch">Checkout Branch</button>
</span>
</div>
</div>
</div>
<p class="help-block">The git tracking remote and branch (default "origin/master"). Select to switch the git branch (requires restart).</p>
</div>
<div class="form-group">
<label for="git_path">Git Path</label>
<div class="row">
<div class="col-md-4">
<input type="text" class="form-control" id="git_path" name="git_path" value="${config['git_path']}" size="30">
</div>
</div>
<p class="help-block">Optional: The path to your git environment variable. Leave blank for default.</p>
</div>
% endif
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-import_backups">
<div class="padded-header">
<h3>Database Import</h3>
</div>
<p class="help-block">Click a button below to import an exisiting database from another app.</p>
<div class="btn-group">
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexwatch">PlexWatch</button>
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexivity">Plexivity</button>
</div>
<div class="padded-header">
<h3>Backup</h3>
</div>
<div class="form-group">
<label for="backup_interval">Backup Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="backup_interval" name="backup_interval" value="${config['backup_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#backup_interval_error" required>
</div>
<div id="backup_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The interval (in hours) Tautulli will backup the database and configuration file. Minimum 1, maximum 24, default 6.</p>
</div>
<div class="form-group">
<label for="backup_interval">Backup Days</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="backup_days" name="backup_days" value="${config['backup_days']}" size="5" data-parsley-min="1" data-parsley-trigger="change" data-parsley-errors-container="#backup_days_error" required>
</div>
<div id="backup_days_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">
The number of days to keep scheduled backups. Minimum 1, default 3.<br />
Note: Manual backups are not removed automatically.
</p>
</div>
<div class="padded-header">
<h3>Directories</h3>
</div>
<div class="form-group">
<label for="backup_dir">Backup Directory</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control directory-settings" id="backup_dir" name="backup_dir" value="${config['backup_dir']}">
<div class="btn-group">
<button class="btn btn-form" type="button" id="backup_config">Backup Config</button>
<button class="btn btn-form" type="button" id="backup_database">Backup Database</button>
</div>
</div>
</div>
</div>
<div class="form-group">
<label for="cache_dir">Cache Directory</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control directory-settings" id="cache_dir" name="cache_dir" value="${config['cache_dir']}">
<div class="btn-group">
<button class="btn btn-form" type="button" id="clear_cache">Clear All Cache</button>
<button class="btn btn-form" type="button" id="clear_image_cache">Clear Image Cache</button>
</div>
</div>
</div>
</div>
<div class="form-group">
<label for="log_dir">Log Directory</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control directory-settings" id="log_dir" name="log_dir" value="${config['log_dir']}">
<div class="btn-group">
<button class="btn btn-form" type="button" id="clear_logs">Clear Logs</button>
</div>
</div>
</div>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-android_app">
<div class="padded-header">
<h3>Tautulli Remote Android App</h3>
</div>
<div class="form-group">
<label>Get the App</label>
<p class="help-block">
Get the <a href="${anon_url('https://play.google.com/store/apps/details?id=com.williamcomartin.plexpyremote')}" target="_blank">Tautulli Remote</a> app on Google Play<sup>TM</sup> to access Tautulli from your Android device!<br />
<span class="google-play-badge">
<a href="${anon_url('https://play.google.com/store/apps/details?id=com.williamcomartin.plexpyremote')}" target="_blank"><img alt="Get it on Google Play" src="images/en-play-badge.png" /></a>
</span>
</p>
</div>
<p class="form-group">
<label>Registered Devices</label>
<p class="help-block">Register a new device, or configure an existing device by clicking the settings icon on the right.</p>
<p id="app_api_msg" style="color: #eb8600;">The API must be enabled under <a data-tab-destination="tabs-access_control" style="cursor: pointer;">Access Control</a> to use the app.</p>
<div class="row">
<div id="plexpy-mobile-devices-table" class="col-md-12">
<div class='text-muted'><i class="fa fa-refresh fa-spin"></i> Loading registered devices...</div>
<br>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</%def>
<%def name="modalIncludes()">
<div id="guidelines-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="guidelines-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Guidelines</h4>
</div>
<div class="modal-body">
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
<strong>Please read the <a href="${anon_url('https://github.com/%s/plexpy/blob/master/CONTRIBUTING.md' % plexpy.CONFIG.GIT_USER)}" target="_blank">guidelines</a>
in the CONTRIBUTING document <br />before submitting a new <span id="guidelines-type"></span>!</strong>
<br /><br />
Your post may be removed for failure to follow the guidelines.
</div>
</div>
<div class="modal-footer">
<a href="#" target="_blank" id="guidelines-continue" class="btn btn-bright">Continue</a>
</div>
</div>
</div>
</div>
<div id="support-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="support-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Support</h4>
</div>
<div class="modal-body">
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
<strong>Please read the <a href="${anon_url('https://github.com/%s/plexpy/wiki/Frequently-Asked-Questions-(FAQ)' % plexpy.CONFIG.GIT_USER)}" target="_blank">FAQ</a>
before asking for help!</strong>
</div>
</div>
<div class="modal-footer">
<a href="#" target="_blank" id="support-continue" class="btn btn-bright">Continue</a>
</div>
</div>
</div>
</div>
<div id="dateTimeOptionsModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="dateTimeOptionsModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
<i class="fa fa-remove"></i>
</button>
<h4 class="modal-title">Date &amp; Time Format Options</h4>
</div>
<div class="modal-body">
% for category in common.DATE_TIME_FORMATS:
<table class="notification-params time-options">
<thead>
<tr>
<th colspan="3">
${category['category']}
</th>
</tr>
</thead>
<tbody>
% for parameter in category['parameters']:
<tr>
<td><strong>${parameter['value']}</strong></td>
<td>${parameter['description']}</td>
<td>${parameter['example']}</td>
</tr>
% endfor
</tbody>
</table>
% endfor
</div>
<div class="modal-footer"></div>
</div>
</div>
</div>
<div id="pms-auth-modal" class="modal fade" tabindex="-1" role="dialog"
aria-labelledby="ip-info-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Fetch Plex.tv Token</h4>
</div>
<div class="modal-body" id="modal-text">
<div>
<p class="help-block">
This will attempt to fetch a new Plex.tv token for you. Tautulli does not store your username and password.
Note: This will not work on Internet Explorer 9 or lower.
</p>
<div class="form-group">
<label for="pms_username">Plex.tv Username</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="pms_username" name="pms_username" size="30">
</div>
</div>
<p class="help-block">Username for Plex.tv authentication.</p>
</div>
<div class="form-group">
<label for="pms_password">Plex.tv Password</label>
<div class="row">
<div class="col-md-6">
<input type="password" class="form-control" id="pms_password" name="pms_password" size="30">
</div>
</div>
<p class="help-block">Password for Plex.tv authentication.</p>
</div>
</div>
</div>
<div class="modal-footer">
<div style="float: left;">
<strong><span id="pms-token-status"></span></strong>
</div>
<input type="button" id="get-pms-auth-token" class="btn btn-bright" value="Fetch Token">
</div>
</div>
</div>
</div>
<div id="app-import-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="app-import-modal"></div>
<div id="add-notifier-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="add-notifier-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Add a Notification Agent</h4>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<ul class="stacked-configs list-unstyled">
% for agent in available_notification_agents:
<li class="new-notification-agent" data-id="${agent['id']}">
<span>${agent['label']}</span>
</li>
% endfor
</ul>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Cancel">
</div>
</div>
</div>
</div>
<div id="notifier-config-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="notifier-config-modal"></div>
<div id="notify-text-sub-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="notify-text-sub-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
<i class="fa fa-remove"></i>
</button>
<h4 class="modal-title">Notification Parameters</h4>
</div>
<div class="modal-body">
<div>
<p class="help-block">
If the value for a selected parameter cannot be provided, it will display as blank.
</p>
% for category in common.NOTIFICATION_PARAMETERS:
<table class="notification-params">
<thead>
<tr>
<th colspan="2">
${category['category']}
</th>
</tr>
</thead>
<tbody>
% for parameter in category['parameters']:
<tr>
<td><strong>{${parameter['value']}}</strong></td>
<td>
${parameter['description']}
% if parameter.get('example'):
<span class="small-muted">(${parameter['example']})</span>
% endif
% if parameter.get('help_text'):
<p class="small-muted">(${parameter['help_text']})</p>
% endif
</td>
</tr>
% endfor
</tbody>
</table>
% endfor
</div>
</div>
<div class="modal-footer"></div>
</div>
</div>
</div>
<div id="notify-text-tags-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="notify-text-tags-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Notification Exclusion Tags</h4>
</div>
<div class="modal-body">
<div>
<div>
<h4>Movie Tag</h4>
</div>
<div style="padding-bottom: 10px;">
<p class="help-block">All text inside <span class="inline-pre">&lt;movie&gt;&lt;/movie&gt;</span> tags will only be sent when the media item is a movie.</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{title} &lt;movie&gt;({year})&lt;/movie&gt; was recently added to Plex</pre>
</div>
<div>
<h4>Show / Season / Episode Tags</h4>
</div>
<div style="padding-bottom: 10px;">
<p class="help-block">
All text inside <span class="inline-pre">&lt;show&gt;&lt;/show&gt;</span>/<span class="inline-pre">&lt;season&gt;&lt;/season&gt;</span>/<span class="inline-pre">&lt;episode&gt;&lt;/episode&gt;</span>
tags will only be sent when the media item is a show/season/episode.
</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{show_name}&lt;season&gt; - Season {season_num}&lt;/season&gt;&lt;episode&gt; - S{season_num}E{episode_num} - {episode_name}&lt;/episode&gt; was recently added to Plex.</pre>
</div>
<div>
<h4>Artist / Album / Track Tag</h4>
</div>
<div>
<p class="help-block">
All text inside <span class="inline-pre">&lt;artist&gt;&lt;/artist&gt;</span>/<span class="inline-pre">&lt;album&gt;&lt;/album&gt;</span>/<span class="inline-pre">&lt;track&gt;&lt;/track&gt;</span>
tags will only be sent when the media item is an artist/album/track.
</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{artist_name}&lt;album&gt; - {album_name}&lt;/album&gt;&lt;track&gt; - {album_name} - {track_name}&lt;/track&gt; was recently added to Plex.</pre>
</div>
</div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
<div id="notify-text-modifiers-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="notify-text-modifiers-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Notification Text Modifiers</h4>
</div>
<div class="modal-body">
<div>
<div>
<h4>Case Modifiers</h4>
</div>
<div>
<p class="help-block">Notification parameters with the <span class="inline-pre">!u</span> modifier will be converted to uppercase.</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{video_codec} --> hevc
{video_codec!u} --> HEVC</pre>
</div>
<div>
<p class="help-block">Notification parameters with the <span class="inline-pre">!l</span> modifier will be converted to lowercase.</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{content_rating} --> TV-PG
{content_rating!l} --> tv-pg</pre>
</div>
<div style="padding-bottom: 10px;">
<p class="help-block">Notification parameters with the <span class="inline-pre">!c</span> modifier will be converted to title case.</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{media_type} --> movie
{media_type!c} --> Movie</pre>
</div>
<div>
<h4>List Slicing</h4>
</div>
<div>
<p class="help-block">
Notification parameters which have a list of items can be sliced with a slice formatter <span class="inline-pre">:[X:Y]</span> to limit the number of items.
Note: the first item in the list is numbered <span class="inline-pre">0</span>.
</p>
<p><strong style="color: #fff;">Example:</strong></p>
<pre>{actors:[0]} --> Only include the 1st actor (Actors: 0)
{actors:[:4]} --> Only the first 4 actors (Actors: 0, 1, 2, 3)
{actors:[2:]} --> Only the 3rd to last actors (Actors: 2, 3, 4, ...)
{actors:[1:5]} --> Only the 2nd to 5th actors (Actors: 1, 2, 3, 4)</pre>
</div>
</div>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
<div id="notifier-text-preview-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="notifier-text-preview-modal">
</div>
<div id="changelog-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="changelog-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Changelog</h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Close">
</div>
</div>
</div>
</div>
<div id="restart-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="restart-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Restart</h4>
</div>
<div class="modal-body">
<div style="text-align: center; margin-top: 20px; margin-bottom: 20px;">
You have changed settings that require Tautulli to restart.<br />Click the restart button below to restart now.
</div>
</div>
<div class="modal-footer">
<button id="modal_link_restart" class="btn btn-bright"><i class="fa fa-refresh"></i> Restart</button>
</div>
</div>
</div>
</div>
<div id="api-qr-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="api-qr-modal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Register Tautulli Android App</h4>
</div>
<div class="modal-body">
<label>Instructions</label>
<p class="help-block">
Scan the QR code below with the Tautulli Android app to automatically register it with the server (make sure the Tautulli Address below is correct)
or manually enter the connection info and device token into the app settings.
</p>
<label>QR Code</label>
<pre id="api_qr_code" style="text-align: center"></pre>
<label>Tautulli Address</label>
<input type="text" class="form-control" id="api_qr_address">
<p class="help-block" id="api_qr_localhost" style="display: none;">
Note: <span class="inline-pre">127.0.0.1</span> and <span class="inline-pre">localhost</span> will not work.
Please enter an internal or external IP address, or hostname or domain instead.
</p>
<p class="help-block" id="api_qr_private" style="display: none;">
Note: This is a private IP address. Tautulli will not be reachable outside of your home network.
Access Tautulli via an externally address or manually enter the address above to generate the QR code for remote access.
</p>
<p class="help-block" id="api_qr_https" style="display: none;">
Note: This URL is not secure. Requests between the app and the server will not be encrypted.
Enable HTTPS to connect the app securely.
</p>
<label>Device Token</label>
<input type="text" class="form-control" id="api_qr_token" readonly>
</div>
<div class="modal-footer">
<input type="button" class="btn btn-bright" data-dismiss="modal" value="Cancel">
</div>
</div>
</div>
</div>
<div id="mobile-device-config-modal" class="modal fade wide" tabindex="-1" role="dialog" aria-labelledby="mobile-device-config-modal"></div>
</%def>
<%def name="javascriptIncludes()">
<script src="${http_root}js/parsley.min.js"></script>
<script src="${http_root}js/Sortable.min.js"></script>
<script src="${http_root}js/selectize.min.js"></script>
<script src="${http_root}js/moment-with-locale.js"></script>
<script src="${http_root}js/jquery.qrcode.min.js"></script>
<script>
function getConfigurationTable() {
$.ajax({
url: 'get_configuration_table',
cache: false,
async: true,
complete: function(xhr, status) {
$("#plexpy-configuration-table").html(xhr.responseText);
if ("${kwargs.get('install_geoip')}" == 'true') {
$('#install_geoip_db').removeClass('no-highlight').css('color','#e9a049');
} else if ("${kwargs.get('reinstall_geoip')}" == 'true') {
$('#reinstall_geoip_db').removeClass('no-highlight').css('color','#e9a049');
}
if ("${kwargs.get('support')}" == 'true') {
$('.support-modal-link').removeClass('no-highlight').css('color','#e9a049');
}
}
});
}
function getSchedulerTable() {
$.ajax({
url: 'get_scheduler_table',
cache: false,
async: true,
complete: function(xhr, status) {
$("#plexpy-scheduler-table").html(xhr.responseText);
}
});
}
function getNotifiersTable() {
$.ajax({
url: 'get_notifiers_table',
cache: false,
async: true,
complete: function(xhr, status) {
$("#plexpy-notifiers-table").html(xhr.responseText);
}
});
}
function loadNotifierConfig(notifier_id) {
$.ajax({
url: 'get_notifier_config_modal',
data: { notifier_id: notifier_id },
cache: false,
async: true,
complete: function (xhr, status) {
$("#notifier-config-modal").html(xhr.responseText).modal('show');
}
});
}
function getMobileDevicesTable() {
$.ajax({
url: 'get_mobile_devices_table',
cache: false,
async: true,
complete: function(xhr, status) {
$("#plexpy-mobile-devices-table").html(xhr.responseText);
}
});
}
function loadMobileDeviceConfig(mobile_device_id) {
$.ajax({
url: 'get_mobile_device_config_modal',
data: { mobile_device_id: mobile_device_id },
cache: false,
async: true,
complete: function (xhr, status) {
$("#mobile-device-config-modal").html(xhr.responseText).modal('show');
}
});
}
$(document).ready(function() {
// Javascript to enable link to tab
var hash = document.location.hash;
var prefix = "tab_";
if (hash) {
$('.nav-settings a[href='+hash.replace(prefix,"")+']').tab('show');
}
// Change hash for page-reload
$('.nav-settings a').on('shown.bs.tab', function (e) {
window.location.hash = e.target.hash.replace("#", "#" + prefix);
});
// Global Variables
settingsChanged = false;
serverChanged = false;
authChanged = false;
httpChanged = false;
directoryChanged = false;
// Alert if leaving the page without saving changes to settings
window.onbeforeunload = confirmExit;
function confirmExit() {
if (settingsChanged) {
return "Settings were changed without saving!";
}
}
function preSaveChecks(_callback) {
if ($("#pms_identifier").val() == "") {
verifyServer();
}
verifyPMSWebURL();
if (_callback) {
_callback();
}
}
// Alert the user that their changes require a restart.
function postSaveChecks() {
if (serverChanged || authChanged || httpChanged || directoryChanged) {
$('#restart-modal').modal('show');
}
$("#http_hashed_password").val($("#http_hash_password").is(":checked") ? 1 : 0)
getConfigurationTable();
getSchedulerTable();
getNotifiersTable();
getMobileDevicesTable();
loadUpdateDistros();
settingsChanged = false;
}
var configForm = $("#configUpdate");
configForm.change(function () {
settingsChanged = true;
});
function saveSettings() {
if (configForm.parsley().validate()) {
doAjaxCall('configUpdate', $(this), 'tabs', true, true, postSaveChecks);
return false;
} else {
showMsg('<i class="fa fa-exclamation-circle"></i> Please verify your settings.', false, true, 5000, true)
}
}
$('.save-button').click(function() {
preSaveChecks(function () { saveSettings() });
});
initConfigCheckbox('#api_enabled');
initConfigCheckbox('#enable_https');
initConfigCheckbox('#https_create_cert');
initConfigCheckbox('#check_github');
initConfigCheckbox('#notify_upload_posters');
initConfigCheckbox('#monitor_pms_updates');
$('#menu_link_shutdown').click(function() {
$('#confirm-message').text("Are you sure you want to shutdown Tautulli?");
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-button', function () {
window.location.href = 'shutdown';
});
});
$('#menu_link_restart').click(function() {
$("#confirm-message").text("Are you sure you want to restart Tautulli?");
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-button', function () {
window.location.href = 'restart';
});
});
$('#menu_link_update_check').click(function() {
// Allow the update bar to show again if previously dismissed.
setCookie('updateDismiss', 'true', 0);
$(this).html('<i class="fa fa-spin fa-refresh"></i> Checking');
$(this).prop('disabled', true);
window.location.href = 'checkGithub';
});
$('#modal_link_restart').click(function() {
window.location.href = 'restart';
});
getConfigurationTable();
getSchedulerTable();
getNotifiersTable();
getMobileDevicesTable();
$('#changelog-modal-link').on('click', function (e) {
e.preventDefault();
$.ajax({
url: 'get_changelog',
cache: false,
async: true,
complete: function(xhr, status) {
$("#changelog-modal .modal-body").html(xhr.responseText);
$('#changelog-modal').modal();
}
});
});
$("#backup_config").click(function () {
var msg = 'Are you sure you want to create a backup of the Tautulli config?';
var url = 'backup_config';
confirmAjaxCall(url, msg);
});
$("#backup_database").click(function () {
var msg = 'Are you sure you want to create a backup of the Tautulli database?';
var url = 'backup_db';
confirmAjaxCall(url, msg);
});
$("#clear_cache").click(function () {
var msg = 'Are you sure you want to clear the Tautulli cache?';
var url = 'delete_cache';
confirmAjaxCall(url, msg);
});
$("#clear_image_cache").click(function () {
var msg = 'Are you sure you want to clear the Tautulli image cache?';
var url = 'delete_image_cache';
confirmAjaxCall(url, msg);
});
$("#clear_logs").click(function () {
var msg = 'Are you sure you want to clear the Tautulli logs?';
var url = 'delete_logs';
confirmAjaxCall(url, msg);
});
$("#delete_temp_sessions").click(function () {
var msg = 'Are you sure you want to flush the temporary sessions?<br /><strong>This will reset all currently active sessions.</strong>';
var url = 'delete_temp_sessions';
confirmAjaxCall(url, msg);
});
$("#switch_git_branch").click(function () {
var current_remote = "${config['git_remote']}";
var current_branch = "${config['git_branch']}";
var new_remote = $("#git_remote").val();
var new_branch = $("#git_branch option:selected").val();
if (new_remote === current_remote && new_branch === current_branch) {
showMsg('<i class="fa fa-exclamation-circle"></i> Already on the ' + current_remote + ' ' + current_branch + ' branch.', false, true, 5000, true)
} else {
var msg = 'Are you sure you want to switch to the <strong>' + new_remote + '/' + new_branch + '</strong> branch?' +
'<br />Switching branches may cause Tautulli to become unstable.<br /><br />Tautulli will restart.';
$('#confirm-message').html(msg);
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-button', function () {
settingsChanged = false;
window.location.href = 'checkout_git_branch?git_remote=' + new_remote + '&git_branch=' + new_branch;
});
}
});
$('#api_key').click(function(){ $('#api_key').select() });
$("#generate_api").click(function() {
$.get('generate_api_key',
function (apikey) {
$('#api_key').val(apikey);
});
});
$( ".http-settings" ).change(function() {
httpChanged = true;
});
$( ".auth-settings" ).change(function() {
authChanged = true;
$("#auth_changed").prop('checked', true);
});
$( ".directory-settings" ).change(function() {
directoryChanged = true;
});
$( ".pms-settings" ).change(function() {
serverChanged = true;
$("#pms_identifier").val("");
$("#server_changed").prop('checked', true);
verifyServer();
});
function verifyServer(_callback) {
var pms_ip = $("#pms_ip").val();
var pms_port = $("#pms_port").val();
var pms_identifier = $("#pms_identifier").val();
var pms_ssl = $("#pms_ssl").is(':checked') ? 1 : 0;
var pms_is_remote = $("#pms_is_remote").is(':checked') ? 1 : 0;
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
$("#pms_verify").html('<i class="fa fa-refresh fa-spin"></i>').fadeIn('fast');
$.ajax({
url: 'get_server_id',
data: {
hostname: pms_ip,
port: pms_port,
identifier: pms_identifier,
ssl: pms_ssl,
remote: pms_is_remote
},
cache: true,
async: true,
timeout: 10000,
error: function (jqXHR, textStatus, errorThrown) {
$("#pms_verify").html('<i class="fa fa-close"></i>').fadeIn('fast');
$("#pms_ip_group").addClass("has-error");
},
success: function (json) {
var machine_identifier = json;
if (machine_identifier) {
$("#pms_identifier").val(machine_identifier);
$("#pms_verify").html('<i class="fa fa-check"></i>').fadeIn('fast');
$("#pms_ip_group").removeClass("has-error");
if (_callback) {
_callback();
}
} else {
$("#pms_verify").html('<i class="fa fa-close"></i>').fadeIn('fast');
$("#pms_ip_group").addClass("has-error");
showMsg('<i class="fa fa-exclamation-circle"></i> Could not verify your server.', false, true, 5000, true)
}
}
});
} else {
$("#pms_verify").html('<i class="fa fa-close"></i>').fadeIn('fast');
$("#pms_ip_group").addClass("has-error");
showMsg('<i class="fa fa-exclamation-circle"></i> Could not verify your server.', false, true, 5000, true)
}
}
$('#verify_server_button').on('click', function(){
$("#pms_identifier").val("");
verifyServer();
});
function verifyPMSWebURL() {
var pms_web_url = $.trim($("#pms_web_url").val());
$("#pms_web_url").val(pms_web_url || 'https://app.plex.tv/desktop');
}
$('#test_pms_web_button').on('click', function(){
var pms_web_url = $.trim($("#pms_web_url").val());
window.open(pms_web_url, '_blank');
});
// Plex.tv auth token fetch
$("#get-pms-auth-token").click(function() {
$("#pms-token-status").html('<i class="fa fa-refresh fa-spin"></i> Fetching token...');
var pms_username = $.trim($("#pms_username").val());
var pms_password = $.trim($("#pms_password").val());
if ((pms_username !== '') && (pms_password !== '')) {
$.ajax({
type: 'GET',
url: 'get_plexpy_pms_token',
data: {
username: pms_username,
password: pms_password,
force: true
},
cache: false,
async: true,
complete: function(xhr, status) {
var result = $.parseJSON(xhr.responseText);
var msg = result.message;
if (result.result == 'success') {
var authToken = result.token;
$("#pms-token-status").html('<i class="fa fa-check"></i> ' + msg);
$("#pms_token").val(authToken);
$('#pms-auth-modal').modal('hide');
} else {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> ' + msg);
}
loadUpdateDistros();
}
});
} else {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Username and password required.');
}
});
// Load database import modal
$(".toggle-app-import-modal").click(function() {
$.ajax({
url: 'import_database_tool',
data: { app: $(this).data('app') },
cache: false,
async: true,
complete: function(xhr, status) {
$("#app-import-modal").html(xhr.responseText);
}
});
});
pms_version = false;
pms_logs_debug = false;
pms_logs = false;
$.ajax({
url: 'get_server_pref',
data: { pref: 'PublishServerOnPlexOnlineKey' },
async: true,
success: function(data) {
if (data !== 'true') {
$("#remoteAccessCheck").html("Remote access must be enabled on your Plex Server. <a target='_blank' href='${anon_url('https://support.plex.tv/hc/en-us/articles/200484543-Enabling-Remote-Access-for-a-Server')}'>Click here</a> for help.");
$("#monitor_remote_access").attr("disabled", true);
$("#monitor_remote_access").attr("checked", false);
}
}
});
// Sortable home_sections
function set_home_sections() {
var home_sections = [];
var hsecs = $('[id^=hsec-]').serializeArray();
$.each(hsecs, function(i, sec) {
home_sections.push(sec.value);
});
$('#home_sections').val(home_sections);
};
var sec_cards = ${config['home_sections'] | n};
sec_cards.reverse().forEach(function (item) {
$('#hsec-' + item).prop('checked', !$(this).prop('checked'))
$('#hsec-' + item).closest('li.card').prependTo('#sortable_home_sections');
});
Sortable.create(sortable_home_sections, {
animation: 250,
onSort: function(elem, ui) {
set_home_sections();
}
});
$('[id^=hsec-]').change(function() { set_home_sections(); });
set_home_sections()
// Sortable home_stats_cards
function set_home_stats_cards() {
var home_stats_cards = [];
var hscards = $('[id^=hscard-]').serializeArray();
$.each(hscards, function(i, card) {
home_stats_cards.push(card.value);
});
$('#home_stats_cards').val(home_stats_cards);
};
var config_cards = ${config['home_stats_cards'] | n};
config_cards.reverse().forEach(function (item) {
$('#hscard-' + item).prop('checked', !$(this).prop('checked'))
$('#hscard-' + item).closest('li.card').prependTo('#sortable_home_stats_cards');
});
Sortable.create(sortable_home_stats_cards, {
animation: 250,
onSort: function(elem, ui) {
set_home_stats_cards();
}
});
$('[id^=hscard-]').change(function() { set_home_stats_cards(); });
set_home_stats_cards()
// Sortable home_library_cards
function set_home_library_cards() {
var home_library_cards = [];
var hlcards = $('[id^=hlcard-]').serializeArray();
$.each(hlcards, function(i, card) {
home_library_cards.push(card.value);
});
$('#home_library_cards').val(home_library_cards);
};
$.ajax({
url: 'get_library_sections',
data: { },
async: true,
complete: function (data) {
libraries_list = $.parseJSON(data.responseText);
for (var i in libraries_list) {
var title = libraries_list[i].section_name;
var key = libraries_list[i].section_id;
$('#sortable_home_library_cards').append(
'<li class="card card-sortable">' +
'<div class="card-handle"><i class="fa fa-bars"></i></div>' +
'<label>' +
'<input type="checkbox" id="hlcard-' + key + '" name="hlcard-' + key + '" value="' + key + '"> ' + title +
'</label>' +
'</li>'
);
}
var config_cards = ${config['home_library_cards'] | n};
config_cards.reverse().forEach(function (item) {
$('#hlcard-' + item).prop('checked', !$(this).prop('checked'));
$('#hlcard-' + item).closest('li.card').prependTo('#sortable_home_library_cards');
});
$('[id^=hlcard-]').change(function() { set_home_library_cards(); });
set_home_library_cards()
}
});
Sortable.create(sortable_home_library_cards, {
animation: 250,
onSort: function(elem, ui) {
set_home_library_cards();
}
});
function allowPlexAdminCheck () {
if ($("#http_basic_auth").is(":checked")) {
$("#http_plex_admin").attr("disabled", true);
$("#http_plex_admin").attr("checked", false);
$("#allowPlexCheck").html("Plex admin login cannot be enabled with basic authentication.");
} else if ($('#http_username').val() == '' || $('#http_password').val() == '') {
$("#http_plex_admin").attr("disabled", true);
$("#http_plex_admin").attr("checked", false);
$("#allowPlexCheck").html("You must set an admin username and password above to allow Plex admin login.");
} else {
$("#http_plex_admin").attr("disabled", false);
$("#allowPlexCheck").html("");
}
}
allowPlexAdminCheck();
$('#http_username, #http_password, #http_basic_auth').change(function () {
allowPlexAdminCheck();
});
function allowGuestAccessCheck () {
if ($("#http_basic_auth").is(":checked")) {
$("#allow_guest_access").attr("disabled", true);
$("#allow_guest_access").attr("checked", false);
$("#allowGuestCheck").html("Guest access cannot be enabled with basic authentication.");
} else if ($('#http_username').val() == '' || $('#http_password').val() == '') {
$("#allow_guest_access").attr("disabled", true);
$("#allow_guest_access").attr("checked", false);
$("#allowGuestCheck").html("You must set an admin username and password above to allow guest access.");
} else {
$("#allow_guest_access").attr("disabled", false);
$("#allowGuestCheck").html("");
}
}
allowGuestAccessCheck();
$('#http_username, #http_password, #http_basic_auth').change(function () {
allowGuestAccessCheck();
});
function hashPasswordCheck () {
if ($("#http_basic_auth").is(":checked")) {
$("#http_hash_password").attr("checked", false);
$("#http_hash_password").attr("disabled", true);
$("#hashPasswordCheck").html("Password cannot be hashed with basic authentication.");
} else {
$("#http_hash_password").attr("disabled", false);
$("#hashPasswordCheck").html("");
}
if (!($("#http_hash_password").is(":checked")) && $("#http_hashed_password").val() == "1" && $("#http_password").val() == " ") {
$("#http_hashed_password").val(-1);
} else if ($("#http_hash_password").is(":checked") && $("#http_hashed_password").val() == "-1" && $("#http_password").val() == " ") {
$("#http_hashed_password").val(1);
$("#http_hash_password_error").html("");
}
}
hashPasswordCheck();
$('#http_password, #http_hash_password, #http_basic_auth').change(function () {
hashPasswordCheck();
});
$('#http_password').change(function () {
$("#http_hashed_password").val($("#http_hash_password").is(":checked") ? 1 : 0);
$("#http_hash_password_error").html("");
});
// Load PMS downloads
function loadUpdateDistros() {
var update_params_ajax = $.getJSON('get_server_update_params', function (data) { return data; });
$.when(update_params_ajax).done(function() {
var update_params = update_params_ajax.responseJSON;
var plexpass = update_params.plexpass;
var platform = update_params.pms_platform;
var update_channel = update_params.pms_update_channel;
var update_distro = update_params.pms_update_distro;
var update_distro_build = update_params.pms_update_distro_build;
$("#pms_update_channel option[value='plexpass']").remove();
if (plexpass) {
var selected = (update_channel == 'plexpass') ? true : false;
$('#pms_update_channel')
.append($('<option></option>')
.text('Plex Pass')
.val('plexpass')
.prop('selected', selected));
}
$.getJSON('https://plex.tv/api/downloads/1.json?channel=' + update_channel, function (downloads) {
platform_downloads = downloads.computer[platform] || downloads.nas[platform];
if (platform_downloads) {
$("#pms_update_distro_build option").remove();
$.each(platform_downloads.releases, function (index, item) {
var label = (platform_downloads.releases.length == 1) ? platform_downloads.name : platform_downloads.name + ' - ' + item.label;
var selected = (item.distro == update_distro && item.build == update_distro_build) ? true : false;
$('#pms_update_distro_build')
.append($('<option></option>')
.text(label)
.val(item.build)
.attr('data-distro', item.distro)
.prop('selected', selected));
})
$('#pms_update_distro').val($("#pms_update_distro_build option:selected").data('distro'))
}
});
});
}
loadUpdateDistros();
$('#pms_update_distro_build').change(function () {
var distro = $("option:selected", this).data('distro')
$('#pms_update_distro').val(distro)
});
// Add a new notification agent
$('.new-notification-agent').click(function () {
$.ajax({
url: 'add_notifier_config',
data: { agent_id: $(this).data('id') },
cache: false,
async: true,
complete: function (xhr, status) {
var result = $.parseJSON(xhr.responseText);
var msg = result.message;
$('#add-notifier-modal').modal('hide');
if (result.result == 'success') {
showMsg('<i class="fa fa-check"></i> ' + msg, false, true, 5000);
loadNotifierConfig(result.notifier_id);
} else {
showMsg('<i class="fa fa-times"></i> ' + msg, false, true, 5000, true);
}
getNotifiersTable();
}
});
});
function apiEnabled() {
var api_enabled = $('#api_enabled').prop('checked');
$('#app_api_msg').toggle(!(api_enabled));
}
apiEnabled();
$('#api_enabled').click(function () {
apiEnabled();
});
$('body').on('click', 'a[data-tab-destination]', function () {
var tab = $(this).data('tab-destination');
$("a[href=#" + tab + "]").click();
});
});
</script>
</%def>