mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 05:31:15 -07:00
2958 lines
119 KiB
Python
2958 lines
119 KiB
Python
# This file is part of Tautulli.
|
|
#
|
|
# Tautulli is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Tautulli is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import ctypes
|
|
import datetime
|
|
import os
|
|
import queue
|
|
import sqlite3
|
|
import sys
|
|
import subprocess
|
|
import threading
|
|
import uuid
|
|
|
|
# Some cut down versions of Python may not include this module and it's not critical for us
|
|
try:
|
|
import webbrowser
|
|
no_browser = False
|
|
except ImportError:
|
|
no_browser = True
|
|
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
from apscheduler.triggers.interval import IntervalTrigger
|
|
from ga4mp import GtagMP
|
|
import pytz
|
|
|
|
from plexpy import activity_handler
|
|
from plexpy import activity_pinger
|
|
from plexpy import common
|
|
from plexpy import database
|
|
from plexpy import datafactory
|
|
from plexpy import exporter
|
|
from plexpy import helpers
|
|
from plexpy import libraries
|
|
from plexpy import logger
|
|
from plexpy import mobile_app
|
|
from plexpy import newsletters
|
|
from plexpy import newsletter_handler
|
|
from plexpy import notification_handler
|
|
from plexpy import notifiers
|
|
from plexpy import plex
|
|
from plexpy import plextv
|
|
from plexpy import users
|
|
from plexpy import versioncheck
|
|
from plexpy import web_socket
|
|
from plexpy import webstart
|
|
from plexpy import config
|
|
|
|
|
|
PROG_DIR = None
|
|
FULL_PATH = None
|
|
|
|
ARGS = None
|
|
SIGNAL = None
|
|
|
|
SYS_PLATFORM = None
|
|
SYS_LANGUAGE = None
|
|
SYS_ENCODING = None
|
|
|
|
QUIET = False
|
|
VERBOSE = False
|
|
DAEMON = False
|
|
CREATEPID = False
|
|
PIDFILE = None
|
|
NOFORK = False
|
|
DOCKER = False
|
|
DOCKER_MOUNT = False
|
|
SNAP = False
|
|
SNAP_MIGRATE = False
|
|
FROZEN = False
|
|
|
|
SCHED = None
|
|
SCHED_LOCK = threading.Lock()
|
|
|
|
NOTIFY_QUEUE = queue.Queue()
|
|
|
|
INIT_LOCK = threading.Lock()
|
|
_INITIALIZED = False
|
|
_STARTED = False
|
|
_UPDATE = False
|
|
|
|
DATA_DIR = None
|
|
|
|
CONFIG = None
|
|
CONFIG_FILE = None
|
|
|
|
DB_FILE = None
|
|
|
|
INSTALL_TYPE = None
|
|
CURRENT_VERSION = None
|
|
LATEST_VERSION = None
|
|
COMMITS_BEHIND = None
|
|
PREV_RELEASE = None
|
|
LATEST_RELEASE = None
|
|
UPDATE_AVAILABLE = False
|
|
|
|
UMASK = None
|
|
|
|
HTTP_PORT = None
|
|
HTTP_ROOT = None
|
|
AUTH_ENABLED = None
|
|
|
|
DEV = False
|
|
|
|
WEBSOCKET = None
|
|
WS_CONNECTED = False
|
|
PLEX_SERVER_UP = None
|
|
PLEX_REMOTE_ACCESS_UP = None
|
|
|
|
TRACKER = None
|
|
|
|
WIN_SYS_TRAY_ICON = None
|
|
MAC_SYS_TRAY_ICON = None
|
|
|
|
SYS_TIMEZONE = None
|
|
SYS_UTC_OFFSET = None
|
|
|
|
|
|
def initialize(config_file):
|
|
with INIT_LOCK:
|
|
|
|
global CONFIG
|
|
global CONFIG_FILE
|
|
global VERBOSE
|
|
global _INITIALIZED
|
|
global CURRENT_VERSION
|
|
global LATEST_VERSION
|
|
global PREV_RELEASE
|
|
global UMASK
|
|
global _UPDATE
|
|
|
|
try:
|
|
CONFIG = config.Config(config_file)
|
|
except:
|
|
alert_message('Failed to start Tautulli: Config file is corrupted.\n\n%s' % config_file)
|
|
raise SystemExit("Unable to initialize Tautulli due to a corrupted config file. Exiting...")
|
|
|
|
CONFIG_FILE = config_file
|
|
|
|
assert CONFIG is not None
|
|
|
|
if _INITIALIZED:
|
|
return False
|
|
|
|
if SNAP_MIGRATE:
|
|
snap_common = os.environ['SNAP_COMMON']
|
|
old_data_dir = os.path.join(snap_common, 'Tautulli')
|
|
CONFIG.HTTPS_CERT = CONFIG.HTTPS_CERT.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.HTTPS_CERT_CHAIN = CONFIG.HTTPS_CERT_CHAIN.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.HTTPS_KEY = CONFIG.HTTPS_KEY.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.LOG_DIR = CONFIG.LOG_DIR.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.BACKUP_DIR = CONFIG.BACKUP_DIR.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.CACHE_DIR = CONFIG.CACHE_DIR.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.EXPORT_DIR = CONFIG.EXPORT_DIR.replace(old_data_dir, DATA_DIR)
|
|
CONFIG.NEWSLETTER_DIR = CONFIG.NEWSLETTER_DIR.replace(old_data_dir, DATA_DIR)
|
|
|
|
if CONFIG.HTTP_PORT < 21 or CONFIG.HTTP_PORT > 65535:
|
|
logger.warn("HTTP_PORT out of bounds: 21 < %s < 65535", CONFIG.HTTP_PORT)
|
|
CONFIG.HTTP_PORT = 8181
|
|
|
|
if not CONFIG.HTTPS_CERT:
|
|
CONFIG.HTTPS_CERT = os.path.join(DATA_DIR, 'server.crt')
|
|
if not CONFIG.HTTPS_KEY:
|
|
CONFIG.HTTPS_KEY = os.path.join(DATA_DIR, 'server.key')
|
|
|
|
CONFIG.LOG_DIR, log_writable = check_folder_writable(
|
|
CONFIG.LOG_DIR, os.path.join(DATA_DIR, 'logs'), 'logs')
|
|
if not log_writable and not QUIET:
|
|
sys.stderr.write("Unable to create the log directory. Logging to screen only.\n")
|
|
|
|
VERBOSE = VERBOSE or bool(CONFIG.VERBOSE_LOGS)
|
|
|
|
# Start the logger, disable console if needed
|
|
logger.initLogger(console=not QUIET, log_dir=CONFIG.LOG_DIR if log_writable else None,
|
|
verbose=VERBOSE)
|
|
|
|
os.environ['PLEXAPI_CONFIG_PATH'] = os.path.join(DATA_DIR, 'plexapi.config.ini')
|
|
os.environ['PLEXAPI_LOG_PATH'] = os.path.join(CONFIG.LOG_DIR, 'plexapi.log')
|
|
os.environ['PLEXAPI_LOG_LEVEL'] = 'DEBUG'
|
|
plex.initialize_plexapi()
|
|
|
|
if DOCKER:
|
|
build = '[Docker] '
|
|
elif SNAP:
|
|
build = '[Snap] '
|
|
elif FROZEN:
|
|
build = '[Bundle] '
|
|
else:
|
|
build = ''
|
|
|
|
logger.info("Starting Tautulli {}".format(
|
|
common.RELEASE
|
|
))
|
|
logger.info("{}{} {} ({}{})".format(
|
|
build, common.PLATFORM, common.PLATFORM_RELEASE, common.PLATFORM_VERSION,
|
|
' - {}'.format(common.PLATFORM_LINUX_DISTRO) if common.PLATFORM_LINUX_DISTRO else ''
|
|
))
|
|
logger.info("{} (UTC{})".format(
|
|
str(SYS_TIMEZONE), SYS_UTC_OFFSET
|
|
))
|
|
logger.info("Language {}{} / Encoding {}".format(
|
|
SYS_LANGUAGE, f' (override {CONFIG.PMS_LANGUAGE})' if CONFIG.PMS_LANGUAGE else '', SYS_ENCODING
|
|
))
|
|
logger.info("Python {}".format(
|
|
sys.version.replace('\n', '')
|
|
))
|
|
logger.info("SQLite {}".format(
|
|
sqlite3.sqlite_version
|
|
))
|
|
|
|
logger.info("Program Dir: {}".format(
|
|
PROG_DIR
|
|
))
|
|
logger.info("Config File: {}".format(
|
|
CONFIG_FILE
|
|
))
|
|
logger.info("Database File: {}".format(
|
|
DB_FILE
|
|
))
|
|
|
|
if DOCKER and not DOCKER_MOUNT:
|
|
logger.warn(
|
|
"Docker /config volume not mounted. Using a Docker volume instead. "
|
|
"All data may be cleared when the container is recreated or updated."
|
|
)
|
|
|
|
CONFIG.BACKUP_DIR, _ = check_folder_writable(
|
|
CONFIG.BACKUP_DIR, os.path.join(DATA_DIR, 'backups'), 'backups')
|
|
CONFIG.CACHE_DIR, _ = check_folder_writable(
|
|
CONFIG.CACHE_DIR, os.path.join(DATA_DIR, 'cache'), 'cache')
|
|
CONFIG.EXPORT_DIR, _ = check_folder_writable(
|
|
CONFIG.EXPORT_DIR, os.path.join(DATA_DIR, 'exports'), 'exports')
|
|
CONFIG.NEWSLETTER_DIR, _ = check_folder_writable(
|
|
CONFIG.NEWSLETTER_DIR, os.path.join(DATA_DIR, 'newsletters'), 'newsletters')
|
|
|
|
# Initialize the database
|
|
logger.info("Checking if the database upgrades are required...")
|
|
try:
|
|
dbcheck()
|
|
except Exception as e:
|
|
logger.error("Can't connect to the database: %s" % e)
|
|
|
|
# Perform upgrades
|
|
logger.info("Checking if configuration upgrades are required...")
|
|
try:
|
|
upgrade()
|
|
except Exception as e:
|
|
logger.error("Could not perform upgrades: %s" % e)
|
|
|
|
# Add notifier configs to logger blacklist
|
|
newsletters.blacklist_logger()
|
|
notifiers.blacklist_logger()
|
|
mobile_app.blacklist_logger()
|
|
|
|
# Check if Tautulli has a uuid
|
|
if CONFIG.PMS_UUID == '' or not CONFIG.PMS_UUID:
|
|
logger.debug("Generating UUID...")
|
|
CONFIG.PMS_UUID = generate_uuid()
|
|
CONFIG.write()
|
|
|
|
# Check if Tautulli has an API key
|
|
if CONFIG.API_KEY == '':
|
|
logger.debug("Generating API key...")
|
|
CONFIG.API_KEY = generate_uuid()
|
|
CONFIG.write()
|
|
|
|
# Check if Tautulli has a jwt_secret
|
|
if CONFIG.JWT_SECRET == '' or not CONFIG.JWT_SECRET or CONFIG.JWT_UPDATE_SECRET:
|
|
logger.debug("Generating JWT secret...")
|
|
CONFIG.JWT_SECRET = generate_uuid()
|
|
CONFIG.JWT_UPDATE_SECRET = False
|
|
CONFIG.write()
|
|
|
|
# Get the previous version from the file
|
|
version_lock_file = os.path.join(DATA_DIR, "version.lock")
|
|
prev_version = None
|
|
if os.path.isfile(version_lock_file):
|
|
try:
|
|
with open(version_lock_file, "r") as fp:
|
|
prev_version = fp.read()
|
|
except IOError as e:
|
|
logger.error("Unable to read previous version from file '%s': %s" %
|
|
(version_lock_file, e))
|
|
|
|
# Get the currently installed version. Returns None, 'win32' or the git
|
|
# hash.
|
|
CURRENT_VERSION, CONFIG.GIT_REMOTE, CONFIG.GIT_BRANCH = versioncheck.get_version()
|
|
|
|
# Write current version to a file, so we know which version did work.
|
|
# This allows one to restore to that version. The idea is that if we
|
|
# arrive here, most parts of Tautulli seem to work.
|
|
if CURRENT_VERSION:
|
|
try:
|
|
with open(version_lock_file, "w") as fp:
|
|
fp.write(CURRENT_VERSION)
|
|
except IOError as e:
|
|
logger.error("Unable to write current version to file '%s': %s" %
|
|
(version_lock_file, e))
|
|
|
|
# Check for new versions
|
|
if CONFIG.CHECK_GITHUB_ON_STARTUP and CONFIG.CHECK_GITHUB:
|
|
try:
|
|
versioncheck.check_update(use_cache=True)
|
|
except:
|
|
logger.exception("Unhandled exception")
|
|
LATEST_VERSION = CURRENT_VERSION
|
|
else:
|
|
LATEST_VERSION = CURRENT_VERSION
|
|
|
|
# Get the previous release from the file
|
|
release_file = os.path.join(DATA_DIR, "release.lock")
|
|
PREV_RELEASE = common.RELEASE
|
|
if os.path.isfile(release_file):
|
|
try:
|
|
with open(release_file, "r") as fp:
|
|
PREV_RELEASE = fp.read()
|
|
except IOError as e:
|
|
logger.error("Unable to read previous release from file '%s': %s" %
|
|
(release_file, e))
|
|
|
|
# Check if the release was updated
|
|
if common.RELEASE != PREV_RELEASE:
|
|
CONFIG.UPDATE_SHOW_CHANGELOG = 1
|
|
CONFIG.write()
|
|
_UPDATE = True
|
|
|
|
# Write current release version to file for update checking
|
|
try:
|
|
with open(release_file, "w") as fp:
|
|
fp.write(common.RELEASE)
|
|
except IOError as e:
|
|
logger.error("Unable to write current release to file '%s': %s" %
|
|
(release_file, e))
|
|
|
|
# Store the original umask
|
|
UMASK = os.umask(0)
|
|
os.umask(UMASK)
|
|
|
|
_INITIALIZED = True
|
|
return True
|
|
|
|
|
|
def daemonize():
|
|
if threading.activeCount() != 1:
|
|
logger.warn(
|
|
"There are %r active threads. Daemonizing may cause"
|
|
" strange behavior.",
|
|
threading.enumerate())
|
|
|
|
sys.stdout.flush()
|
|
sys.stderr.flush()
|
|
|
|
# Do first fork
|
|
try:
|
|
pid = os.fork() # @UndefinedVariable - only available in UNIX
|
|
if pid != 0:
|
|
sys.exit(0)
|
|
except OSError as e:
|
|
raise RuntimeError("1st fork failed: %s [%d]", e.strerror, e.errno)
|
|
|
|
os.setsid()
|
|
|
|
# Make sure I can read my own files and shut out others
|
|
prev = os.umask(0) # @UndefinedVariable - only available in UNIX
|
|
os.umask(prev and int('077', 8))
|
|
|
|
# Make the child a session-leader by detaching from the terminal
|
|
try:
|
|
pid = os.fork() # @UndefinedVariable - only available in UNIX
|
|
if pid != 0:
|
|
sys.exit(0)
|
|
except OSError as e:
|
|
raise RuntimeError("2nd fork failed: %s [%d]", e.strerror, e.errno)
|
|
|
|
dev_null = open('/dev/null', 'r')
|
|
os.dup2(dev_null.fileno(), sys.stdin.fileno())
|
|
|
|
si = open('/dev/null', "r")
|
|
so = open('/dev/null', "a+")
|
|
se = open('/dev/null', "a+")
|
|
|
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
|
os.dup2(se.fileno(), sys.stderr.fileno())
|
|
|
|
pid = os.getpid()
|
|
logger.info("Daemonized to PID: %d", pid)
|
|
|
|
if CREATEPID:
|
|
logger.info("Writing PID %d to %s", pid, PIDFILE)
|
|
with open(PIDFILE, 'w') as fp:
|
|
fp.write("%s\n" % pid)
|
|
|
|
|
|
def launch_browser(host, port, root):
|
|
if not no_browser:
|
|
if host in ('0.0.0.0', '::'):
|
|
host = 'localhost'
|
|
|
|
if CONFIG.ENABLE_HTTPS:
|
|
protocol = 'https'
|
|
else:
|
|
protocol = 'http'
|
|
|
|
try:
|
|
webbrowser.open('%s://%s:%i%s' % (protocol, host, port, root))
|
|
except Exception as e:
|
|
logger.error("Could not launch browser: %s" % e)
|
|
|
|
|
|
def initialize_scheduler():
|
|
"""
|
|
Start the scheduled background tasks. Re-schedule if interval settings changed.
|
|
"""
|
|
|
|
with SCHED_LOCK:
|
|
|
|
# Check if scheduler should be started
|
|
start_jobs = not len(SCHED.get_jobs())
|
|
|
|
# Update check
|
|
github_hours = CONFIG.CHECK_GITHUB_INTERVAL if CONFIG.CHECK_GITHUB_INTERVAL and CONFIG.CHECK_GITHUB else 0
|
|
pms_update_check_hours = CONFIG.PMS_UPDATE_CHECK_INTERVAL if 1 <= CONFIG.PMS_UPDATE_CHECK_INTERVAL else 24
|
|
|
|
schedule_job(versioncheck.check_update, 'Check GitHub for updates',
|
|
hours=github_hours, minutes=0, seconds=0, args=(True, True))
|
|
|
|
backup_hours = CONFIG.BACKUP_INTERVAL if 1 <= CONFIG.BACKUP_INTERVAL <= 24 else 6
|
|
|
|
schedule_job(database.optimize_db, 'Optimize Tautulli database',
|
|
hours=24, minutes=0, seconds=0)
|
|
schedule_job(database.make_backup, 'Backup Tautulli database',
|
|
hours=backup_hours, minutes=0, seconds=0, args=(True, True))
|
|
schedule_job(config.make_backup, 'Backup Tautulli config',
|
|
hours=backup_hours, minutes=0, seconds=0, args=(True, True))
|
|
|
|
if WS_CONNECTED and CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
|
|
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
|
|
hours=12 * (not bool(CONFIG.PMS_URL_MANUAL)), minutes=0, seconds=0)
|
|
|
|
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
|
|
hours=pms_update_check_hours * bool(CONFIG.MONITOR_PMS_UPDATES), minutes=0, seconds=0)
|
|
|
|
# Refresh the users list and libraries list
|
|
user_hours = CONFIG.REFRESH_USERS_INTERVAL if 1 <= CONFIG.REFRESH_USERS_INTERVAL <= 24 else 12
|
|
library_hours = CONFIG.REFRESH_LIBRARIES_INTERVAL if 1 <= CONFIG.REFRESH_LIBRARIES_INTERVAL <= 24 else 12
|
|
|
|
schedule_job(users.refresh_users, 'Refresh users list',
|
|
hours=user_hours, minutes=0, seconds=0)
|
|
schedule_job(libraries.refresh_libraries, 'Refresh libraries list',
|
|
hours=library_hours, minutes=0, seconds=0)
|
|
|
|
schedule_job(activity_pinger.connect_server, 'Check for server response',
|
|
hours=0, minutes=0, seconds=0)
|
|
schedule_job(web_socket.send_ping, 'Websocket ping',
|
|
hours=0, minutes=0, seconds=10 * bool(CONFIG.WEBSOCKET_MONITOR_PING_PONG))
|
|
|
|
else:
|
|
# Cancel all jobs
|
|
schedule_job(plextv.get_server_resources, 'Refresh Plex server URLs',
|
|
hours=0, minutes=0, seconds=0)
|
|
|
|
schedule_job(activity_pinger.check_server_updates, 'Check for Plex updates',
|
|
hours=0, minutes=0, seconds=0)
|
|
|
|
schedule_job(users.refresh_users, 'Refresh users list',
|
|
hours=0, minutes=0, seconds=0)
|
|
schedule_job(libraries.refresh_libraries, 'Refresh libraries list',
|
|
hours=0, minutes=0, seconds=0)
|
|
|
|
# Schedule job to reconnect server
|
|
schedule_job(activity_pinger.connect_server, 'Check for server response',
|
|
hours=0, minutes=0, seconds=30, args=(False,))
|
|
schedule_job(web_socket.send_ping, 'Websocket ping',
|
|
hours=0, minutes=0, seconds=0)
|
|
|
|
# Start scheduler
|
|
if start_jobs and len(SCHED.get_jobs()):
|
|
try:
|
|
SCHED.start()
|
|
except Exception as e:
|
|
logger.error(e)
|
|
|
|
|
|
def schedule_job(func, name, hours=0, minutes=0, seconds=0, args=None):
|
|
"""
|
|
Start scheduled job if starting or restarting plexpy.
|
|
Reschedule job if Interval Settings have changed.
|
|
Remove job if if Interval Settings changed to 0
|
|
|
|
"""
|
|
|
|
job = SCHED.get_job(name)
|
|
if job:
|
|
if hours == 0 and minutes == 0 and seconds == 0:
|
|
SCHED.remove_job(name)
|
|
logger.info("Removed background task: %s", name)
|
|
elif job.trigger.interval != datetime.timedelta(hours=hours, minutes=minutes):
|
|
SCHED.reschedule_job(
|
|
name, trigger=IntervalTrigger(
|
|
hours=hours, minutes=minutes, seconds=seconds, timezone=pytz.UTC),
|
|
args=args)
|
|
logger.info("Re-scheduled background task: %s", name)
|
|
elif hours > 0 or minutes > 0 or seconds > 0:
|
|
SCHED.add_job(
|
|
func, id=name, trigger=IntervalTrigger(
|
|
hours=hours, minutes=minutes, seconds=seconds, timezone=pytz.UTC),
|
|
args=args, misfire_grace_time=None)
|
|
logger.info("Scheduled background task: %s", name)
|
|
|
|
|
|
def start():
|
|
global _STARTED
|
|
|
|
if _INITIALIZED:
|
|
logger.filter_usernames()
|
|
|
|
# Start refreshes on a separate thread
|
|
threading.Thread(target=startup_refresh).start()
|
|
|
|
global SCHED
|
|
SCHED = BackgroundScheduler(timezone=pytz.UTC)
|
|
activity_handler.ACTIVITY_SCHED = BackgroundScheduler(timezone=pytz.UTC)
|
|
newsletter_handler.NEWSLETTER_SCHED = BackgroundScheduler(timezone=pytz.UTC)
|
|
|
|
# Start the scheduler for stale stream callbacks
|
|
activity_handler.ACTIVITY_SCHED.start()
|
|
|
|
# Start background notification thread
|
|
notification_handler.start_threads(num_threads=CONFIG.NOTIFICATION_THREADS)
|
|
notifiers.check_browser_enabled()
|
|
|
|
# Schedule newsletters
|
|
newsletter_handler.NEWSLETTER_SCHED.start()
|
|
newsletter_handler.schedule_newsletters()
|
|
|
|
# Cancel processing exports
|
|
exporter.cancel_exports()
|
|
|
|
if CONFIG.SYSTEM_ANALYTICS:
|
|
global TRACKER
|
|
TRACKER = initialize_tracker()
|
|
|
|
# Send system analytics events
|
|
if not CONFIG.FIRST_RUN_COMPLETE:
|
|
analytics_event(name='install')
|
|
|
|
elif _UPDATE:
|
|
analytics_event(name='update')
|
|
|
|
analytics_event(name='start')
|
|
|
|
_STARTED = True
|
|
|
|
|
|
def startup_refresh():
|
|
# Get the real PMS urls for SSL and remote access
|
|
if CONFIG.PMS_TOKEN and CONFIG.PMS_IP and CONFIG.PMS_PORT:
|
|
plextv.get_server_resources()
|
|
|
|
# Connect server after server resource is refreshed
|
|
if CONFIG.FIRST_RUN_COMPLETE:
|
|
activity_pinger.connect_server(log=True, startup=True)
|
|
|
|
# Refresh the users list on startup
|
|
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
|
|
users.refresh_users()
|
|
|
|
# Refresh the libraries list on startup
|
|
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN and CONFIG.REFRESH_LIBRARIES_ON_STARTUP:
|
|
libraries.refresh_libraries()
|
|
|
|
|
|
def sig_handler(signum=None, frame=None):
|
|
if signum is not None:
|
|
logger.info("Signal %i caught, saving and exiting...", signum)
|
|
shutdown()
|
|
|
|
|
|
def dbcheck():
|
|
conn_db = sqlite3.connect(DB_FILE)
|
|
c_db = conn_db.cursor()
|
|
|
|
# schema table :: This is a table which keeps track of the database version
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS version_info (key TEXT UNIQUE, value TEXT)"
|
|
)
|
|
|
|
# sessions table :: This is a temp table that logs currently active sessions
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, session_key INTEGER, session_id TEXT, "
|
|
"transcode_key TEXT, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, stopped INTEGER, "
|
|
"paused_counter INTEGER DEFAULT 0, state TEXT, user_id INTEGER, user TEXT, friendly_name TEXT, "
|
|
"ip_address TEXT, machine_id TEXT, bandwidth INTEGER, location TEXT, player TEXT, product TEXT, platform TEXT, "
|
|
"title TEXT, parent_title TEXT, grandparent_title TEXT, original_title TEXT, full_title TEXT, "
|
|
"media_index INTEGER, parent_media_index INTEGER, "
|
|
"thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, year INTEGER, "
|
|
"parent_rating_key INTEGER, grandparent_rating_key INTEGER, "
|
|
"originally_available_at TEXT, added_at INTEGER, guid TEXT, "
|
|
"view_offset INTEGER DEFAULT 0, duration INTEGER, video_decision TEXT, audio_decision TEXT, "
|
|
"transcode_decision TEXT, container TEXT, bitrate INTEGER, width INTEGER, height INTEGER, "
|
|
"video_codec TEXT, video_bitrate INTEGER, video_resolution TEXT, video_width INTEGER, video_height INTEGER, "
|
|
"video_framerate TEXT, video_scan_type TEXT, video_full_resolution TEXT, "
|
|
"video_dynamic_range TEXT, aspect_ratio TEXT, "
|
|
"audio_codec TEXT, audio_bitrate INTEGER, audio_channels INTEGER, audio_language TEXT, audio_language_code TEXT, "
|
|
"subtitle_codec TEXT, subtitle_forced INTEGER, subtitle_language TEXT, "
|
|
"stream_bitrate INTEGER, stream_video_resolution TEXT, quality_profile TEXT, "
|
|
"stream_container_decision TEXT, stream_container TEXT, "
|
|
"stream_video_decision TEXT, stream_video_codec TEXT, stream_video_bitrate INTEGER, stream_video_width INTEGER, "
|
|
"stream_video_height INTEGER, stream_video_framerate TEXT, stream_video_scan_type TEXT, stream_video_full_resolution TEXT, "
|
|
"stream_video_dynamic_range TEXT, "
|
|
"stream_audio_decision TEXT, stream_audio_codec TEXT, stream_audio_bitrate INTEGER, stream_audio_channels INTEGER, "
|
|
"stream_audio_language TEXT, stream_audio_language_code TEXT, "
|
|
"subtitles INTEGER, stream_subtitle_decision TEXT, stream_subtitle_codec TEXT, "
|
|
"stream_subtitle_forced INTEGER, stream_subtitle_language TEXT, "
|
|
"transcode_protocol TEXT, transcode_container TEXT, "
|
|
"transcode_video_codec TEXT, transcode_audio_codec TEXT, transcode_audio_channels INTEGER,"
|
|
"transcode_width INTEGER, transcode_height INTEGER, "
|
|
"transcode_hw_decoding INTEGER, transcode_hw_encoding INTEGER, "
|
|
"optimized_version INTEGER, optimized_version_profile TEXT, optimized_version_title TEXT, "
|
|
"synced_version INTEGER, synced_version_profile TEXT, "
|
|
"live INTEGER, live_uuid TEXT, "
|
|
"channel_call_sign TEXT, channel_id TEXT, channel_identifier TEXT, channel_title TEXT, channel_thumb TEXT, channel_vcn TEXT, "
|
|
"secure INTEGER, relayed INTEGER, "
|
|
"buffer_count INTEGER DEFAULT 0, buffer_last_triggered INTEGER, last_paused INTEGER, watched INTEGER DEFAULT 0, "
|
|
"intro INTEGER DEFAULT 0, credits INTEGER DEFAULT 0, commercial INTEGER DEFAULT 0, marker INTEGER DEFAULT 0, "
|
|
"initial_stream INTEGER DEFAULT 1, write_attempts INTEGER DEFAULT 0, raw_stream_info TEXT, "
|
|
"rating_key_websocket TEXT)"
|
|
)
|
|
|
|
# sessions_continued table :: This is a temp table that keeps track of continued streaming sessions
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS sessions_continued (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"user_id INTEGER, machine_id TEXT, media_type TEXT, stopped INTEGER)"
|
|
)
|
|
|
|
# session_history table :: This is a history table which logs essential stream details
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS session_history (id INTEGER PRIMARY KEY AUTOINCREMENT, reference_id INTEGER, "
|
|
"started INTEGER, stopped INTEGER, rating_key INTEGER, user_id INTEGER, user TEXT, "
|
|
"ip_address TEXT, paused_counter INTEGER DEFAULT 0, player TEXT, product TEXT, product_version TEXT, "
|
|
"platform TEXT, platform_version TEXT, profile TEXT, machine_id TEXT, "
|
|
"bandwidth INTEGER, location TEXT, quality_profile TEXT, secure INTEGER, relayed INTEGER, "
|
|
"parent_rating_key INTEGER, grandparent_rating_key INTEGER, media_type TEXT, section_id INTEGER, "
|
|
"view_offset INTEGER DEFAULT 0)"
|
|
)
|
|
|
|
# session_history_media_info table :: This is a table which logs each session"s media info
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS session_history_media_info (id INTEGER PRIMARY KEY, rating_key INTEGER, "
|
|
"video_decision TEXT, audio_decision TEXT, transcode_decision TEXT, duration INTEGER DEFAULT 0, "
|
|
"container TEXT, bitrate INTEGER, width INTEGER, height INTEGER, video_bitrate INTEGER, video_bit_depth INTEGER, "
|
|
"video_codec TEXT, video_codec_level TEXT, video_width INTEGER, video_height INTEGER, video_resolution TEXT, "
|
|
"video_framerate TEXT, video_scan_type TEXT, video_full_resolution TEXT, video_dynamic_range TEXT, aspect_ratio TEXT, "
|
|
"audio_bitrate INTEGER, audio_codec TEXT, audio_channels INTEGER, audio_language TEXT, audio_language_code TEXT, "
|
|
"subtitles INTEGER, subtitle_codec TEXT, subtitle_forced, subtitle_language TEXT,"
|
|
"transcode_protocol TEXT, transcode_container TEXT, transcode_video_codec TEXT, transcode_audio_codec TEXT, "
|
|
"transcode_audio_channels INTEGER, transcode_width INTEGER, transcode_height INTEGER, "
|
|
"transcode_hw_requested INTEGER, transcode_hw_full_pipeline INTEGER, "
|
|
"transcode_hw_decode TEXT, transcode_hw_decode_title TEXT, transcode_hw_decoding INTEGER, "
|
|
"transcode_hw_encode TEXT, transcode_hw_encode_title TEXT, transcode_hw_encoding INTEGER, "
|
|
"stream_container TEXT, stream_container_decision TEXT, stream_bitrate INTEGER, "
|
|
"stream_video_decision TEXT, stream_video_bitrate INTEGER, stream_video_codec TEXT, stream_video_codec_level TEXT, "
|
|
"stream_video_bit_depth INTEGER, stream_video_height INTEGER, stream_video_width INTEGER, stream_video_resolution TEXT, "
|
|
"stream_video_framerate TEXT, stream_video_scan_type TEXT, stream_video_full_resolution TEXT, stream_video_dynamic_range TEXT, "
|
|
"stream_audio_decision TEXT, stream_audio_codec TEXT, stream_audio_bitrate INTEGER, stream_audio_channels INTEGER, "
|
|
"stream_audio_language TEXT, stream_audio_language_code TEXT, "
|
|
"stream_subtitle_decision TEXT, stream_subtitle_codec TEXT, "
|
|
"stream_subtitle_container TEXT, stream_subtitle_forced INTEGER, stream_subtitle_language TEXT, "
|
|
"synced_version INTEGER, synced_version_profile TEXT, "
|
|
"optimized_version INTEGER, optimized_version_profile TEXT, optimized_version_title TEXT)"
|
|
)
|
|
|
|
# session_history_metadata table :: This is a table which logs each session"s media metadata
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS session_history_metadata (id INTEGER PRIMARY KEY, "
|
|
"rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, "
|
|
"title TEXT, parent_title TEXT, grandparent_title TEXT, original_title TEXT, full_title TEXT, "
|
|
"media_index INTEGER, parent_media_index INTEGER, "
|
|
"thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, "
|
|
"art TEXT, media_type TEXT, year INTEGER, originally_available_at TEXT, added_at INTEGER, updated_at INTEGER, "
|
|
"last_viewed_at INTEGER, content_rating TEXT, summary TEXT, tagline TEXT, rating TEXT, "
|
|
"duration INTEGER DEFAULT 0, guid TEXT, directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT, "
|
|
"labels TEXT, live INTEGER DEFAULT 0, "
|
|
"channel_call_sign TEXT, channel_id TEXT, channel_identifier TEXT, channel_title TEXT, channel_thumb TEXT, channel_vcn TEXT, "
|
|
"marker_credits_first INTEGER DEFAULT NULL, marker_credits_final INTEGER DEFAULT NULL)"
|
|
)
|
|
|
|
# users table :: This table keeps record of the friends list
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"user_id INTEGER DEFAULT NULL UNIQUE, username TEXT NOT NULL, friendly_name TEXT, "
|
|
"thumb TEXT, custom_avatar_url TEXT, title TEXT, email TEXT, "
|
|
"is_active INTEGER DEFAULT 1, is_admin INTEGER DEFAULT 0, is_home_user INTEGER DEFAULT NULL, "
|
|
"is_allow_sync INTEGER DEFAULT NULL, is_restricted INTEGER DEFAULT NULL, "
|
|
"do_notify INTEGER DEFAULT 1, keep_history INTEGER DEFAULT 1, deleted_user INTEGER DEFAULT 0, "
|
|
"allow_guest INTEGER DEFAULT 0, user_token TEXT, server_token TEXT, shared_libraries TEXT, "
|
|
"filter_all TEXT, filter_movies TEXT, filter_tv TEXT, filter_music TEXT, filter_photos TEXT)"
|
|
)
|
|
|
|
# library_sections table :: This table keeps record of the servers library sections
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS library_sections (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"server_id TEXT, section_id INTEGER, section_name TEXT, section_type TEXT, agent TEXT, "
|
|
"thumb TEXT, custom_thumb_url TEXT, art TEXT, custom_art_url TEXT, "
|
|
"count INTEGER, parent_count INTEGER, child_count INTEGER, is_active INTEGER DEFAULT 1, "
|
|
"do_notify INTEGER DEFAULT 1, do_notify_created INTEGER DEFAULT 1, keep_history INTEGER DEFAULT 1, "
|
|
"deleted_section INTEGER DEFAULT 0, UNIQUE(server_id, section_id))"
|
|
)
|
|
|
|
# user_login table :: This table keeps record of the Tautulli guest logins
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS user_login (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"timestamp INTEGER, user_id INTEGER, user TEXT, user_group TEXT, "
|
|
"ip_address TEXT, host TEXT, user_agent TEXT, success INTEGER DEFAULT 1,"
|
|
"expiry TEXT, jwt_token TEXT)"
|
|
)
|
|
|
|
# notifiers table :: This table keeps record of the notification agent settings
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS notifiers (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"agent_id INTEGER, agent_name TEXT, agent_label TEXT, friendly_name TEXT, notifier_config TEXT, "
|
|
"on_play INTEGER DEFAULT 0, on_stop INTEGER DEFAULT 0, on_pause INTEGER DEFAULT 0, "
|
|
"on_resume INTEGER DEFAULT 0, on_change INTEGER DEFAULT 0, on_buffer INTEGER DEFAULT 0, "
|
|
"on_error INTEGER DEFAULT 0, "
|
|
"on_intro INTEGER DEFAULT 0, on_credits INTEGER DEFAULT 0, on_commercial INTEGER DEFAULT 0, "
|
|
"on_watched INTEGER DEFAULT 0, on_created INTEGER DEFAULT 0, "
|
|
"on_extdown INTEGER DEFAULT 0, on_intdown INTEGER DEFAULT 0, "
|
|
"on_extup INTEGER DEFAULT 0, on_intup INTEGER DEFAULT 0, on_pmsupdate INTEGER DEFAULT 0, "
|
|
"on_concurrent INTEGER DEFAULT 0, on_newdevice INTEGER DEFAULT 0, on_plexpyupdate INTEGER DEFAULT 0, "
|
|
"on_plexpydbcorrupt INTEGER DEFAULT 0, "
|
|
"on_play_subject TEXT, on_stop_subject TEXT, on_pause_subject TEXT, "
|
|
"on_resume_subject TEXT, on_change_subject TEXT, on_buffer_subject TEXT, on_error_subject TEXT, "
|
|
"on_intro_subject TEXT, on_credits_subject TEXT, on_commercial_subject TEXT,"
|
|
"on_watched_subject TEXT, on_created_subject TEXT, on_extdown_subject TEXT, on_intdown_subject TEXT, "
|
|
"on_extup_subject TEXT, on_intup_subject TEXT, on_pmsupdate_subject TEXT, "
|
|
"on_concurrent_subject TEXT, on_newdevice_subject TEXT, on_plexpyupdate_subject TEXT, "
|
|
"on_plexpydbcorrupt_subject TEXT, "
|
|
"on_play_body TEXT, on_stop_body TEXT, on_pause_body TEXT, "
|
|
"on_resume_body TEXT, on_change_body TEXT, on_buffer_body TEXT, on_error_body TEXT, "
|
|
"on_intro_body TEXT, on_credits_body TEXT, on_commercial_body TEXT, "
|
|
"on_watched_body TEXT, on_created_body TEXT, on_extdown_body TEXT, on_intdown_body TEXT, "
|
|
"on_extup_body TEXT, on_intup_body TEXT, on_pmsupdate_body TEXT, "
|
|
"on_concurrent_body TEXT, on_newdevice_body TEXT, on_plexpyupdate_body TEXT, "
|
|
"on_plexpydbcorrupt_body TEXT, "
|
|
"custom_conditions TEXT, custom_conditions_logic TEXT)"
|
|
)
|
|
|
|
# notify_log table :: This is a table which logs notifications sent
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, "
|
|
"session_key INTEGER, rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, "
|
|
"user_id INTEGER, user TEXT, notifier_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, "
|
|
"subject_text TEXT, body_text TEXT, script_args TEXT, success INTEGER DEFAULT 0, tag TEXT)"
|
|
)
|
|
|
|
# newsletters table :: This table keeps record of the newsletter settings
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS newsletters (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"agent_id INTEGER, agent_name TEXT, agent_label TEXT, id_name TEXT NOT NULL, "
|
|
"friendly_name TEXT, newsletter_config TEXT, email_config TEXT, "
|
|
"subject TEXT, body TEXT, message TEXT, "
|
|
"cron TEXT NOT NULL DEFAULT '0 0 * * 0', active INTEGER DEFAULT 0)"
|
|
)
|
|
|
|
# newsletter_log table :: This is a table which logs newsletters sent
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS newsletter_log (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, "
|
|
"newsletter_id INTEGER, agent_id INTEGER, agent_name TEXT, notify_action TEXT, "
|
|
"subject_text TEXT, body_text TEXT, message_text TEXT, start_date TEXT, end_date TEXT, "
|
|
"start_time INTEGER, end_time INTEGER, uuid TEXT UNIQUE, filename TEXT, email_msg_id TEXT, "
|
|
"success INTEGER DEFAULT 0)"
|
|
)
|
|
|
|
# recently_added table :: This table keeps record of recently added items
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS recently_added (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"added_at INTEGER, pms_identifier TEXT, section_id INTEGER, "
|
|
"rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, media_type TEXT, "
|
|
"media_info TEXT)"
|
|
)
|
|
|
|
# mobile_devices table :: This table keeps record of devices linked with the mobile app
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS mobile_devices (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"device_id TEXT NOT NULL UNIQUE, device_token TEXT, device_name TEXT, "
|
|
"platform TEXT, version TEXT, friendly_name TEXT, "
|
|
"onesignal_id TEXT, last_seen INTEGER, official INTEGER DEFAULT 0)"
|
|
)
|
|
|
|
# tvmaze_lookup table :: This table keeps record of the TVmaze lookups
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS tvmaze_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"rating_key INTEGER, thetvdb_id INTEGER, imdb_id TEXT, "
|
|
"tvmaze_id INTEGER, tvmaze_url TEXT, tvmaze_json TEXT)"
|
|
)
|
|
|
|
# themoviedb_lookup table :: This table keeps record of the TheMovieDB lookups
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS themoviedb_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"rating_key INTEGER, thetvdb_id INTEGER, imdb_id TEXT, "
|
|
"themoviedb_id INTEGER, themoviedb_url TEXT, themoviedb_json TEXT)"
|
|
)
|
|
|
|
# musicbrainz_lookup table :: This table keeps record of the MusicBrainz lookups
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS musicbrainz_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"rating_key INTEGER, musicbrainz_id INTEGER, musicbrainz_url TEXT, musicbrainz_type TEXT, "
|
|
"musicbrainz_json TEXT)"
|
|
)
|
|
|
|
# image_hash_lookup table :: This table keeps record of the image hash lookups
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS image_hash_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"img_hash TEXT UNIQUE, img TEXT, rating_key INTEGER, width INTEGER, height INTEGER, "
|
|
"opacity INTEGER, background TEXT, blur INTEGER, fallback TEXT)"
|
|
)
|
|
|
|
# imgur_lookup table :: This table keeps record of the Imgur uploads
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS imgur_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"img_hash TEXT, imgur_title TEXT, imgur_url TEXT, delete_hash TEXT)"
|
|
)
|
|
|
|
# cloudinary_lookup table :: This table keeps record of the Cloudinary uploads
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS cloudinary_lookup (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"img_hash TEXT, cloudinary_title TEXT, cloudinary_url TEXT)"
|
|
)
|
|
|
|
# exports table :: This table keeps record of the exported files
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS exports (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"timestamp INTEGER, section_id INTEGER, user_id INTEGER, rating_key INTEGER, media_type TEXT, "
|
|
"title TEXT, file_format TEXT, "
|
|
"metadata_level INTEGER, media_info_level INTEGER, "
|
|
"thumb_level INTEGER DEFAULT 0, art_level INTEGER DEFAULT 0, "
|
|
"custom_fields TEXT, individual_files INTEGER DEFAULT 0, "
|
|
"file_size INTEGER DEFAULT 0, complete INTEGER DEFAULT 0, "
|
|
"exported_items INTEGER DEFAULT 0, total_items INTEGER DEFAULT 0)"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT started FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN started INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN paused_counter INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN state TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN user TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN machine_id TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT title FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN parent_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN grandparent_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN friendly_name TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN player TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN user_id INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT ip_address FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN ip_address TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN platform TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN parent_rating_key INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN grandparent_rating_key INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN view_offset INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN duration INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN audio_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN width INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN height INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN container TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN audio_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_resolution TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_framerate TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN aspect_ratio TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN audio_channels INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_protocol TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_container TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_video_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_audio_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_audio_channels INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_width INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_height INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT buffer_count FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN buffer_count INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN buffer_last_triggered INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT last_paused FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN last_paused INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT section_id FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN section_id INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT stopped FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stopped INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT transcode_key FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_key TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT write_attempts FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN write_attempts INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT transcode_decision FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN full_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN media_index INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN parent_media_index INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN thumb TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN parent_thumb TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN grandparent_thumb TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN year INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT raw_stream_info FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN product TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN optimized_version INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN optimized_version_profile TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN synced_version INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_width INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_height INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN audio_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN subtitle_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_resolution TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN quality_profile TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_container_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_container TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_width INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_height INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_framerate TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_audio_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_audio_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_audio_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_audio_channels INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN subtitles INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_subtitle_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_subtitle_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN raw_stream_info TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT video_height FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_height INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitles FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN subtitles INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT synced_version_profile FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN synced_version_profile TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN optimized_version_title TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT transcode_hw_decoding FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_hw_decoding INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN transcode_hw_encoding INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT watched FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN watched INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT live FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN live INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN live_uuid TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT session_id FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN session_id TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT original_title FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN original_title TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT secure FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN secure INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN relayed INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT rating_key_websocket FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN rating_key_websocket TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT video_scan_type FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_scan_type TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_full_resolution TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_scan_type TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_full_resolution TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT video_dynamic_range FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN video_dynamic_range TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_video_dynamic_range TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT channel_identifier FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN channel_call_sign TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN channel_identifier TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN channel_thumb TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT originally_available_at FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN originally_available_at TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN added_at INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT guid FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN guid TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT bandwidth FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN bandwidth INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN location TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT initial_stream FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN initial_stream INTEGER DEFAULT 1"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT audio_language FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN audio_language TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN audio_language_code TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_audio_language TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_audio_language_code TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitle_language FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN subtitle_language TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_subtitle_language TEXT"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitle_forced FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN subtitle_forced INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN stream_subtitle_forced INTEGER"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT intro FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN intro INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN credits INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT commercial FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN commercial INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT marker FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug(u"Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN marker INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade sessions table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT channel_id FROM sessions")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table sessions.")
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN channel_id TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN channel_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE sessions ADD COLUMN channel_vcn TEXT"
|
|
)
|
|
|
|
# Upgrade session_history table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT reference_id FROM session_history")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN reference_id INTEGER DEFAULT 0"
|
|
)
|
|
# Set reference_id to the first row where (user_id = previous row, rating_key != previous row) and user_id = user_id
|
|
c_db.execute(
|
|
"UPDATE session_history " \
|
|
"SET reference_id = (SELECT (CASE \
|
|
WHEN (SELECT MIN(id) FROM session_history WHERE id > ( \
|
|
SELECT MAX(id) FROM session_history \
|
|
WHERE (user_id = t1.user_id AND rating_key <> t1.rating_key AND id < t1.id)) AND user_id = t1.user_id) IS NULL \
|
|
THEN (SELECT MIN(id) FROM session_history WHERE (user_id = t1.user_id)) \
|
|
ELSE (SELECT MIN(id) FROM session_history WHERE id > ( \
|
|
SELECT MAX(id) FROM session_history \
|
|
WHERE (user_id = t1.user_id AND rating_key <> t1.rating_key AND id < t1.id)) AND user_id = t1.user_id) END) " \
|
|
"FROM session_history AS t1 " \
|
|
"WHERE t1.id = session_history.id) "
|
|
)
|
|
|
|
# Upgrade session_history table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT bandwidth FROM session_history")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN platform_version TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN product TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN product_version TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN profile TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN bandwidth INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN location TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN quality_profile TEXT"
|
|
)
|
|
|
|
# Upgrade session_history table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT secure FROM session_history")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN secure INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN relayed INTEGER"
|
|
)
|
|
|
|
# Upgrade session_history table from earlier versions
|
|
try:
|
|
result = c_db.execute("SELECT platform FROM session_history "
|
|
"WHERE platform = 'windows'").fetchall()
|
|
if len(result) > 0:
|
|
logger.debug("Altering database. Capitalizing Windows platform values in session_history table.")
|
|
c_db.execute(
|
|
"UPDATE session_history SET platform = 'Windows' WHERE platform = 'windows' "
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to capitalize Windows platform values in session_history table.")
|
|
|
|
# Upgrade session_history table from earlier versions
|
|
try:
|
|
result = c_db.execute("SELECT platform FROM session_history "
|
|
"WHERE platform = 'macos'").fetchall()
|
|
if len(result) > 0:
|
|
logger.debug("Altering database. Capitalizing macOS platform values in session_history table.")
|
|
c_db.execute(
|
|
"UPDATE session_history SET platform = 'macOS' WHERE platform = 'macos' "
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to capitalize macOS platform values in session_history table.")
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT full_title FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN full_title TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT tagline FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN tagline TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT labels FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN labels TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT original_title FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN original_title TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT live FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN live INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN channel_call_sign TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN channel_identifier TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN channel_thumb TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT marker_credits_first FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN marker_credits_first INTEGER DEFAULT NULL"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN marker_credits_final INTEGER DEFAULT NULL"
|
|
)
|
|
|
|
# Upgrade session_history_metadata table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT channel_id FROM session_history_metadata")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_metadata.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN channel_id TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN channel_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata ADD COLUMN channel_vcn TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT transcode_decision FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET transcode_decision = (CASE "
|
|
"WHEN video_decision = 'transcode' OR audio_decision = 'transcode' THEN 'transcode' "
|
|
"WHEN video_decision = 'copy' OR audio_decision = 'copy' THEN 'copy' "
|
|
"WHEN video_decision = 'direct play' OR audio_decision = 'direct play' THEN 'direct play' END)"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitles FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_bit_depth INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_codec_level TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_width INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_height INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN audio_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_requested INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_full_pipeline INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_decode TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_encode TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_decode_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_encode_title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_container TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_container_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_codec_level TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_bit_depth INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_height INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_width INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_resolution TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_framerate TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_audio_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_audio_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_audio_bitrate INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_audio_channels INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_subtitle_decision TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_subtitle_codec TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_subtitle_container TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_subtitle_forced INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN subtitles INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN synced_version INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN optimized_version INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN optimized_version_profile TEXT"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET video_resolution=REPLACE(video_resolution, 'p', '')"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET video_resolution=REPLACE(video_resolution, 'SD', 'sd')"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitle_codec FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN subtitle_codec TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT synced_version_profile FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN synced_version_profile TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN optimized_version_title TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT transcode_hw_decoding FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_decoding INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN transcode_hw_encoding INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET subtitle_codec = '' WHERE subtitle_codec IS NULL"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
result = c_db.execute("SELECT stream_container FROM session_history_media_info "
|
|
"WHERE stream_container IS NULL").fetchall()
|
|
if len(result) > 0:
|
|
logger.debug("Altering database. Removing NULL values from session_history_media_info table.")
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_container = '' WHERE stream_container IS NULL"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_video_codec = '' WHERE stream_video_codec IS NULL"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_audio_codec = '' WHERE stream_audio_codec IS NULL"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_subtitle_codec = '' WHERE stream_subtitle_codec IS NULL"
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to remove NULL values from session_history_media_info table.")
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT video_scan_type FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_scan_type TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_full_resolution TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_scan_type TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_full_resolution TEXT"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET video_scan_type = 'progressive' "
|
|
"WHERE video_resolution != ''"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_video_scan_type = 'progressive' "
|
|
"WHERE stream_video_resolution != '' AND stream_video_resolution IS NOT NULL"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET video_full_resolution = (CASE "
|
|
"WHEN video_resolution = '' OR video_resolution = 'SD' OR video_resolution = '4k' THEN video_resolution "
|
|
"WHEN video_resolution = 'sd' THEN 'SD' "
|
|
"ELSE video_resolution || 'p' END)"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_video_full_resolution = ( "
|
|
"CASE WHEN stream_video_resolution = '' OR stream_video_resolution = 'SD' OR stream_video_resolution = '4k' "
|
|
"THEN stream_video_resolution "
|
|
"WHEN stream_video_resolution = 'sd' THEN 'SD' "
|
|
"ELSE stream_video_resolution || 'p' END)"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT video_dynamic_range FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN video_dynamic_range TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_video_dynamic_range TEXT"
|
|
)
|
|
|
|
result = c_db.execute("SELECT * FROM session_history_media_info "
|
|
"WHERE video_dynamic_range = 'SDR' AND stream_video_dynamic_range = 'HDR'").fetchone()
|
|
if result:
|
|
c_db.execute(
|
|
"UPDATE session_history_media_info SET stream_video_dynamic_range = 'SDR' "
|
|
"WHERE video_dynamic_range = 'SDR' AND stream_video_dynamic_range = 'HDR'"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT audio_language FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN audio_language TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN audio_language_code TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_audio_language TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_audio_language_code TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitle_language FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN subtitle_language TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN stream_subtitle_language TEXT"
|
|
)
|
|
|
|
# Upgrade session_history_media_info table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT subtitle_forced FROM session_history_media_info")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history_media_info.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_media_info ADD COLUMN subtitle_forced INTEGER"
|
|
)
|
|
|
|
# Upgrade session_history table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT section_id FROM session_history")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table session_history.")
|
|
c_db.execute(
|
|
"ALTER TABLE session_history ADD COLUMN section_id INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE session_history SET section_id = ("
|
|
"SELECT section_id FROM session_history_metadata "
|
|
"WHERE session_history_metadata.id = session_history.id)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS session_history_metadata_temp (id INTEGER PRIMARY KEY, "
|
|
"rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, "
|
|
"title TEXT, parent_title TEXT, grandparent_title TEXT, original_title TEXT, full_title TEXT, "
|
|
"media_index INTEGER, parent_media_index INTEGER, "
|
|
"thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, "
|
|
"art TEXT, media_type TEXT, year INTEGER, originally_available_at TEXT, added_at INTEGER, updated_at INTEGER, "
|
|
"last_viewed_at INTEGER, content_rating TEXT, summary TEXT, tagline TEXT, rating TEXT, "
|
|
"duration INTEGER DEFAULT 0, guid TEXT, directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT, "
|
|
"labels TEXT, live INTEGER DEFAULT 0, channel_call_sign TEXT, channel_identifier TEXT, channel_thumb TEXT)"
|
|
)
|
|
c_db.execute(
|
|
"INSERT INTO session_history_metadata_temp (id, rating_key, parent_rating_key, grandparent_rating_key, "
|
|
"title, parent_title, grandparent_title, original_title, full_title, "
|
|
"media_index, parent_media_index, "
|
|
"thumb, parent_thumb, grandparent_thumb, "
|
|
"art, media_type, year, originally_available_at, added_at, updated_at, "
|
|
"last_viewed_at, content_rating, summary, tagline, rating, "
|
|
"duration, guid, directors, writers, actors, genres, studio, "
|
|
"labels, live, channel_call_sign, channel_identifier, channel_thumb) "
|
|
"SELECT id, rating_key, parent_rating_key, grandparent_rating_key, "
|
|
"title, parent_title, grandparent_title, original_title, full_title, "
|
|
"media_index, parent_media_index, "
|
|
"thumb, parent_thumb, grandparent_thumb, "
|
|
"art, media_type, year, originally_available_at, added_at, updated_at, "
|
|
"last_viewed_at, content_rating, summary, tagline, rating, "
|
|
"duration, guid, directors, writers, actors, genres, studio, "
|
|
"labels, live, channel_call_sign, channel_identifier, channel_thumb "
|
|
"FROM session_history_metadata"
|
|
)
|
|
c_db.execute(
|
|
"DROP TABLE session_history_metadata"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE session_history_metadata_temp RENAME TO session_history_metadata"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT do_notify FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN do_notify INTEGER DEFAULT 1"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT keep_history FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN keep_history INTEGER DEFAULT 1"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT custom_avatar_url FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN custom_avatar_url TEXT"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT deleted_user FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN deleted_user INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT allow_guest FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN allow_guest INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN user_token TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN server_token TEXT"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT shared_libraries FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN shared_libraries TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN filter_all TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN filter_movies TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN filter_tv TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN filter_music TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN filter_photos TEXT"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT is_admin FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN is_admin INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT is_active FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN is_active INTEGER DEFAULT 1"
|
|
)
|
|
|
|
# Upgrade users table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT title FROM users")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table users.")
|
|
c_db.execute(
|
|
"ALTER TABLE users ADD COLUMN title TEXT"
|
|
)
|
|
|
|
try:
|
|
result = c_db.execute("SELECT * FROM users WHERE friendly_name = username").fetchall()
|
|
if result:
|
|
logger.debug("Altering database. Resetting user friendly names equal to username.")
|
|
c_db.execute("UPDATE users SET friendly_name = NULL WHERE friendly_name = username")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
# Upgrade notify_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT poster_url FROM notify_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notify_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE notify_log ADD COLUMN poster_url TEXT"
|
|
)
|
|
|
|
# Upgrade notify_log table from earlier versions (populate table with data from notify_log)
|
|
try:
|
|
c_db.execute("SELECT timestamp FROM notify_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notify_log.")
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS notify_log_temp (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER, "
|
|
"session_key INTEGER, rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, "
|
|
"user_id INTEGER, user TEXT, agent_id INTEGER, agent_name TEXT, notify_action TEXT, "
|
|
"subject_text TEXT, body_text TEXT, script_args TEXT, poster_url TEXT)"
|
|
)
|
|
c_db.execute(
|
|
"INSERT INTO notify_log_temp (session_key, rating_key, user_id, user, agent_id, agent_name, "
|
|
"poster_url, timestamp, notify_action) "
|
|
"SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, timestamp, "
|
|
"notify_action FROM notify_log_temp "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_play, 'play' FROM notify_log WHERE on_play "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_stop, 'stop' FROM notify_log WHERE on_stop "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_watched, 'watched' FROM notify_log WHERE on_watched "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_pause, 'pause' FROM notify_log WHERE on_pause "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_resume, 'resume' FROM notify_log WHERE on_resume "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_buffer, 'buffer' FROM notify_log WHERE on_buffer "
|
|
"UNION ALL SELECT session_key, rating_key, user_id, user, agent_id, agent_name, poster_url, "
|
|
"on_created, 'created' FROM notify_log WHERE on_created "
|
|
"ORDER BY timestamp ")
|
|
c_db.execute(
|
|
"DROP TABLE notify_log"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notify_log_temp RENAME TO notify_log"
|
|
)
|
|
|
|
# Upgrade notify_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT notifier_id FROM notify_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notify_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE notify_log ADD COLUMN notifier_id INTEGER"
|
|
)
|
|
|
|
# Upgrade notify_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT success FROM notify_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notify_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE notify_log ADD COLUMN success INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE notify_log SET success = 1"
|
|
)
|
|
|
|
# Upgrade notify_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT tag FROM notify_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notify_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE notify_log ADD COLUMN tag TEXT"
|
|
)
|
|
|
|
# Upgrade newsletter_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT start_time FROM newsletter_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table newsletter_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE newsletter_log ADD COLUMN start_time INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE newsletter_log ADD COLUMN end_time INTEGER"
|
|
)
|
|
|
|
# Upgrade newsletter_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT filename FROM newsletter_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table newsletter_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE newsletter_log ADD COLUMN filename TEXT"
|
|
)
|
|
|
|
# Upgrade newsletter_log table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT email_msg_id FROM newsletter_log")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table newsletter_log.")
|
|
c_db.execute(
|
|
"ALTER TABLE newsletter_log ADD COLUMN email_msg_id TEXT"
|
|
)
|
|
|
|
# Upgrade newsletters table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT id_name FROM newsletters")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table newsletters.")
|
|
c_db.execute(
|
|
"ALTER TABLE newsletters ADD COLUMN id_name TEXT NOT NULL"
|
|
)
|
|
|
|
# Upgrade newsletters table from earlier versions
|
|
try:
|
|
result = c_db.execute("SELECT SQL FROM sqlite_master WHERE type='table' AND name='newsletters'").fetchone()
|
|
if "TEXT NOT NULL DEFAULT \"0 0 * * 0\"" in result[0]:
|
|
logger.debug("Altering database. Updating default cron value in newsletters table.")
|
|
c_db.execute(
|
|
"CREATE TABLE newsletters_temp (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"agent_id INTEGER, agent_name TEXT, agent_label TEXT, id_name TEXT NOT NULL, "
|
|
"friendly_name TEXT, newsletter_config TEXT, email_config TEXT, "
|
|
"subject TEXT, body TEXT, message TEXT, "
|
|
"cron TEXT NOT NULL DEFAULT '0 0 * * 0', active INTEGER DEFAULT 0)"
|
|
)
|
|
c_db.execute(
|
|
"INSERT INTO newsletters_temp (id, agent_id, agent_name, agent_label, id_name, "
|
|
"friendly_name, newsletter_config, email_config, subject, body, message, cron, active) "
|
|
"SELECT id, agent_id, agent_name, agent_label, id_name, "
|
|
"friendly_name, newsletter_config, email_config, subject, body, message, cron, active "
|
|
"FROM newsletters"
|
|
)
|
|
c_db.execute(
|
|
"DROP TABLE newsletters"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE newsletters_temp RENAME TO newsletters"
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to update default cron value in newsletters table.")
|
|
try:
|
|
c_db.execute(
|
|
"DROP TABLE newsletters_temp"
|
|
)
|
|
except:
|
|
pass
|
|
|
|
# Upgrade library_sections table from earlier versions (remove UNIQUE constraint on section_id)
|
|
try:
|
|
result = c_db.execute("SELECT SQL FROM sqlite_master WHERE type='table' AND name='library_sections'").fetchone()
|
|
if "section_id INTEGER UNIQUE" in result[0]:
|
|
logger.debug("Altering database. Removing unique constraint on section_id from library_sections table.")
|
|
c_db.execute(
|
|
"CREATE TABLE library_sections_temp (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"server_id TEXT, section_id INTEGER, section_name TEXT, section_type TEXT, "
|
|
"thumb TEXT, custom_thumb_url TEXT, art TEXT, count INTEGER, parent_count INTEGER, child_count INTEGER, "
|
|
"do_notify INTEGER DEFAULT 1, do_notify_created INTEGER DEFAULT 1, keep_history INTEGER DEFAULT 1, "
|
|
"deleted_section INTEGER DEFAULT 0, UNIQUE(server_id, section_id))"
|
|
)
|
|
c_db.execute(
|
|
"INSERT INTO library_sections_temp (id, server_id, section_id, section_name, section_type, "
|
|
"thumb, custom_thumb_url, art, count, parent_count, child_count, do_notify, do_notify_created, "
|
|
"keep_history, deleted_section) "
|
|
"SELECT id, server_id, section_id, section_name, section_type, "
|
|
"thumb, custom_thumb_url, art, count, parent_count, child_count, do_notify, do_notify_created, "
|
|
"keep_history, deleted_section "
|
|
"FROM library_sections"
|
|
)
|
|
c_db.execute(
|
|
"DROP TABLE library_sections"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE library_sections_temp RENAME TO library_sections"
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to remove section_id unique constraint from library_sections.")
|
|
try:
|
|
c_db.execute(
|
|
"DROP TABLE library_sections_temp"
|
|
)
|
|
except:
|
|
pass
|
|
|
|
# Upgrade library_sections table from earlier versions (remove duplicated libraries)
|
|
try:
|
|
result = c_db.execute("SELECT * FROM library_sections WHERE server_id = ''").fetchall()
|
|
if len(result) > 0:
|
|
logger.debug("Altering database. Removing duplicate libraries from library_sections table.")
|
|
c_db.execute(
|
|
"DELETE FROM library_sections WHERE server_id = ''"
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to remove duplicate libraries from library_sections table.")
|
|
|
|
# Upgrade library_sections table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT agent FROM library_sections")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table library_sections.")
|
|
c_db.execute(
|
|
"ALTER TABLE library_sections ADD COLUMN agent TEXT"
|
|
)
|
|
|
|
# Upgrade library_sections table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT custom_art_url FROM library_sections")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table library_sections.")
|
|
c_db.execute(
|
|
"ALTER TABLE library_sections ADD COLUMN custom_art_url TEXT"
|
|
)
|
|
|
|
# Upgrade library_sections table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT is_active FROM library_sections")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table library_sections.")
|
|
c_db.execute(
|
|
"ALTER TABLE library_sections ADD COLUMN is_active INTEGER DEFAULT 1"
|
|
)
|
|
|
|
# Upgrade library_sections table from earlier versions
|
|
try:
|
|
result = c_db.execute("SELECT thumb, art FROM library_sections WHERE section_id = ?",
|
|
[common.LIVE_TV_SECTION_ID]).fetchone()
|
|
if result and (not result[0] or not result[1]):
|
|
logger.debug("Altering database. Updating database table library_sections.")
|
|
c_db.execute("UPDATE library_sections SET thumb = ?, art =? WHERE section_id = ?",
|
|
[common.DEFAULT_LIVE_TV_THUMB,
|
|
common.DEFAULT_LIVE_TV_ART_FULL,
|
|
common.LIVE_TV_SECTION_ID])
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
# Upgrade users table from earlier versions (remove UNIQUE constraint on username)
|
|
try:
|
|
result = c_db.execute("SELECT SQL FROM sqlite_master WHERE type='table' AND name='users'").fetchone()
|
|
if "username TEXT NOT NULL UNIQUE" in result[0]:
|
|
logger.debug("Altering database. Removing unique constraint on username from users table.")
|
|
c_db.execute(
|
|
"CREATE TABLE users_temp (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"user_id INTEGER DEFAULT NULL UNIQUE, username TEXT NOT NULL, friendly_name TEXT, "
|
|
"thumb TEXT, custom_avatar_url TEXT, email TEXT, is_home_user INTEGER DEFAULT NULL, "
|
|
"is_allow_sync INTEGER DEFAULT NULL, is_restricted INTEGER DEFAULT NULL, do_notify INTEGER DEFAULT 1, "
|
|
"keep_history INTEGER DEFAULT 1, deleted_user INTEGER DEFAULT 0)"
|
|
)
|
|
c_db.execute(
|
|
"INSERT INTO users_temp (id, user_id, username, friendly_name, thumb, custom_avatar_url, "
|
|
"email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user) "
|
|
"SELECT id, user_id, username, friendly_name, thumb, custom_avatar_url, "
|
|
"email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history, deleted_user "
|
|
"FROM users"
|
|
)
|
|
c_db.execute(
|
|
"DROP TABLE users"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE users_temp RENAME TO users"
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Unable to remove username unique constraint from users.")
|
|
try:
|
|
c_db.execute(
|
|
"DROP TABLE users_temp"
|
|
)
|
|
except:
|
|
pass
|
|
|
|
# Upgrade mobile_devices table from earlier versions
|
|
try:
|
|
result = c_db.execute("SELECT SQL FROM sqlite_master WHERE type='table' AND name='mobile_devices'").fetchone()
|
|
if "device_token TEXT NOT NULL UNIQUE" in result[0]:
|
|
logger.debug("Altering database. Dropping and recreating mobile_devices table.")
|
|
c_db.execute(
|
|
"DROP TABLE mobile_devices"
|
|
)
|
|
c_db.execute(
|
|
"CREATE TABLE IF NOT EXISTS mobile_devices (id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
"device_id TEXT NOT NULL UNIQUE, device_token TEXT, device_name TEXT, friendly_name TEXT)"
|
|
)
|
|
except sqlite3.OperationalError:
|
|
logger.warn("Failed to recreate mobile_devices table.")
|
|
pass
|
|
|
|
# Upgrade mobile_devices table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT last_seen FROM mobile_devices")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table mobile_devices.")
|
|
c_db.execute(
|
|
"ALTER TABLE mobile_devices ADD COLUMN last_seen INTEGER"
|
|
)
|
|
|
|
# Upgrade mobile_devices table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT official FROM mobile_devices")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table mobile_devices.")
|
|
c_db.execute(
|
|
"ALTER TABLE mobile_devices ADD COLUMN official INTEGER DEFAULT 0"
|
|
)
|
|
# Update official mobile device flag
|
|
for device_id, in c_db.execute("SELECT device_id FROM mobile_devices").fetchall():
|
|
c_db.execute("UPDATE mobile_devices SET official = ? WHERE device_id = ?",
|
|
[mobile_app.validate_onesignal_id(device_id), device_id])
|
|
|
|
# Upgrade mobile_devices table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT onesignal_id FROM mobile_devices")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table mobile_devices.")
|
|
c_db.execute(
|
|
"ALTER TABLE mobile_devices ADD COLUMN onesignal_id TEXT"
|
|
)
|
|
|
|
# Upgrade mobile_devices table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT platform FROM mobile_devices")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table mobile_devices.")
|
|
c_db.execute(
|
|
"ALTER TABLE mobile_devices ADD COLUMN platform TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE mobile_devices ADD COLUMN version TEXT"
|
|
)
|
|
# Update mobile device platforms
|
|
for device_id, in c_db.execute(
|
|
"SELECT device_id FROM mobile_devices WHERE official > 0").fetchall():
|
|
c_db.execute("UPDATE mobile_devices SET platform = ? WHERE device_id = ?",
|
|
["android", device_id])
|
|
|
|
# Upgrade notifiers table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT custom_conditions FROM notifiers")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notifiers.")
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN custom_conditions TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN custom_conditions_logic TEXT"
|
|
)
|
|
|
|
# Upgrade notifiers table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT on_change FROM notifiers")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notifiers.")
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_change INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_change_subject TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_change_body TEXT"
|
|
)
|
|
|
|
# Upgrade notifiers table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT on_plexpydbcorrupt FROM notifiers")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notifiers.")
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_plexpydbcorrupt INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_plexpydbcorrupt_subject TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_plexpydbcorrupt_body TEXT"
|
|
)
|
|
|
|
# Upgrade notifiers table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT on_error FROM notifiers")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notifiers.")
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_error INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_error_subject TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_error_body TEXT"
|
|
)
|
|
|
|
# Upgrade notifiers table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT on_intro FROM notifiers")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notifiers.")
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_intro INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_intro_subject TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_intro_body TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_credits INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_credits_subject TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_credits_body TEXT"
|
|
)
|
|
|
|
# Upgrade notifiers table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT on_commercial FROM notifiers")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table notifiers.")
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_commercial INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_commercial_subject TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE notifiers ADD COLUMN on_commercial_body TEXT"
|
|
)
|
|
|
|
# Upgrade tvmaze_lookup table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT rating_key FROM tvmaze_lookup")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table tvmaze_lookup.")
|
|
c_db.execute(
|
|
"ALTER TABLE tvmaze_lookup ADD COLUMN rating_key INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"DROP INDEX IF EXISTS idx_tvmaze_lookup_thetvdb_id"
|
|
)
|
|
c_db.execute(
|
|
"DROP INDEX IF EXISTS idx_tvmaze_lookup_imdb_id"
|
|
)
|
|
|
|
# Upgrade themoviedb_lookup table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT rating_key FROM themoviedb_lookup")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table themoviedb_lookup.")
|
|
c_db.execute(
|
|
"ALTER TABLE themoviedb_lookup ADD COLUMN rating_key INTEGER"
|
|
)
|
|
c_db.execute(
|
|
"DROP INDEX IF EXISTS idx_themoviedb_lookup_thetvdb_id"
|
|
)
|
|
c_db.execute(
|
|
"DROP INDEX IF EXISTS idx_themoviedb_lookup_imdb_id"
|
|
)
|
|
|
|
# Upgrade user_login table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT success FROM user_login")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table user_login.")
|
|
c_db.execute(
|
|
"ALTER TABLE user_login ADD COLUMN success INTEGER DEFAULT 1"
|
|
)
|
|
|
|
# Upgrade user_login table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT expiry FROM user_login")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table user_login.")
|
|
c_db.execute(
|
|
"ALTER TABLE user_login ADD COLUMN expiry TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE user_login ADD COLUMN jwt_token TEXT"
|
|
)
|
|
|
|
# Rename notifiers in the database
|
|
result = c_db.execute("SELECT agent_label FROM notifiers "
|
|
"WHERE agent_label = 'XBMC' "
|
|
"OR agent_label = 'OSX Notify' "
|
|
"OR agent_name = 'androidapp'").fetchone()
|
|
if result:
|
|
logger.debug("Altering database. Renaming notifiers.")
|
|
c_db.execute(
|
|
"UPDATE notifiers SET agent_label = 'Kodi' WHERE agent_label = 'XBMC'"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE notifiers SET agent_label = 'macOS Notification Center' WHERE agent_label = 'OSX Notify'"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE notifiers SET agent_name = 'remoteapp', agent_label = 'Tautulli Remote App' "
|
|
"WHERE agent_name = 'androidapp'"
|
|
)
|
|
|
|
# Upgrade exports table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT thumb_level FROM exports")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table exports.")
|
|
c_db.execute(
|
|
"ALTER TABLE exports ADD COLUMN thumb_level INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE exports SET thumb_level = 9 WHERE include_thumb = 1"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE exports ADD COLUMN art_level INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"UPDATE exports SET art_level = 9 WHERE include_art = 1"
|
|
)
|
|
|
|
# Upgrade exports table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT title FROM exports")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table exports.")
|
|
c_db.execute(
|
|
"ALTER TABLE exports ADD COLUMN title TEXT"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE exports ADD COLUMN individual_files INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Upgrade exports table from earlier versions
|
|
try:
|
|
c_db.execute("SELECT total_items FROM exports")
|
|
except sqlite3.OperationalError:
|
|
logger.debug("Altering database. Updating database table exports.")
|
|
c_db.execute(
|
|
"ALTER TABLE exports ADD COLUMN exported_items INTEGER DEFAULT 0"
|
|
)
|
|
c_db.execute(
|
|
"ALTER TABLE exports ADD COLUMN total_items INTEGER DEFAULT 0"
|
|
)
|
|
|
|
# Fix unique constraints
|
|
try:
|
|
c_db.execute("DELETE FROM tvmaze_lookup "
|
|
"WHERE id NOT IN (SELECT MIN(id) FROM tvmaze_lookup GROUP BY rating_key)")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
try:
|
|
c_db.execute("DELETE FROM themoviedb_lookup "
|
|
"WHERE id NOT IN (SELECT MIN(id) FROM themoviedb_lookup GROUP BY rating_key)")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
try:
|
|
c_db.execute("DELETE FROM musicbrainz_lookup "
|
|
"WHERE id NOT IN (SELECT MIN(id) FROM musicbrainz_lookup GROUP BY rating_key)")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
try:
|
|
c_db.execute("DELETE FROM image_hash_lookup "
|
|
"WHERE id NOT IN (SELECT MIN(id) FROM image_hash_lookup GROUP BY img_hash)")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
try:
|
|
c_db.execute("DELETE FROM cloudinary_lookup "
|
|
"WHERE id NOT IN (SELECT MIN(id) FROM cloudinary_lookup GROUP BY img_hash)")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
try:
|
|
c_db.execute("DELETE FROM imgur_lookup "
|
|
"WHERE id NOT IN (SELECT MIN(id) FROM imgur_lookup GROUP BY img_hash)")
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
# Add "Local" user to database as default unauthenticated user.
|
|
result = c_db.execute("SELECT id FROM users WHERE username = 'Local'")
|
|
if not result.fetchone():
|
|
logger.debug("User 'Local' does not exist. Adding user.")
|
|
c_db.execute("INSERT INTO users (user_id, username) VALUES (0, 'Local')")
|
|
|
|
# Create session_history table indices
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_media_type "
|
|
"ON session_history (media_type)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_media_type_stopped "
|
|
"ON session_history (media_type, stopped ASC)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_rating_key "
|
|
"ON session_history (rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_parent_rating_key "
|
|
"ON session_history (parent_rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_grandparent_rating_key "
|
|
"ON session_history (grandparent_rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_user "
|
|
"ON session_history (user)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_user_id "
|
|
"ON session_history (user_id)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_user_id_stopped "
|
|
"ON session_history (user_id, stopped ASC)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_section_id "
|
|
"ON session_history (section_id)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_section_id_stopped "
|
|
"ON session_history (section_id, stopped ASC)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_reference_id "
|
|
"ON session_history (reference_id ASC)"
|
|
)
|
|
|
|
# Create session_history_metadata table indices
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_metadata_rating_key "
|
|
"ON session_history_metadata (rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_metadata_guid "
|
|
"ON session_history_metadata (guid)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_metadata_live "
|
|
"ON session_history_metadata (live)"
|
|
)
|
|
|
|
# Create session_history_media_info table indices
|
|
c_db.execute(
|
|
"CREATE INDEX IF NOT EXISTS idx_session_history_media_info_transcode_decision "
|
|
"ON session_history_media_info (transcode_decision)"
|
|
)
|
|
|
|
# Create lookup table indices
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_tvmaze_lookup "
|
|
"ON tvmaze_lookup (rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_themoviedb_lookup "
|
|
"ON themoviedb_lookup (rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_musicbrainz_lookup "
|
|
"ON musicbrainz_lookup (rating_key)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_image_hash_lookup "
|
|
"ON image_hash_lookup (img_hash)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_cloudinary_lookup "
|
|
"ON cloudinary_lookup (img_hash)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_imgur_lookup "
|
|
"ON imgur_lookup (img_hash)"
|
|
)
|
|
c_db.execute(
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_continued "
|
|
"ON sessions_continued (user_id, machine_id, media_type)"
|
|
)
|
|
|
|
# Set database version
|
|
result = c_db.execute("SELECT value FROM version_info WHERE key = 'version'").fetchone()
|
|
if not result:
|
|
c_db.execute(
|
|
"INSERT OR REPLACE INTO version_info (key, value) VALUES ('version', ?)",
|
|
[common.RELEASE]
|
|
)
|
|
elif helpers.version_to_tuple(result[0]) < helpers.version_to_tuple(common.RELEASE):
|
|
c_db.execute(
|
|
"UPDATE version_info SET value = ? WHERE key = 'version'",
|
|
[common.RELEASE]
|
|
)
|
|
|
|
conn_db.commit()
|
|
c_db.close()
|
|
|
|
# Migrate poster_urls to imgur_lookup table
|
|
try:
|
|
db = database.MonitorDatabase()
|
|
result = db.select("SELECT SQL FROM sqlite_master WHERE type='table' AND name='poster_urls'")
|
|
if result:
|
|
result = db.select("SELECT * FROM poster_urls")
|
|
logger.debug("Altering database. Updating database table imgur_lookup.")
|
|
|
|
data_factory = datafactory.DataFactory()
|
|
|
|
for row in result:
|
|
img_hash = notification_handler.set_hash_image_info(
|
|
rating_key=row['rating_key'], width=1000, height=1500, fallback='poster')
|
|
data_factory.set_img_info(img_hash=img_hash, img_title=row['poster_title'],
|
|
img_url=row['poster_url'], delete_hash=row['delete_hash'],
|
|
service='imgur')
|
|
|
|
db.action('DROP TABLE poster_urls')
|
|
except sqlite3.OperationalError:
|
|
pass
|
|
|
|
|
|
def upgrade():
|
|
if CONFIG.UPGRADE_FLAG == 0:
|
|
mobile_app.revalidate_onesignal_ids()
|
|
CONFIG.UPGRADE_FLAG = 1
|
|
CONFIG.write()
|
|
|
|
return
|
|
|
|
|
|
def shutdown(restart=False, update=False, checkout=False, reset=False):
|
|
webstart.stop()
|
|
|
|
# Shutdown the websocket connection
|
|
if WEBSOCKET:
|
|
web_socket.shutdown()
|
|
|
|
if SCHED.running:
|
|
SCHED.shutdown(wait=False)
|
|
if activity_handler.ACTIVITY_SCHED.running:
|
|
activity_handler.ACTIVITY_SCHED.shutdown(wait=False)
|
|
|
|
# Stop the notification threads
|
|
for i in range(CONFIG.NOTIFICATION_THREADS):
|
|
NOTIFY_QUEUE.put(None)
|
|
|
|
CONFIG.write()
|
|
|
|
if update:
|
|
logger.info("Tautulli is updating...")
|
|
try:
|
|
versioncheck.update()
|
|
except Exception as e:
|
|
logger.warn("Tautulli failed to update: %s. Restarting." % e)
|
|
|
|
if checkout:
|
|
logger.info("Tautulli is switching the git branch...")
|
|
try:
|
|
versioncheck.checkout_git_branch()
|
|
except Exception as e:
|
|
logger.warn("Tautulli failed to switch git branch: %s. Restarting." % e)
|
|
|
|
if reset:
|
|
logger.info("Tautulli is resetting the git install...")
|
|
try:
|
|
versioncheck.reset_git_install()
|
|
except Exception as e:
|
|
logger.warn("Tautulli failed to reset git install: %s. Restarting." % e)
|
|
|
|
if CREATEPID:
|
|
logger.info("Removing PID file: %s", PIDFILE)
|
|
try:
|
|
os.remove(PIDFILE)
|
|
except OSError:
|
|
logger.warn("Failed to remove PID file '%s'", PIDFILE)
|
|
|
|
if restart:
|
|
logger.info("Tautulli is restarting...")
|
|
|
|
exe = sys.executable
|
|
if FROZEN:
|
|
args = [exe]
|
|
else:
|
|
args = [exe, FULL_PATH]
|
|
args += ARGS
|
|
if '--nolaunch' not in args:
|
|
args += ['--nolaunch']
|
|
|
|
# Separate out logger so we can shutdown logger after
|
|
if NOFORK:
|
|
logger.info("Running as service, not forking. Exiting...")
|
|
else:
|
|
logger.info("Restarting Tautulli with %s", args)
|
|
|
|
# os.execv fails with spaced names on Windows
|
|
# https://bugs.python.org/issue19066
|
|
if NOFORK:
|
|
pass
|
|
elif common.PLATFORM in ('Windows', 'Darwin'):
|
|
subprocess.Popen(args, cwd=os.getcwd())
|
|
else:
|
|
os.execv(exe, args)
|
|
|
|
else:
|
|
logger.info("Tautulli is shutting down...")
|
|
|
|
logger.shutdown()
|
|
|
|
if WIN_SYS_TRAY_ICON:
|
|
WIN_SYS_TRAY_ICON.shutdown()
|
|
elif MAC_SYS_TRAY_ICON:
|
|
MAC_SYS_TRAY_ICON.shutdown()
|
|
|
|
os._exit(0)
|
|
|
|
|
|
def generate_uuid():
|
|
return uuid.uuid4().hex
|
|
|
|
|
|
def initialize_tracker():
|
|
tracker = GtagMP(
|
|
api_secret='Cl_LjAKUT26AS22YZwqaPw',
|
|
measurement_id='G-NH1M4BYM2P',
|
|
client_id=CONFIG.PMS_UUID
|
|
)
|
|
return tracker
|
|
|
|
|
|
def analytics_event(name, **kwargs):
|
|
event = TRACKER.create_new_event(name=name)
|
|
event.set_event_param('name', common.PRODUCT)
|
|
event.set_event_param('version', common.RELEASE)
|
|
event.set_event_param('install', INSTALL_TYPE)
|
|
event.set_event_param('branch', CONFIG.GIT_BRANCH)
|
|
event.set_event_param('platform', common.PLATFORM)
|
|
event.set_event_param('platformRelease', common.PLATFORM_RELEASE)
|
|
event.set_event_param('platformVersion', common.PLATFORM_VERSION[:100])
|
|
event.set_event_param('linuxDistro', common.PLATFORM_LINUX_DISTRO)
|
|
event.set_event_param('pythonVersion', common.PYTHON_VERSION)
|
|
event.set_event_param('language', SYS_LANGUAGE)
|
|
event.set_event_param('encoding', SYS_ENCODING)
|
|
event.set_event_param('timezone', str(SYS_TIMEZONE))
|
|
event.set_event_param('timezoneUTCOffset', f'UTC{SYS_UTC_OFFSET}')
|
|
|
|
for key, value in kwargs.items():
|
|
event.set_event_param(key, value)
|
|
|
|
plex_tv = plextv.PlexTV()
|
|
ip_address = plex_tv.get_public_ip(output_format='text')
|
|
geolocation = plex_tv.get_geoip_lookup(ip_address) or {}
|
|
|
|
event.set_event_param('country', geolocation.get('country', 'Unknown'))
|
|
event.set_event_param('countryCode', geolocation.get('code', 'Unknown'))
|
|
|
|
if TRACKER:
|
|
try:
|
|
TRACKER.send(events=[event])
|
|
except Exception as e:
|
|
logger.warn("Failed to send analytics event for name '%s': %s" % (name, e))
|
|
|
|
|
|
def check_folder_writable(folder, fallback, name):
|
|
if not folder:
|
|
folder = fallback
|
|
|
|
if not os.path.exists(folder):
|
|
try:
|
|
os.makedirs(folder)
|
|
except OSError as e:
|
|
logger.error("Could not create %s dir '%s': %s" % (name, folder, e))
|
|
if fallback and folder != fallback:
|
|
logger.warn("Falling back to %s dir '%s'" % (name, fallback))
|
|
return check_folder_writable(None, fallback, name)
|
|
else:
|
|
return folder, None
|
|
|
|
if not os.access(folder, os.W_OK):
|
|
logger.error("Cannot write to %s dir '%s'" % (name, folder))
|
|
if fallback and folder != fallback:
|
|
logger.warn("Falling back to %s dir '%s'" % (name, fallback))
|
|
return check_folder_writable(None, fallback, name)
|
|
else:
|
|
return folder, False
|
|
|
|
return folder, True
|
|
|
|
|
|
def get_tautulli_info():
|
|
tautulli = {
|
|
'tautulli_install_type': INSTALL_TYPE,
|
|
'tautulli_version': common.RELEASE,
|
|
'tautulli_branch': CONFIG.GIT_BRANCH,
|
|
'tautulli_commit': CURRENT_VERSION,
|
|
'tautulli_platform':common.PLATFORM,
|
|
'tautulli_platform_release': common.PLATFORM_RELEASE,
|
|
'tautulli_platform_version': common.PLATFORM_VERSION,
|
|
'tautulli_platform_linux_distro': common.PLATFORM_LINUX_DISTRO,
|
|
'tautulli_platform_device_name': common.PLATFORM_DEVICE_NAME,
|
|
'tautulli_python_version': common.PYTHON_VERSION,
|
|
}
|
|
return tautulli
|
|
|
|
|
|
def alert_message(msg, title='Tautulli Startup Error'):
|
|
if common.PLATFORM == 'Windows':
|
|
ctypes.windll.user32.MessageBoxW(0, str(msg), str(title), 0)
|
|
elif common.PLATFORM == 'Darwin':
|
|
applescript = 'display dialog "%s" with title "%s"' \
|
|
'with icon caution buttons {"OK"}' % (msg, title)
|
|
os.system("osascript -e '%s'" % applescript)
|