mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-16 02:02:58 -07:00
Merge branch 'nightly' into python3
# Conflicts: # plexpy/datafactory.py # plexpy/libraries.py # plexpy/logger.py # plexpy/version.py
This commit is contained in:
commit
19d8c1be5a
33 changed files with 443 additions and 231 deletions
|
@ -677,7 +677,8 @@ def dbcheck():
|
|||
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, count INTEGER, parent_count INTEGER, child_count INTEGER, '
|
||||
'thumb TEXT, custom_thumb_url TEXT, art TEXT, custom_art_url 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))'
|
||||
)
|
||||
|
@ -1325,6 +1326,18 @@ def dbcheck():
|
|||
'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(u"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(u"Unable to capitalize Windows 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')
|
||||
|
@ -1886,6 +1899,15 @@ def dbcheck():
|
|||
'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(u"Altering database. Updating database table library_sections.")
|
||||
c_db.execute(
|
||||
'ALTER TABLE library_sections ADD COLUMN custom_art_url TEXT'
|
||||
)
|
||||
|
||||
# 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()
|
||||
|
@ -2072,7 +2094,7 @@ def upgrade():
|
|||
libraries.update_libraries_db_notify()
|
||||
|
||||
|
||||
def shutdown(restart=False, update=False, checkout=False):
|
||||
def shutdown(restart=False, update=False, checkout=False, reset=False):
|
||||
webstart.stop()
|
||||
|
||||
# Shutdown the websocket connection
|
||||
|
@ -2107,6 +2129,13 @@ def shutdown(restart=False, update=False, checkout=False):
|
|||
except Exception as e:
|
||||
logger.warn("Tautulli failed to switch git branch: %s. Restarting." % e)
|
||||
|
||||
if reset:
|
||||
logger.info(u"Tautulli is resetting the git install...")
|
||||
try:
|
||||
versioncheck.reset_git_install()
|
||||
except Exception as e:
|
||||
logger.warn(u"Tautulli failed to reset git install: %s. Restarting." % e)
|
||||
|
||||
if CREATEPID:
|
||||
logger.info("Removing pidfile %s", PIDFILE)
|
||||
os.remove(PIDFILE)
|
||||
|
|
|
@ -348,10 +348,16 @@ NOTIFICATION_PARAMETERS = [
|
|||
'category': 'Stream Details',
|
||||
'parameters': [
|
||||
{'name': 'Streams', 'type': 'int', 'value': 'streams', 'description': 'The number of concurrent streams.'},
|
||||
{'name': 'User Streams', 'type': 'int', 'value': 'user_streams', 'description': 'The number of concurrent streams by the person streaming.'},
|
||||
{'name': 'User', 'type': 'str', 'value': 'user', 'description': 'The friendly name of the person streaming.'},
|
||||
{'name': 'Username', 'type': 'str', 'value': 'username', 'description': 'The username of the person streaming.'},
|
||||
{'name': 'User Email', 'type': 'str', 'value': 'user_email', 'description': 'The email address of the person streaming.'},
|
||||
{'name': 'Direct Plays', 'type': 'int', 'value': 'direct_plays', 'description': 'The number of concurrent direct plays.'},
|
||||
{'name': 'Direct Streams', 'type': 'int', 'value': 'direct_streams', 'description': 'The number of concurrent direct streams.'},
|
||||
{'name': 'Transcodes', 'type': 'int', 'value': 'transcodes', 'description': 'The number of concurrent transcodes.'},
|
||||
{'name': 'User Streams', 'type': 'int', 'value': 'user_streams', 'description': 'The number of concurrent streams by the user streaming.'},
|
||||
{'name': 'User Direct Plays', 'type': 'int', 'value': 'user_direct_plays', 'description': 'The number of concurrent direct plays by the user streaming.'},
|
||||
{'name': 'User Direct Streams', 'type': 'int', 'value': 'user_direct_streams', 'description': 'The number of concurrent direct streams by the user streaming.'},
|
||||
{'name': 'User Transcodes', 'type': 'int', 'value': 'user_transcodes', 'description': 'The number of concurrent transcodes by the user streaming.'},
|
||||
{'name': 'User', 'type': 'str', 'value': 'user', 'description': 'The friendly name of the user streaming.'},
|
||||
{'name': 'Username', 'type': 'str', 'value': 'username', 'description': 'The username of the user streaming.'},
|
||||
{'name': 'User Email', 'type': 'str', 'value': 'user_email', 'description': 'The email address of the user streaming.'},
|
||||
{'name': 'Device', 'type': 'str', 'value': 'device', 'description': 'The type of client device being used for playback.'},
|
||||
{'name': 'Platform', 'type': 'str', 'value': 'platform', 'description': 'The type of client platform being used for playback.'},
|
||||
{'name': 'Product', 'type': 'str', 'value': 'product', 'description': 'The type of client product being used for playback.'},
|
||||
|
@ -538,6 +544,7 @@ NOTIFICATION_PARAMETERS = [
|
|||
{'name': 'Rating Key', 'type': 'int', 'value': 'rating_key', 'description': 'The unique identifier for the movie, episode, or track.'},
|
||||
{'name': 'Parent Rating Key', 'type': 'int', 'value': 'parent_rating_key', 'description': 'The unique identifier for the season or album.'},
|
||||
{'name': 'Grandparent Rating Key', 'type': 'int', 'value': 'grandparent_rating_key', 'description': 'The unique identifier for the TV show or artist.'},
|
||||
{'name': 'Art', 'type': 'str', 'value': 'art', 'description': 'The Plex background art for the media.'},
|
||||
{'name': 'Thumb', 'type': 'str', 'value': 'thumb', 'description': 'The Plex thumbnail for the movie or episode.'},
|
||||
{'name': 'Parent Thumb', 'type': 'str', 'value': 'parent_thumb', 'description': 'The Plex thumbnail for the season or album.'},
|
||||
{'name': 'Grandparent Thumb', 'type': 'str', 'value': 'grandparent_thumb', 'description': 'The Plex thumbnail for the TV show or artist.'},
|
||||
|
|
|
@ -872,7 +872,8 @@ class DataFactory(object):
|
|||
|
||||
try:
|
||||
query = 'SELECT section_id, section_name, section_type, thumb AS library_thumb, ' \
|
||||
'custom_thumb_url AS custom_thumb, art, count, parent_count, child_count ' \
|
||||
'custom_thumb_url AS custom_thumb, art AS library_art, custom_art_url AS custom_art, ' \
|
||||
'count, parent_count, child_count ' \
|
||||
'FROM library_sections ' \
|
||||
'WHERE section_id IN (%s) ' \
|
||||
'ORDER BY section_type, count DESC, parent_count DESC, child_count DESC ' % ','.join(library_cards)
|
||||
|
@ -889,11 +890,16 @@ class DataFactory(object):
|
|||
else:
|
||||
library_thumb = common.DEFAULT_COVER_THUMB
|
||||
|
||||
if item['custom_art'] and item['custom_art'] != item['library_art']:
|
||||
library_art = item['custom_art']
|
||||
else:
|
||||
library_art = item['library_art']
|
||||
|
||||
library = {'section_id': item['section_id'],
|
||||
'section_name': item['section_name'],
|
||||
'section_type': item['section_type'],
|
||||
'thumb': library_thumb,
|
||||
'art': item['art'],
|
||||
'art': library_art,
|
||||
'count': item['count'],
|
||||
'child_count': item['parent_count'],
|
||||
'grandchild_count': item['child_count']
|
||||
|
@ -1410,16 +1416,29 @@ class DataFactory(object):
|
|||
|
||||
return lookup_info
|
||||
|
||||
def delete_lookup_info(self, rating_key='', title=''):
|
||||
def delete_lookup_info(self, rating_key='', service='', delete_all=False):
|
||||
if not rating_key and not delete_all:
|
||||
logger.error(u"Tautulli DataFactory :: Unable to delete lookup info: rating_key not provided.")
|
||||
return False
|
||||
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
if rating_key:
|
||||
logger.info("Tautulli DataFactory :: Deleting lookup info for '%s' (rating_key %s) from the database."
|
||||
logger.info("Tautulli DataFactory :: Deleting lookup info for rating_key %s from the database."
|
||||
% (title, rating_key))
|
||||
result_tvmaze = monitor_db.action('DELETE FROM tvmaze_lookup WHERE rating_key = ?', [rating_key])
|
||||
result_themoviedb = monitor_db.action('DELETE FROM themoviedb_lookup WHERE rating_key = ?', [rating_key])
|
||||
result_tvmaze = monitor_db.action('DELETE FROM tvmaze_lookup WHERE rating_key = ?', [rating_key])
|
||||
result_musicbrainz = monitor_db.action('DELETE FROM musicbrainz_lookup WHERE rating_key = ?', [rating_key])
|
||||
return True if (result_tvmaze or result_themoviedb or result_musicbrainz) else False
|
||||
return bool(result_themoviedb or result_tvmaze or result_musicbrainz)
|
||||
elif service and delete_all:
|
||||
if service.lower() in ('themoviedb', 'tvmaze', 'musicbrainz'):
|
||||
logger.info(u"Tautulli DataFactory :: Deleting all lookup info for '%s' from the database."
|
||||
% service)
|
||||
result = monitor_db.action('DELETE FROM %s_lookup' % service.lower())
|
||||
return bool(result)
|
||||
else:
|
||||
logger.error(u"Tautulli DataFactory :: Unable to delete lookup info: invalid service '%s' provided."
|
||||
% service)
|
||||
|
||||
def get_search_query(self, rating_key=''):
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
|
|
@ -51,7 +51,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -165,7 +165,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -296,7 +296,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -409,7 +409,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -530,7 +530,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -626,7 +626,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -735,7 +735,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -841,7 +841,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -927,7 +927,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -1037,7 +1037,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
@ -1127,7 +1127,7 @@ class Graphs(object):
|
|||
if grouping is None:
|
||||
grouping = plexpy.CONFIG.GROUP_HISTORY_TABLES
|
||||
|
||||
group_by = 'reference_id' if grouping else 'id'
|
||||
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
|
||||
|
||||
try:
|
||||
if y_axis == 'plays':
|
||||
|
|
|
@ -1042,7 +1042,7 @@ def build_datatables_json(kwargs, dt_columns, default_sort_col=None):
|
|||
|
||||
def humanFileSize(bytes, si=False):
|
||||
if str(bytes).isdigit():
|
||||
bytes = int(bytes)
|
||||
bytes = cast_to_float(bytes)
|
||||
else:
|
||||
return bytes
|
||||
|
||||
|
|
|
@ -715,13 +715,14 @@ class Libraries(object):
|
|||
logger.debug("Tautulli Libraries :: File sizes updated for section_id %s." % section_id)
|
||||
|
||||
return True
|
||||
|
||||
def set_config(self, section_id=None, custom_thumb='', do_notify=1, keep_history=1, do_notify_created=1):
|
||||
def set_config(self, section_id=None, custom_thumb='', custom_art='',
|
||||
do_notify=1, keep_history=1, do_notify_created=1):
|
||||
if section_id:
|
||||
monitor_db = database.MonitorDatabase()
|
||||
|
||||
key_dict = {'section_id': section_id}
|
||||
value_dict = {'custom_thumb_url': custom_thumb,
|
||||
'custom_art_url': custom_art,
|
||||
'do_notify': do_notify,
|
||||
'do_notify_created': do_notify_created,
|
||||
'keep_history': keep_history}
|
||||
|
@ -754,7 +755,8 @@ class Libraries(object):
|
|||
try:
|
||||
if str(section_id).isdigit():
|
||||
query = 'SELECT section_id, section_name, section_type, count, parent_count, child_count, ' \
|
||||
'thumb AS library_thumb, custom_thumb_url AS custom_thumb, art, ' \
|
||||
'thumb AS library_thumb, custom_thumb_url AS custom_thumb, art AS library_art, ' \
|
||||
'custom_art_url AS custom_art, ' \
|
||||
'do_notify, do_notify_created, keep_history, deleted_section ' \
|
||||
'FROM library_sections ' \
|
||||
'WHERE section_id = ? '
|
||||
|
@ -775,11 +777,16 @@ class Libraries(object):
|
|||
else:
|
||||
library_thumb = common.DEFAULT_COVER_THUMB
|
||||
|
||||
if item['custom_art'] and item['custom_art'] != item['library_art']:
|
||||
library_art = item['custom_art']
|
||||
else:
|
||||
library_art = item['library_art']
|
||||
|
||||
library_details = {'section_id': item['section_id'],
|
||||
'section_name': item['section_name'],
|
||||
'section_type': item['section_type'],
|
||||
'library_thumb': library_thumb,
|
||||
'library_art': item['art'],
|
||||
'library_art': library_art,
|
||||
'count': item['count'],
|
||||
'parent_count': item['parent_count'],
|
||||
'child_count': item['child_count'],
|
||||
|
|
|
@ -148,11 +148,12 @@ class PublicIPFilter(RegexFilter):
|
|||
super(PublicIPFilter, self).__init__()
|
||||
|
||||
# Currently only checking for ipv4 addresses
|
||||
self.regex = re.compile(r'[0-9]+(?:\.[0-9]+){3}(?!\d*-[a-z0-9]{6})')
|
||||
self.regex = re.compile(r'[0-9]+(?:[.-][0-9]+){3}(?!\d*-[a-z0-9]{6})')
|
||||
|
||||
def replace(self, text, ip):
|
||||
if is_public_ip(ip):
|
||||
return text.replace(ip, ip.partition('.')[0] + '.***.***.***')
|
||||
if is_public_ip(ip.replace('-', '.')):
|
||||
partition = '-' if '-' in ip else '.'
|
||||
return text.replace(ip, ip.partition(partition)[0] + (partition + '***') * 3)
|
||||
return text
|
||||
|
||||
|
||||
|
|
|
@ -565,6 +565,8 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||
transcode_decision = 'Direct Stream'
|
||||
else:
|
||||
transcode_decision = 'Direct Play'
|
||||
transcode_decision_count = Counter(s['transcode_decision'] for s in sessions)
|
||||
user_transcode_decision_count = Counter(s['transcode_decision'] for s in user_sessions)
|
||||
|
||||
if notify_action != 'on_play':
|
||||
stream_duration = int(old_div((time.time() -
|
||||
|
@ -701,6 +703,13 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||
poster_key = notify_params['parent_rating_key']
|
||||
poster_title = '%s - %s' % (notify_params['grandparent_title'],
|
||||
notify_params['parent_title'])
|
||||
elif notify_params['media_type'] == 'clip':
|
||||
if notify_params['extra_type']:
|
||||
poster_thumb = notify_params['art'].replace('/art', '/thumb') or notify_params['thumb']
|
||||
else:
|
||||
poster_thumb = notify_params['parent_thumb'] or notify_params['thumb']
|
||||
poster_key = notify_params['rating_key']
|
||||
poster_title = notify_params['title']
|
||||
else:
|
||||
poster_thumb = ''
|
||||
poster_key = ''
|
||||
|
@ -812,7 +821,13 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||
'utctime': helpers.utc_now_iso(),
|
||||
# Stream parameters
|
||||
'streams': stream_count,
|
||||
'direct_plays': transcode_decision_count['direct play'],
|
||||
'direct_streams': transcode_decision_count['copy'],
|
||||
'transcodes': transcode_decision_count['transcode'],
|
||||
'user_streams': user_stream_count,
|
||||
'user_direct_plays': user_transcode_decision_count['direct play'],
|
||||
'user_direct_streams': user_transcode_decision_count['copy'],
|
||||
'user_transcodes': user_transcode_decision_count['transcode'],
|
||||
'user': notify_params['friendly_name'],
|
||||
'username': notify_params['user'],
|
||||
'user_email': notify_params['email'],
|
||||
|
@ -1006,6 +1021,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
|
|||
'rating_key': notify_params['rating_key'],
|
||||
'parent_rating_key': notify_params['parent_rating_key'],
|
||||
'grandparent_rating_key': notify_params['grandparent_rating_key'],
|
||||
'art': notify_params['art'],
|
||||
'thumb': notify_params['thumb'],
|
||||
'parent_thumb': notify_params['parent_thumb'],
|
||||
'grandparent_thumb': notify_params['grandparent_thumb'],
|
||||
|
@ -1210,10 +1226,6 @@ def strip_tag(data, agent_id=None):
|
|||
'font': ['color']}
|
||||
data = bleach.clean(data, tags=list(whitelist.keys()), attributes=whitelist, strip=True)
|
||||
|
||||
elif agent_id in (10, 14, 20):
|
||||
# Don't remove tags for Email, Slack, and Discord
|
||||
pass
|
||||
|
||||
elif agent_id == 13:
|
||||
# Allow tags b, i, code, pre, a[href] for Telegram
|
||||
whitelist = {'b': [],
|
||||
|
@ -1223,6 +1235,10 @@ def strip_tag(data, agent_id=None):
|
|||
'a': ['href']}
|
||||
data = bleach.clean(data, tags=list(whitelist.keys()), attributes=whitelist, strip=True)
|
||||
|
||||
elif agent_id in (10, 14, 20, 25):
|
||||
# Don't remove tags for Email, Slack, Discord, and Webhook
|
||||
pass
|
||||
|
||||
else:
|
||||
whitelist = {}
|
||||
data = bleach.clean(data, tags=list(whitelist.keys()), attributes=whitelist, strip=True)
|
||||
|
|
|
@ -17,5 +17,5 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
PLEXPY_BRANCH = "beta"
|
||||
PLEXPY_RELEASE_VERSION = "v2.2.0-beta"
|
||||
PLEXPY_BRANCH = "master"
|
||||
PLEXPY_RELEASE_VERSION = "v2.2.0"
|
||||
|
|
|
@ -337,9 +337,12 @@ def update():
|
|||
return
|
||||
|
||||
|
||||
def reset():
|
||||
def reset_git_install():
|
||||
if plexpy.INSTALL_TYPE == 'git':
|
||||
logger.info('Attempting to reset git install to "%s/%s"' % (plexpy.CONFIG.GIT_REMOTE, plexpy.CONFIG.GIT_BRANCH))
|
||||
logger.info('Attempting to reset git install to "{}/{}/{}"'.format(plexpy.CONFIG.GIT_REMOTE,
|
||||
plexpy.CONFIG.GIT_BRANCH,
|
||||
common.RELEASE))
|
||||
|
||||
output, err = runGit('remote set-url {} https://github.com/{}/{}.git'.format(plexpy.CONFIG.GIT_REMOTE,
|
||||
plexpy.CONFIG.GIT_USER,
|
||||
plexpy.CONFIG.GIT_REPO))
|
||||
|
@ -347,10 +350,7 @@ def reset():
|
|||
output, err = runGit('checkout {}'.format(plexpy.CONFIG.GIT_BRANCH))
|
||||
output, err = runGit('branch -u {}/{}'.format(plexpy.CONFIG.GIT_REMOTE,
|
||||
plexpy.CONFIG.GIT_BRANCH))
|
||||
output, err = runGit('reset --hard {}/{}'.format(plexpy.CONFIG.GIT_REMOTE,
|
||||
plexpy.CONFIG.GIT_BRANCH))
|
||||
output, err = runGit('pull {} {}'.format(plexpy.CONFIG.GIT_REMOTE,
|
||||
plexpy.CONFIG.GIT_BRANCH))
|
||||
output, err = runGit('reset --hard {}'.format(common.RELEASE))
|
||||
|
||||
if not output:
|
||||
logger.error('Unable to reset Tautulli installation.')
|
||||
|
@ -367,8 +367,11 @@ def reset():
|
|||
|
||||
def checkout_git_branch():
|
||||
if plexpy.INSTALL_TYPE == 'git':
|
||||
output, err = runGit('fetch %s' % plexpy.CONFIG.GIT_REMOTE)
|
||||
output, err = runGit('checkout %s' % plexpy.CONFIG.GIT_BRANCH)
|
||||
logger.info('Attempting to checkout git branch "{}/{}"'.format(plexpy.CONFIG.GIT_REMOTE,
|
||||
plexpy.CONFIG.GIT_BRANCH))
|
||||
|
||||
output, err = runGit('fetch {}'.format(plexpy.CONFIG.GIT_REMOTE))
|
||||
output, err = runGit('checkout {}'.format(plexpy.CONFIG.GIT_BRANCH))
|
||||
|
||||
if not output:
|
||||
logger.error('Unable to change git branch.')
|
||||
|
@ -398,6 +401,9 @@ def read_changelog(latest_only=False, since_prev_release=False):
|
|||
header_pattern = re.compile(r'(^#+)\s(.+)')
|
||||
list_pattern = re.compile(r'(^[ \t]*\*\s)(.+)')
|
||||
|
||||
beta_release = False
|
||||
prev_release = str(plexpy.PREV_RELEASE)
|
||||
|
||||
with open(changelog_file, "r") as logfile:
|
||||
for line in logfile:
|
||||
line_header_match = re.search(header_pattern, line)
|
||||
|
@ -415,8 +421,16 @@ def read_changelog(latest_only=False, since_prev_release=False):
|
|||
elif latest_only:
|
||||
latest_version_found = True
|
||||
# Add a space to the end of the release to match tags
|
||||
elif since_prev_release and str(plexpy.PREV_RELEASE) + ' ' in header_text:
|
||||
break
|
||||
elif since_prev_release:
|
||||
if prev_release.endswith('-beta') and not beta_release:
|
||||
if prev_release + ' ' in header_text:
|
||||
break
|
||||
elif prev_release.replace('-beta', '') + ' ' in header_text:
|
||||
beta_release = True
|
||||
elif prev_release.endswith('-beta') and beta_release:
|
||||
break
|
||||
elif prev_release + ' ' in header_text:
|
||||
break
|
||||
|
||||
output[-1] += '<h' + header_level + '>' + header_text + '</h' + header_level + '>'
|
||||
|
||||
|
|
|
@ -537,6 +537,7 @@ class WebInterface(object):
|
|||
|
||||
Optional parameters:
|
||||
custom_thumb (str): The URL for the custom library thumbnail
|
||||
custom_art (str): The URL for the custom library background art
|
||||
keep_history (int): 0 or 1
|
||||
|
||||
Returns:
|
||||
|
@ -544,6 +545,7 @@ class WebInterface(object):
|
|||
```
|
||||
"""
|
||||
custom_thumb = kwargs.get('custom_thumb', '')
|
||||
custom_art = kwargs.get('custom_art', '')
|
||||
do_notify = kwargs.get('do_notify', 0)
|
||||
do_notify_created = kwargs.get('do_notify_created', 0)
|
||||
keep_history = kwargs.get('keep_history', 0)
|
||||
|
@ -553,6 +555,7 @@ class WebInterface(object):
|
|||
library_data = libraries.Libraries()
|
||||
library_data.set_config(section_id=section_id,
|
||||
custom_thumb=custom_thumb,
|
||||
custom_art=custom_art,
|
||||
do_notify=do_notify,
|
||||
do_notify_created=do_notify_created,
|
||||
keep_history=keep_history)
|
||||
|
@ -3928,16 +3931,9 @@ class WebInterface(object):
|
|||
return self.do_state_change('checkout', 'Switching Git Branches', 120)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
def reset_git_install(self, **kwargs):
|
||||
result = versioncheck.reset()
|
||||
|
||||
if result:
|
||||
return {'result': 'success', 'message': 'Tautulli installation reset.'}
|
||||
else:
|
||||
return {'result': 'error', 'message': 'Reset installation failed.'}
|
||||
|
||||
return self.do_state_change('reset', 'Resetting to {}'.format(common.RELEASE), 120)
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
@ -3971,29 +3967,29 @@ class WebInterface(object):
|
|||
"pms_web_url": plexpy.CONFIG.PMS_WEB_URL
|
||||
}
|
||||
|
||||
if source == 'history':
|
||||
data_factory = datafactory.DataFactory()
|
||||
metadata = data_factory.get_metadata_details(rating_key=rating_key, guid=guid)
|
||||
if metadata:
|
||||
poster_info = data_factory.get_poster_info(metadata=metadata)
|
||||
metadata.update(poster_info)
|
||||
lookup_info = data_factory.get_lookup_info(metadata=metadata)
|
||||
metadata.update(lookup_info)
|
||||
else:
|
||||
# Try to get metadata from the Plex server first
|
||||
if rating_key:
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
metadata = pms_connect.get_metadata_details(rating_key=rating_key)
|
||||
if metadata:
|
||||
data_factory = datafactory.DataFactory()
|
||||
poster_info = data_factory.get_poster_info(metadata=metadata)
|
||||
metadata.update(poster_info)
|
||||
lookup_info = data_factory.get_lookup_info(metadata=metadata)
|
||||
metadata.update(lookup_info)
|
||||
|
||||
# If the item is not found on the Plex server, get the metadata from history
|
||||
if not metadata and source == 'history':
|
||||
data_factory = datafactory.DataFactory()
|
||||
metadata = data_factory.get_metadata_details(rating_key=rating_key, guid=guid)
|
||||
|
||||
if metadata:
|
||||
data_factory = datafactory.DataFactory()
|
||||
poster_info = data_factory.get_poster_info(metadata=metadata)
|
||||
metadata.update(poster_info)
|
||||
lookup_info = data_factory.get_lookup_info(metadata=metadata)
|
||||
metadata.update(lookup_info)
|
||||
|
||||
if metadata:
|
||||
if metadata['section_id'] and not allow_session_library(metadata['section_id']):
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||
|
||||
return serve_template(templatename="info.html", metadata=metadata, title="Info", config=config, source=source)
|
||||
return serve_template(templatename="info.html", metadata=metadata, title="Info",
|
||||
config=config, source=source)
|
||||
else:
|
||||
if get_session_user_id():
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||
|
@ -4360,15 +4356,18 @@ class WebInterface(object):
|
|||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def delete_lookup_info(self, rating_key='', title='', **kwargs):
|
||||
def delete_lookup_info(self, rating_key='', service='', delete_all=False, **kwargs):
|
||||
""" Delete the 3rd party API lookup info.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
None
|
||||
|
||||
Optional parameters:
|
||||
rating_key (int): 1234
|
||||
(Note: Must be the movie, show, artist, album, or track rating key)
|
||||
Optional parameters:
|
||||
None
|
||||
service (str): 'themoviedb' or 'tvmaze' or 'musicbrainz'
|
||||
delete_all (bool): 'true' to delete all images form the service
|
||||
|
||||
Returns:
|
||||
json:
|
||||
|
@ -4378,7 +4377,7 @@ class WebInterface(object):
|
|||
"""
|
||||
|
||||
data_factory = datafactory.DataFactory()
|
||||
result = data_factory.delete_lookup_info(rating_key=rating_key, title=title)
|
||||
result = data_factory.delete_lookup_info(rating_key=rating_key, service=service, delete_all=delete_all)
|
||||
|
||||
if result:
|
||||
return {'result': 'success', 'message': 'Deleted lookup info.'}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue