Compare commits

...

9 Commits

Author SHA1 Message Date
JonnyWong16
6e0a0d51b5 v2.1.39 2019-12-08 12:12:24 -08:00
JonnyWong16
55aad4e6ee Fix integrity check before database backup 2019-11-29 23:14:33 -08:00
JonnyWong16
d1e401cb0c Remove default subject and body for notify API command 2019-11-24 22:35:58 -08:00
JonnyWong16
018479fae9 Fix favicons in setup wizard 2019-11-24 15:03:36 -08:00
JonnyWong16
1c18e72539 Enable allow Plex admin by default 2019-11-20 21:51:52 -08:00
JonnyWong16
779e710045 Fix setup wizard height 2019-11-20 21:51:36 -08:00
JonnyWong16
089a981f6e Check for database corruption when making backup 2019-11-20 19:03:44 -08:00
JonnyWong16
3b24bbee5f Add web server HTTP username and password to setup wizard 2019-11-20 18:50:17 -08:00
JonnyWong16
f9a597bed9 Use bit.ly link for PayPal in FUNDING file 2019-11-17 21:54:47 -08:00
10 changed files with 147 additions and 68 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
github: JonnyWong16 github: JonnyWong16
patreon: Tautulli patreon: Tautulli
custom: ["https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6XPPKTDSX9QFL&lc=US&item_name=Tautulli&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted"] custom: ["https://bit.ly/2InPp15"]

View File

@@ -1,5 +1,15 @@
# Changelog # Changelog
## v2.1.39 (2019-12-08)
* UI:
* New: Added creating admin username and password to setup wizard.
* API:
* Change: Remove default notification subject and body for notify API command.
* Other:
* Change: Check for database corruption when making backup.
## v2.1.38 (2019-11-17) ## v2.1.38 (2019-11-17)
* Notifications: * Notifications:

View File

@@ -234,31 +234,19 @@ def main():
plexpy.CONFIG.ENABLE_HTTPS = False plexpy.CONFIG.ENABLE_HTTPS = False
# Try to start the server. Will exit here is address is already in use. # Try to start the server. Will exit here is address is already in use.
web_config = { webstart.start()
'http_port': plexpy.HTTP_PORT,
'http_host': plexpy.CONFIG.HTTP_HOST, # Windows system tray icon
'http_root': plexpy.CONFIG.HTTP_ROOT, if os.name == 'nt' and plexpy.CONFIG.WIN_SYS_TRAY:
'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT, plexpy.win_system_tray()
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
'enable_https': plexpy.CONFIG.ENABLE_HTTPS, logger.info("Tautulli is ready!")
'https_cert': plexpy.CONFIG.HTTPS_CERT,
'https_cert_chain': plexpy.CONFIG.HTTPS_CERT_CHAIN,
'https_key': plexpy.CONFIG.HTTPS_KEY,
'http_username': plexpy.CONFIG.HTTP_USERNAME,
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
}
webstart.initialize(web_config)
# Open webbrowser # Open webbrowser
if plexpy.CONFIG.LAUNCH_BROWSER and not args.nolaunch and not plexpy.DEV: if plexpy.CONFIG.LAUNCH_BROWSER and not args.nolaunch and not plexpy.DEV:
plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, plexpy.HTTP_PORT, plexpy.launch_browser(plexpy.CONFIG.HTTP_HOST, plexpy.HTTP_PORT,
plexpy.HTTP_ROOT) plexpy.HTTP_ROOT)
# Windows system tray icon
if os.name == 'nt' and plexpy.CONFIG.WIN_SYS_TRAY:
plexpy.win_system_tray()
# Wait endlessy for a signal to happen # Wait endlessy for a signal to happen
while True: while True:
if not plexpy.SIGNAL: if not plexpy.SIGNAL:

View File

