plexpy/data/interfaces/default/settings.html
2017-12-08 23:02:58 -08:00

2096 lines
117 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()">
</%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">PlexPy 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>PlexPy 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>PlexPy 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 PlexPy 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 PlexPy 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>
<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 PlexPy 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 PlexPy 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 PlexPy, 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 PlexPy, 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_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>
<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 PlexPy
</label>
<span id="allowGuestCheck" style="color: #eb8600; padding-left: 10px;"></span>
<p class="help-block">Allow shared users to login to PlexPy 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 PlexPy.</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">
<label>
<input type="checkbox" id="monitor_pms_updates" name="monitor_pms_updates" value="1" ${config['monitor_pms_updates']}> Monitor Plex Updates
</label>
<p class="help-block">Enable to have PlexPy 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">
<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>
<p class="help-block">Enable to have PlexPy 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 PlexPy.</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>
<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 PlexPy as a log viewer. Plex logs are not needed for PlexPy to function.
A complete folder path is required, shortcuts are not recognized.
<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">PMS 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) PlexPy 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 PlexPy 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) PlexPy 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 PlexPy 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>Monitoring Settings</h3>
</div>
<div class="form-group">
<label for="monitoring_interval">Monitoring Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="monitoring_interval" name="monitoring_interval" value="${config['monitoring_interval']}" size="5" data-parsley-min="30" data-parsley-trigger="change" data-parsley-errors-container="#monitoring_interval_error" required>
</div>
<div id="monitoring_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">
The interval (in seconds) PlexPy will ping your Plex Server. Minimum 30 seconds, recommended 60 seconds.<br />
Note: Only used for checking Plex remote access.
</p>
</div>
<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) PlexPy 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>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']}> Enable Posters in 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 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 for TV shows when available.</p>
</div>
<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 PlexPy 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="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, PlexPy will assume the higher quality one is newer.
</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="pms_use_bif" name="pms_use_bif" value="1" ${config['pms_use_bif']}> Use Video Preview Thumbnails (BIF)
</label>
<p class="help-block">If you have media indexing enabled on your server, use these on the activity pane.</p>
</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 <span style="color: #eb8600; padding-left: 10px;">[experimental]</span>
</label>
<p class="help-block">Enable if you want PlexPy 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">
% if config['git_branch'] == 'master':
<option value="master" selected>master</option>
<option value="dev">dev</option>
% elif config['git_branch'] == 'dev':
<option value="master">master</option>
<option value="dev" selected>dev</option>
% else:
<option value="master">master</option>
<option value="dev">dev</option>
<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) PlexPy 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>
</div>
<div role="tabpanel" class="tab-pane" id="tabs-android_app">
<div class="padded-header">
<h3>PlexPy 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">PlexPy Remote</a> app on Google Play<sup>TM</sup> to access PlexPy 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="#" target="_blank" id="guidelines-link">guidelines</a> in the README 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="#" target="_blank" id="faq-link">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. PlexPy 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 PlexPy 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 PlexPy Android App</h4>
</div>
<div class="modal-body">
<label>Instructions</label>
<p class="help-block">
Scan the QR code below with the PlexPy Android app to automatically register it with the server (make sure the PlexPy 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>PlexPy 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. PlexPy will not be reachable outside of your home network.
Access PlexPy 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/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');
$('#best-support-link').prepend('<span data-toggle="tooltip" title="Most Active"><i class="fa fa-star"></i></span>&nbsp;')
$('#best-support-link span').tooltip({ container: 'body' });
}
}
});
}
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!";
}
}
// 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() {
if ($("#pms_identifier").val() == "") {
verifyServer(function () { saveSettings() });
} else {
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 PlexPy?");
$('#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 PlexPy?");
$('#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 PlexPy 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 PlexPy database?';
var url = 'backup_db';
confirmAjaxCall(url, msg);
});
$("#clear_cache").click(function () {
var msg = 'Are you sure you want to clear the PlexPy cache?';
var url = 'delete_cache';
confirmAjaxCall(url, msg);
});
$("#clear_image_cache").click(function () {
var msg = 'Are you sure you want to clear the PlexPy image cache?';
var url = 'delete_image_cache';
confirmAjaxCall(url, msg);
});
$("#clear_logs").click(function () {
var msg = 'Are you sure you want to clear the PlexPy 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 PlexPy to become unstable.<br /><br />PlexPy 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;
});
$( ".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>');
$('#pms-verify').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>');
$('#pms-verify').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>');
$('#pms-verify').fadeIn('fast');
$("#pms-ip-group").removeClass("has-error");
if (_callback) {
_callback();
}
} else {
$("#pms-verify").html('<i class="fa fa-close"></i>');
$('#pms-verify').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>');
$('#pms-verify').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();
});
// 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 = $("#pms_username").val().trim();
var pms_password = $("#pms_password").val().trim();
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 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 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>