@@ -21,28 +21,21 @@
<link href="${http_root}css/font-awesome.v4-shims.min.css" rel="stylesheet"> <link href="${http_root}css/font-awesome.v4-shims.min.css" rel="stylesheet">
<!-- Favicons --> <!-- Favicons -->
<link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.0"> <link rel="icon" type="image/png" sizes="32x32" href="${http_root}images/favicon/favicon-32x32.png?v=2.0.5">
<link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.0"> <link rel="icon" type="image/png" sizes="16x16" href="${http_root}images/favicon/favicon-16x16.png?v=2.0.5">
<link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.0"> <link rel="shortcut icon" href="${http_root}images/favicon/favicon.ico?v=2.0.5">
<!-- ICONS --> <!-- ICONS -->
<!-- Android >M39 icon --> <!-- Android -->
<link rel="icon" type="image/png" sizes="192x192" href="${http_root}images/favicon/android-chrome-192x192.png?v=2.0.0"> <link rel="manifest" href="${http_root}images/favicon/manifest.json?v=2.0.5">
<link rel="manifest" href="${http_root}json/Android-manifest.json?v=2.0.0"> <meta name="theme-color" content="#282a2d">
<meta name="theme-color" content="#1f1f1f">
<!-- Apple --> <!-- Apple -->
<link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.0"> <link rel="apple-touch-icon" sizes="180x180" href="${http_root}images/favicon/apple-touch-icon.png?v=2.0.5">
<link rel="mask-icon" href="${http_root}images/favicon/safari-pinned-tab.svg?v=2.0.0" color="#1f1f1f"> <link rel="mask-icon" href="${http_root}images/favicon/safari-pinned-tab.svg?v=2.0.5" color="#282a2d">
<meta name="apple-mobile-web-app-title" content="Tautulli"> <meta name="apple-mobile-web-app-title" content="Tautulli">
<meta name="apple-mobile-web-app-capable" content="yes"> <!-- Microsoft -->
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" content="initial-scale=1">
<meta name="format-detection" content="telephone=no">
<!-- IE10 icon -->
<meta name="application-name" content="Tautulli"> <meta name="application-name" content="Tautulli">
<meta name="msapplication-TileColor" content="#1f1f1f"> <meta name="msapplication-config" content="${http_root}images/favicon/browserconfig.xml?v=2.0.5">
<meta name="msapplication-TileImage" content="${http_root}images/favicon/mstile-144x144.png?v=2.0.0">
<meta name="msapplication-config" content="${http_root}xml/IEconfig.xml?v=2.0.0" />
</head> </head>
<body> <body>
@@ -67,8 +60,38 @@
</p> </p>
</div> </div>
</div> </div>
<div class="wizard-card" data-cardname="card2"> <div class="wizard-card" data-cardname="card2">
<h3>Plex Authentication</h3> <h3>Authentication</h3>
<div class="wizard-input-section">
<p class="help-block">
Please setup an admin username and password for Tautulli.
</p>
</div>
<div class="wizard-input-section">
<label for="http_username">HTTP Username</label>
<div class="row">
<div class="col-xs-8">
<input type="text" class="form-control auth-settings" id="http_username" name="http_username" value="" size="30">
</div>
</div>
</div>
<div class="wizard-input-section">
<label for="http_password">HTTP Password</label>
<div class="row">
<div class="col-xs-8">
<input type="password" class="form-control auth-settings" id="http_password" name="http_password" value="" size="30" autocomplete="new-password">
</div>
</div>
</div>
<input type="hidden" class="form-control" name="http_hash_password" id="http_hash_password" value="1">
<input type="hidden" class="form-control" name="http_plex_admin" id="http_plex_admin" value="1">
<input type="hidden" id="authentication_valid" data-validate="validateAuthentication" value="">
<span style="display: none;" id="authentication-status"></span>
</div>
<div class="wizard-card" data-cardname="card3">
<h3>Plex Account</h3>
<div class="wizard-input-section"> <div class="wizard-input-section">
<p class="help-block"> <p class="help-block">
Tautulli requires a Plex.tv account. Click the button below to sign in on Plex.tv. You may need to allow popups in your browser. Tautulli requires a Plex.tv account. Click the button below to sign in on Plex.tv. You may need to allow popups in your browser.
@@ -78,7 +101,8 @@
<a class="btn btn-dark" id="sign-in-plex" href="#" role="button">Sign In with Plex</a> <a class="btn btn-dark" id="sign-in-plex" href="#" role="button">Sign In with Plex</a>
<span style="margin-left: 10px; display: none;" id="pms-token-status"></span> <span style="margin-left: 10px; display: none;" id="pms-token-status"></span>
</div> </div>
<div class="wizard-card" data-cardname="card3">
<div class="wizard-card" data-cardname="card4">
<h3>Plex Media Server</h3> <h3>Plex Media Server</h3>
<div class="wizard-input-section"> <div class="wizard-input-section">
<p class="help-block"> <p class="help-block">
@@ -137,7 +161,7 @@
<span style="margin-left: 10px; display: none;" id="pms-verify-status"></span> <span style="margin-left: 10px; display: none;" id="pms-verify-status"></span>
</div> </div>
<div class="wizard-card" data-cardname="card4"> <div class="wizard-card" data-cardname="card5">
<h3>Activity Logging</h3> <h3>Activity Logging</h3>
<div class="wizard-input-section"> <div class="wizard-input-section">
<p class="help-block"> <p class="help-block">
@@ -162,7 +186,7 @@
</div> </div>
</div> </div>
<div class="wizard-card" data-cardname="card4"> <div class="wizard-card" data-cardname="card6">
<h3>Notifications</h3> <h3>Notifications</h3>
<div class="wizard-input-section"> <div class="wizard-input-section">
<p class="help-block"> <p class="help-block">
@@ -175,7 +199,7 @@
</div> </div>
</div> </div>
<div class="wizard-card" data-cardname="card5"> <div class="wizard-card" data-cardname="card7">
<h3>Database Import</h3> <h3>Database Import</h3>
<div class="wizard-input-section"> <div class="wizard-input-section">
<p class="help-block"> <p class="help-block">
@@ -227,11 +251,29 @@
<script src="${http_root}js/script.js${cache_param}"></script> <script src="${http_root}js/script.js${cache_param}"></script>
<script src="${http_root}js/bootstrap-wizard.min.js"></script> <script src="${http_root}js/bootstrap-wizard.min.js"></script>
<script> <script>
function validateAuthentication(el) {
var http_username = $("#http_username").val();
var http_password = $("#http_password").val();
var valid_authentication = el.val();
var retValue = {};
if (http_username === "" || http_password === "") {
retValue.status = false;
retValue.msg = "Please enter a username and password.";
$("#authentication-status").html('<i class="fa fa-exclamation-circle"></i> Please enter a username and password.');
$('#authentication-status').fadeIn('fast').delay(2000).fadeOut('fast');
} else {
retValue.status = true;
}
return retValue;
}
function validatePMSip(el) { function validatePMSip(el) {
var valid_pms_ip = el.val(); var valid_pms_ip = el.val();
var retValue = {}; var retValue = {};
if (valid_pms_ip == "") { if (valid_pms_ip === "") {
retValue.status = false; retValue.status = false;
retValue.msg = "Please verify your server."; retValue.msg = "Please verify your server.";
$("#pms-verify-status").html('<i class="fa fa-exclamation-circle"></i> Please verify your server.'); $("#pms-verify-status").html('<i class="fa fa-exclamation-circle"></i> Please verify your server.');
@@ -247,7 +289,7 @@
var valid_pms_token = el.val(); var valid_pms_token = el.val();
var retValue = {}; var retValue = {};
if (valid_pms_token == "") { if (valid_pms_token === "") {
retValue.status = false; retValue.status = false;
retValue.msg = "Please authenticate."; retValue.msg = "Please authenticate.";
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Please authenticate.'); $("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Please authenticate.');
@@ -284,7 +326,7 @@ $(document).ready(function() {
$.fn.wizard.logging = false; $.fn.wizard.logging = false;
var options = { var options = {
keyboard : false, keyboard : false,
contentHeight : 400, contentHeight : 450,
contentWidth : 700, contentWidth : 700,
backdrop: 'static', backdrop: 'static',
buttons: {submitText: 'Finish'}, buttons: {submitText: 'Finish'},

View File

@@ -29,7 +29,6 @@ try:
except ImportError: except ImportError:
no_browser = True no_browser = True
import cherrypy
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger from apscheduler.triggers.interval import IntervalTrigger
from UniversalAnalytics import Tracker from UniversalAnalytics import Tracker
@@ -51,6 +50,7 @@ import plextv
import users import users
import versioncheck import versioncheck
import web_socket import web_socket
import webstart
import plexpy.config import plexpy.config
PROG_DIR = None PROG_DIR = None
@@ -1972,8 +1972,7 @@ def upgrade():
def shutdown(restart=False, update=False, checkout=False): def shutdown(restart=False, update=False, checkout=False):
logger.info(u"Stopping Tautulli web server...") webstart.stop()
cherrypy.engine.exit()
# Shutdown the websocket connection # Shutdown the websocket connection
if WEBSOCKET: if WEBSOCKET:

View File

@@ -411,7 +411,7 @@ class API2:
return return
def notify(self, notifier_id='', subject='Tautulli', body='Test notification', **kwargs): def notify(self, notifier_id='', subject='', body='', **kwargs):
""" Send a notification using Tautulli. """ Send a notification using Tautulli.
``` ```

View File

@@ -69,10 +69,17 @@ def db_filename(filename=FILENAME):
def make_backup(cleanup=False, scheduler=False): def make_backup(cleanup=False, scheduler=False):
""" Makes a backup of db, removes all but the last 5 backups """ """ Makes a backup of db, removes all but the last 5 backups """
# Check the integrity of the database first
integrity = (integrity_check()['integrity_check'] == 'ok')
corrupt = ''
if not integrity:
corrupt = '.corrupt'
if scheduler: if scheduler:
backup_file = 'tautulli.backup-%s.sched.db' % arrow.now().format('YYYYMMDDHHmmss') backup_file = 'tautulli.backup-{}{}.sched.db'.format(arrow.now().format('YYYYMMDDHHmmss'), corrupt)
else: else:
backup_file = 'tautulli.backup-%s.db' % arrow.now().format('YYYYMMDDHHmmss') backup_file = 'tautulli.backup-{}{}.db'.format(arrow.now().format('YYYYMMDDHHmmss'), corrupt)
backup_folder = plexpy.CONFIG.BACKUP_DIR backup_folder = plexpy.CONFIG.BACKUP_DIR
backup_file_fp = os.path.join(backup_folder, backup_file) backup_file_fp = os.path.join(backup_folder, backup_file)
@@ -85,7 +92,8 @@ def make_backup(cleanup=False, scheduler=False):
shutil.copyfile(db_filename(), backup_file_fp) shutil.copyfile(db_filename(), backup_file_fp)
db.connection.rollback() db.connection.rollback()
if cleanup: # Only cleanup if the database integrity is okay
if cleanup and integrity:
now = time.time() now = time.time()
# Delete all scheduled backup older than BACKUP_DAYS. # Delete all scheduled backup older than BACKUP_DAYS.
for root, dirs, files in os.walk(backup_folder): for root, dirs, files in os.walk(backup_folder):

View File

@@ -1,2 +1,2 @@
PLEXPY_BRANCH = "master" PLEXPY_BRANCH = "master"
PLEXPY_RELEASE_VERSION = "v2.1.38" PLEXPY_RELEASE_VERSION = "v2.1.39"

View File

@@ -53,6 +53,7 @@ import pmsconnect
import users import users
import versioncheck import versioncheck
import web_socket import web_socket
import webstart
from plexpy.api2 import API2 from plexpy.api2 import API2
from plexpy.helpers import checked, addtoapi, get_ip, create_https_certificates, build_datatables_json, sanitize_out from plexpy.helpers import checked, addtoapi, get_ip, create_https_certificates, build_datatables_json, sanitize_out
from plexpy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library from plexpy.session import get_session_info, get_session_user_id, allow_session_user, allow_session_library
@@ -2829,6 +2830,18 @@ class WebInterface(object):
def configUpdate(self, **kwargs): def configUpdate(self, **kwargs):
# Handle the variable config options. Note - keys with False values aren't getting passed # Handle the variable config options. Note - keys with False values aren't getting passed
# Check if we should refresh our data
first_run = False
server_changed = False
reschedule = False
https_changed = False
refresh_libraries = False
refresh_users = False
# First run from the setup wizard
if kwargs.pop('first_run', None):
first_run = True
checked_configs = [ checked_configs = [
"launch_browser", "enable_https", "https_create_cert", "api_enabled", "freeze_db", "check_github", "launch_browser", "enable_https", "https_create_cert", "api_enabled", "freeze_db", "check_github",
"grouping_global_history", "grouping_user_history", "grouping_charts", "group_history_tables", "grouping_global_history", "grouping_user_history", "grouping_charts", "group_history_tables",
@@ -2863,13 +2876,13 @@ class WebInterface(object):
kwargs['http_hashed_password'] = 1 kwargs['http_hashed_password'] = 1
# Flag to refresh JWT uuid to log out clients # Flag to refresh JWT uuid to log out clients
kwargs['jwt_update_secret'] = True kwargs['jwt_update_secret'] = True and not first_run
elif not kwargs.get('http_hash_password'): elif not kwargs.get('http_hash_password'):
kwargs['http_hashed_password'] = 0 kwargs['http_hashed_password'] = 0
# Flag to refresh JWT uuid to log out clients # Flag to refresh JWT uuid to log out clients
kwargs['jwt_update_secret'] = True kwargs['jwt_update_secret'] = True and not first_run
else: else:
kwargs['http_hashed_password'] = 0 kwargs['http_hashed_password'] = 0
@@ -2879,18 +2892,6 @@ class WebInterface(object):
kwargs[plain_config] = kwargs[use_config] kwargs[plain_config] = kwargs[use_config]
del kwargs[use_config] del kwargs[use_config]
# Check if we should refresh our data
first_run = False
server_changed = False
reschedule = False
https_changed = False
refresh_libraries = False
refresh_users = False
# First run from the setup wizard
if kwargs.pop('first_run', None):
first_run = True
# If we change any monitoring settings, make sure we reschedule tasks. # If we change any monitoring settings, make sure we reschedule tasks.
if kwargs.get('check_github') != plexpy.CONFIG.CHECK_GITHUB or \ if kwargs.get('check_github') != plexpy.CONFIG.CHECK_GITHUB or \
kwargs.get('refresh_libraries_interval') != str(plexpy.CONFIG.REFRESH_LIBRARIES_INTERVAL) or \ kwargs.get('refresh_libraries_interval') != str(plexpy.CONFIG.REFRESH_LIBRARIES_INTERVAL) or \
@@ -2965,6 +2966,7 @@ class WebInterface(object):
# If first run, start websocket # If first run, start websocket
if first_run: if first_run:
webstart.restart()
activity_pinger.connect_server(log=True, startup=True) activity_pinger.connect_server(log=True, startup=True)
# Reconfigure scheduler if intervals changed # Reconfigure scheduler if intervals changed

View File

@@ -25,6 +25,36 @@ from plexpy.helpers import create_https_certificates
from plexpy.webserve import WebInterface from plexpy.webserve import WebInterface
def start():
logger.info(u"Tautulli WebStart :: Initializing Tautulli web server...")
web_config = {
'http_port': plexpy.HTTP_PORT,
'http_host': plexpy.CONFIG.HTTP_HOST,
'http_root': plexpy.CONFIG.HTTP_ROOT,
'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT,
'http_proxy': plexpy.CONFIG.HTTP_PROXY,
'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
'https_cert': plexpy.CONFIG.HTTPS_CERT,
'https_cert_chain': plexpy.CONFIG.HTTPS_CERT_CHAIN,
'https_key': plexpy.CONFIG.HTTPS_KEY,
'http_username': plexpy.CONFIG.HTTP_USERNAME,
'http_password': plexpy.CONFIG.HTTP_PASSWORD,
'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
}
initialize(web_config)
def stop():
logger.info(u"Tautulli WebStart :: Stopping Tautulli web server...")
cherrypy.engine.exit()
def restart():
logger.info(u"Tautulli WebStart :: Restarting Tautulli web server...")
stop()
start()
def initialize(options): def initialize(options):
# HTTPS stuff stolen from sickbeard # HTTPS stuff stolen from sickbeard