diff --git a/data/interfaces/default/logs.html b/data/interfaces/default/logs.html index d795ac26..3747d549 100644 --- a/data/interfaces/default/logs.html +++ b/data/interfaces/default/logs.html @@ -21,6 +21,7 @@ Logs
@@ -120,7 +121,7 @@ LoadPlexPyLogs(); clearSearchButton('log_table', log_table); }); - + function LoadPlexPyLogs() { log_table_options.ajax = { url: "getLog" @@ -157,6 +158,7 @@ $("#plexpy-logs-btn").click(function () { $("#clear-logs").show(); + $("#download-plexpylog").show() $("#clear-notify-logs").hide(); LoadPlexPyLogs(); clearSearchButton('log_table', log_table); @@ -164,6 +166,7 @@ $("#plex-logs-btn").click(function () { $("#clear-logs").hide(); + $("#download-plexpylog").hide() $("#clear-notify-logs").hide(); LoadPlexLogs(); clearSearchButton('plex_log_table', plex_log_table); @@ -171,6 +174,7 @@ $("#plex-scanner-logs-btn").click(function () { $("#clear-logs").hide(); + $("#download-plexpylog").hide() $("#clear-notify-logs").hide(); LoadPlexScannerLogs(); clearSearchButton('plex_scanner_log_table', plex_scanner_log_table); @@ -178,6 +182,7 @@ $("#notification-logs-btn").click(function () { $("#clear-logs").hide(); + $("#download-plexpylog").hide() $("#clear-notify-logs").show(); LoadNotificationLogs(); clearSearchButton('notification_log_table', notification_log_table); @@ -190,6 +195,11 @@ } }); + $("#download-plexpylog").click(function () { + window.location.href = "download_log"; + }); + + $("#clear-notify-logs").click(function () { var r = confirm("Are you sure you want to clear the PlexPy notification log?"); if (r == true) { diff --git a/plexpy/__init__.py b/plexpy/__init__.py index 5995f259..d07cd117 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -67,8 +67,6 @@ CONFIG_FILE = None DB_FILE = None -LOG_LIST = [] - INSTALL_TYPE = None CURRENT_VERSION = None LATEST_VERSION = None @@ -134,7 +132,7 @@ def initialize(config_file): try: os.makedirs(CONFIG.BACKUP_DIR) except OSError as e: - logger.error("Could not create backup dir '%s': %s", BACKUP_DIR, e) + logger.error("Could not create backup dir '%s': %s" % (BACKUP_DIR, e)) if not CONFIG.CACHE_DIR: CONFIG.CACHE_DIR = os.path.join(DATA_DIR, 'cache') @@ -142,14 +140,14 @@ def initialize(config_file): try: os.makedirs(CONFIG.CACHE_DIR) except OSError as e: - logger.error("Could not create cache dir '%s': %s", CACHE_DIR, e) + logger.error("Could not create cache dir '%s': %s" % (CACHE_DIR, e)) # Initialize the database logger.info('Checking to see if the database has all tables....') try: dbcheck() except Exception as e: - logger.error("Can't connect to the database: %s", e) + logger.error("Can't connect to the database: %s" % e) # Check if PlexPy has a uuid if CONFIG.PMS_UUID == '' or not CONFIG.PMS_UUID: @@ -171,8 +169,8 @@ def initialize(config_file): 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) + 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: @@ -219,7 +217,7 @@ def daemonize(): pid = os.fork() # @UndefinedVariable - only available in UNIX if pid != 0: sys.exit(0) - except OSError, e: + except OSError as e: raise RuntimeError("1st fork failed: %s [%d]", e.strerror, e.errno) os.setsid() @@ -233,7 +231,7 @@ def daemonize(): pid = os.fork() # @UndefinedVariable - only available in UNIX if pid != 0: sys.exit(0) - except OSError, e: + except OSError as e: raise RuntimeError("2nd fork failed: %s [%d]", e.strerror, e.errno) dev_null = file('/dev/null', 'r') @@ -269,7 +267,7 @@ def launch_browser(host, port, root): try: webbrowser.open('%s://%s:%i%s' % (protocol, host, port, root)) except Exception as e: - logger.error('Could not launch browser: %s', e) + logger.error('Could not launch browser: %s' % e) def initialize_scheduler(): @@ -949,7 +947,7 @@ def shutdown(restart=False, update=False): try: versioncheck.update() except Exception as e: - logger.warn('PlexPy failed to update: %s. Restarting.', e) + logger.warn('PlexPy failed to update: %s. Restarting.' % e) if CREATEPID: logger.info('Removing pidfile %s', PIDFILE) @@ -963,7 +961,7 @@ def shutdown(restart=False, update=False): if '--nolaunch' not in args: args += ['--nolaunch'] logger.info('Restarting PlexPy with %s', args) - + # os.execv fails with spaced names on Windows # https://bugs.python.org/issue19066 if os.name == 'nt': diff --git a/plexpy/api2.py b/plexpy/api2.py index b0d085a1..65ca2ee5 100644 --- a/plexpy/api2.py +++ b/plexpy/api2.py @@ -443,6 +443,9 @@ General optional parameters: if self._api_cmd == 'docs_md': return out['response']['data'] + elif self._api_cmd == 'download_log': + return + if self._api_out_type == 'json': cherrypy.response.headers['Content-Type'] = 'application/json;charset=UTF-8' try: diff --git a/plexpy/logger.py b/plexpy/logger.py index 0ab56bf1..b43e06d3 100644 --- a/plexpy/logger.py +++ b/plexpy/logger.py @@ -31,7 +31,7 @@ import helpers # These settings are for file logging only FILENAME = "plexpy.log" -MAX_SIZE = 1000000 # 1 MB +MAX_SIZE = 5000000 # 5 MB MAX_FILES = 5 _BLACKLIST_WORDS = [] @@ -42,18 +42,6 @@ logger = logging.getLogger("plexpy") # Global queue for multiprocessing logging queue = None -class LogListHandler(logging.Handler): - """ - Log handler for Web UI. - """ - - def emit(self, record): - message = self.format(record) - message = message.replace("\n", "', '').replace('', '') + s = '
%s' % s + return s + + filt = [] + fa = filt.append + with open(os.path.join(plexpy.CONFIG.LOG_DIR, 'plexpy.log')) as f: + for l in f.readlines(): + try: + temp_loglevel_and_time = l.split('- ') + loglvl = temp_loglevel_and_time[1].split(' :')[0].strip() + msg = l.split(' : ')[1].replace('\n', '') + fa([temp_loglevel_and_time[0], loglvl, msg]) + except IndexError: + # Add traceback message to previous msg.. + tl = (len(filt) - 1) + filt[tl][2] += ' ' + l + # Inject the pre tag since we only want it on tracebacks + filt[tl][2] = oh_i_feel_so_dirty(filt[tl][2]) + continue + filtered = [] - if search_value == "": - filtered = plexpy.LOG_LIST[::] + if search_value == '': + filtered = filt else: - filtered = [row for row in plexpy.LOG_LIST for column in row if search_value.lower() in column.lower()] + filtered = [row for row in filt for column in row if search_value.lower() in column.lower()] sortcolumn = 0 if order_column == '1': @@ -1959,11 +1987,10 @@ class WebInterface(object): filtered.sort(key=lambda x: x[sortcolumn], reverse=order_dir == "desc") rows = filtered[start:(start + length)] - rows = [[row[0], row[2], row[1]] for row in rows] return json.dumps({ 'recordsFiltered': len(filtered), - 'recordsTotal': len(plexpy.LOG_LIST), + 'recordsTotal': len(filt), 'data': rows, }) @@ -1984,8 +2011,8 @@ class WebInterface(object): Returns: json: - [["May 08, 2016 09:35:37", - "DEBUG", + [["May 08, 2016 09:35:37", + "DEBUG", "Auth: Came in with a super-token, authorization succeeded." ], [...], @@ -2028,20 +2055,20 @@ class WebInterface(object): "recordsTotal": 1039, "recordsFiltered": 163, "data": - [{"agent_id": 13, - "agent_name": "Telegram", - "body_text": "Game of Thrones - S06E01 - The Red Woman [Transcode].", - "id": 1000, - "notify_action": "play", - "poster_url": "http://i.imgur.com/ZSqS8Ri.jpg", - "rating_key": 153037, - "script_args": "[]", - "session_key": 147, - "subject_text": "PlexPy (Winterfell-Server)", - "timestamp": 1462253821, - "user": "DanyKhaleesi69", + [{"agent_id": 13, + "agent_name": "Telegram", + "body_text": "Game of Thrones - S06E01 - The Red Woman [Transcode].", + "id": 1000, + "notify_action": "play", + "poster_url": "http://i.imgur.com/ZSqS8Ri.jpg", + "rating_key": 153037, + "script_args": "[]", + "session_key": 147, + "subject_text": "PlexPy (Winterfell-Server)", + "timestamp": 1462253821, + "user": "DanyKhaleesi69", "user_id": 8008135 - }, + }, {...}, {...} ] @@ -2085,17 +2112,19 @@ class WebInterface(object): """ data_factory = datafactory.DataFactory() result = data_factory.delete_notification_log() + result = result if result else 'no data received' - if result: - return {'message': result} - else: - return {'message': 'no data received'} + return {'message': result} @cherrypy.expose @requireAuth(member_of("admin")) def clearLogs(self): - plexpy.LOG_LIST = [] - logger.info(u"Web logs cleared") + try: + open(os.path.join(plexpy.CONFIG.LOG_DIR, 'plexpy.log'), 'w').close() + except Exception as e: + logger.exception(u'Failed to delete plexpy.log %s' % e) + + logger.info(u'plexpy.log cleared') raise cherrypy.HTTPRedirect("logs") @cherrypy.expose @@ -2292,7 +2321,7 @@ class WebInterface(object): kwargs['http_hashed_password'] = 1 else: kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD - + elif kwargs['http_password'] and kwargs.get('http_hash_password'): kwargs['http_password'] = make_hash(kwargs['http_password']) kwargs['http_hashed_password'] = 1 @@ -2418,7 +2447,6 @@ class WebInterface(object): else: return {'message': 'Database backup failed.'} - @cherrypy.expose @requireAuth(member_of("admin")) def get_notification_agent_config(self, agent_id, **kwargs): @@ -2806,22 +2834,105 @@ class WebInterface(object): @cherrypy.expose @requireAuth() - def pms_image_proxy(self, img='', width='0', height='0', fallback=None, **kwargs): + def pms_image_proxy(self, img='', ratingkey=None, width='0', height='0', fallback=None, **kwargs): + """ Grabs the images from pms and saved them to disk """ - pms_connect = pmsconnect.PmsConnect() - result = pms_connect.get_image(img, width, height, fallback) + if not img and not ratingkey: + logger.debug('No image input received') + return - if result: - cherrypy.response.headers['Content-type'] = result[1] - return result[0] - else: - return None + if ratingkey and not img: + img = '/library/metadata/%s/thumb/1337' % ratingkey + + img_string = img.rsplit('/', 1)[0] + img_string += '%s%s' % (width, height) + fp = hashlib.md5(img_string).hexdigest() + fp += '.jpg' # we want to be able to preview the thumbs + c_dir = os.path.join(plexpy.CONFIG.CACHE_DIR, 'images') + ffp = os.path.join(c_dir, fp) + + if not os.path.exists(c_dir): + os.mkdir(c_dir) + + try: + if 'indexes' in img: + raise + return serve_file(path=ffp, content_type='image/jpeg') + + except NotFound: + # the image does not exist, download it from pms + try: + pms_connect = pmsconnect.PmsConnect() + result = pms_connect.get_image(img, width, height) + + if result and result[0]: + cherrypy.response.headers['Content-type'] = result[1] + if 'indexes' not in img: + with open(ffp, 'wb') as f: + f.write(result[0]) + + return result[0] + else: + raise + + except Exception as e: + logger.debug('Failed to get image %s file %s falling back to %s' % (img, fp, e)) + fbi = None + if fallback == 'poster': + fbi = common.DEFAULT_POSTER_THUMB + elif fallback == 'cover': + fbi = common.DEFAULT_COVER_THUMB + elif fallback == 'art': + fbi = common.DEFAULT_ART + + if fbi: + fp = os.path.join(plexpy.PROG_DIR, 'data', fbi) + return serve_file(path=fp, content_type='image/png') + + + @cherrypy.expose + @requireAuth(member_of("admin")) + @addtoapi() + def download_log(self): + try: + logger.logger.flush() + except: + pass + + return serve_download(os.path.join(plexpy.CONFIG.LOG_DIR, 'plexpy.log'), name='plexpy.log') + + @cherrypy.expose + @cherrypy.tools.json_out() + @requireAuth(member_of("admin")) + @addtoapi() + def delete_image_cache(self): + """ Deletes the image cache dir and recreates it """ + cache_dir = os.path.join(plexpy.CONFIG.CACHE_DIR, 'images') + result = 'success' + msg = 'Deleted your cache images' + try: + shutil.rmtree(cache_dir, ignore_errors=True) + except OSError as e: + result = 'error' + msg = 'Failed to delete %s %s' % (cache_dir, e) + logger.exception(msg) + return + + try: + os.makedirs(cache_dir) + except OSError as e: + result = 'error' + msg = 'Failed to make %s %s' % (cache_dir, e) + logger.exception(msg) + return + + return {'result': result, 'message': msg} @cherrypy.expose @cherrypy.tools.json_out() @requireAuth(member_of("admin")) def delete_poster_url(self, poster_url=''): - + if poster_url: data_factory = datafactory.DataFactory() result = data_factory.delete_poster_url(poster_url=poster_url) @@ -2854,7 +2965,7 @@ class WebInterface(object): Returns: json: - {"results_count": 69, + {"results_count": 69, "results_list": {"movie": [{...}, @@ -2898,8 +3009,8 @@ class WebInterface(object): return serve_template(templatename="info_search_results_list.html", data=None, title="Search Result List") - ##### Update Metadata ##### - + ##### Update Metadata ##### + @cherrypy.expose @requireAuth(member_of("admin")) def update_metadata(self, rating_key=None, query=None, update=False, **kwargs): @@ -3048,51 +3159,51 @@ class WebInterface(object): json: {"metadata": {"actors": [ - "Kit Harington", - "Emilia Clarke", - "Isaac Hempstead-Wright", - "Maisie Williams", - "Liam Cunningham", - ], - "added_at": "1461572396", - "art": "/library/metadata/1219/art/1462175063", - "content_rating": "TV-MA", + "Kit Harington", + "Emilia Clarke", + "Isaac Hempstead-Wright", + "Maisie Williams", + "Liam Cunningham", + ], + "added_at": "1461572396", + "art": "/library/metadata/1219/art/1462175063", + "content_rating": "TV-MA", "directors": [ "Jeremy Podeswa" - ], - "duration": "2998290", + ], + "duration": "2998290", "genres": [ - "Adventure", - "Drama", + "Adventure", + "Drama", "Fantasy" - ], - "grandparent_rating_key": "1219", - "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", - "grandparent_title": "Game of Thrones", - "guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en", - "labels": [], - "last_viewed_at": "1462165717", - "library_name": "TV Shows", - "media_index": "1", - "media_type": "episode", - "originally_available_at": "2016-04-24", - "parent_media_index": "6", - "parent_rating_key": "153036", - "parent_thumb": "/library/metadata/153036/thumb/1462175062", - "parent_title": "", - "rating": "7.8", - "rating_key": "153037", - "section_id": "2", - "studio": "HBO", - "summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.", - "tagline": "", - "thumb": "/library/metadata/153037/thumb/1462175060", - "title": "The Red Woman", - "updated_at": "1462175060", + ], + "grandparent_rating_key": "1219", + "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", + "grandparent_title": "Game of Thrones", + "guid": "com.plexapp.agents.thetvdb://121361/6/1?lang=en", + "labels": [], + "last_viewed_at": "1462165717", + "library_name": "TV Shows", + "media_index": "1", + "media_type": "episode", + "originally_available_at": "2016-04-24", + "parent_media_index": "6", + "parent_rating_key": "153036", + "parent_thumb": "/library/metadata/153036/thumb/1462175062", + "parent_title": "", + "rating": "7.8", + "rating_key": "153037", + "section_id": "2", + "studio": "HBO", + "summary": "Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.", + "tagline": "", + "thumb": "/library/metadata/153037/thumb/1462175060", + "title": "The Red Woman", + "updated_at": "1462175060", "writers": [ - "David Benioff", + "David Benioff", "D. B. Weiss" - ], + ], "year": "2016" } } @@ -3123,21 +3234,21 @@ class WebInterface(object): Returns: json: {"recently_added": - [{"added_at": "1461572396", - "grandparent_rating_key": "1219", - "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", - "grandparent_title": "Game of Thrones", - "library_name": "", - "media_index": "1", - "media_type": "episode", - "parent_media_index": "6", - "parent_rating_key": "153036", - "parent_thumb": "/library/metadata/153036/thumb/1462175062", - "parent_title": "", - "rating_key": "153037", - "section_id": "2", - "thumb": "/library/metadata/153037/thumb/1462175060", - "title": "The Red Woman", + [{"added_at": "1461572396", + "grandparent_rating_key": "1219", + "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", + "grandparent_title": "Game of Thrones", + "library_name": "", + "media_index": "1", + "media_type": "episode", + "parent_media_index": "6", + "parent_rating_key": "153036", + "parent_thumb": "/library/metadata/153036/thumb/1462175062", + "parent_title": "", + "rating_key": "153037", + "section_id": "2", + "thumb": "/library/metadata/153037/thumb/1462175060", + "title": "The Red Woman", "year": "2016" }, {...}, @@ -3327,60 +3438,60 @@ class WebInterface(object): json: {"stream_count": 3, "session": - [{"art": "/library/metadata/1219/art/1462175063", - "aspect_ratio": "1.78", - "audio_channels": "6", - "audio_codec": "ac3", - "audio_decision": "transcode", - "bif_thumb": "/library/parts/274169/indexes/sd/", - "bitrate": "10617", - "container": "mkv", - "content_rating": "TV-MA", - "duration": "2998290", - "friendly_name": "Mother of Dragons", - "grandparent_rating_key": "1219", - "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", - "grandparent_title": "Game of Thrones", - "height": "1078", - "indexes": 1, - "ip_address": "xxx.xxx.xxx.xxx", - "labels": [], - "machine_id": "83f189w617623ccs6a1lqpby", - "media_index": "1", - "media_type": "episode", - "parent_media_index": "6", - "parent_rating_key": "153036", - "parent_thumb": "/library/metadata/153036/thumb/1462175062", - "parent_title": "", - "platform": "Chrome", - "player": "Plex Web (Chrome)", - "progress_percent": "0", - "rating_key": "153037", - "section_id": "2", - "session_key": "291", - "state": "playing", - "throttled": "1", - "thumb": "/library/metadata/153037/thumb/1462175060", - "title": "The Red Woman", - "transcode_audio_channels": "2", - "transcode_audio_codec": "aac", - "transcode_container": "mkv", - "transcode_height": "1078", - "transcode_key": "tiv5p524wcupe8nxegc26s9k9", - "transcode_progress": 2, - "transcode_protocol": "http", - "transcode_speed": "0.0", - "transcode_video_codec": "h264", - "transcode_width": "1920", - "user": "DanyKhaleesi69", - "user_id": 8008135, - "user_thumb": "https://plex.tv/users/568gwwoib5t98a3a/avatar", - "video_codec": "h264", - "video_decision": "copy", - "video_framerate": "24p", - "video_resolution": "1080", - "view_offset": "", - "width": "1920", + [{"art": "/library/metadata/1219/art/1462175063", + "aspect_ratio": "1.78", + "audio_channels": "6", + "audio_codec": "ac3", + "audio_decision": "transcode", + "bif_thumb": "/library/parts/274169/indexes/sd/", + "bitrate": "10617", + "container": "mkv", + "content_rating": "TV-MA", + "duration": "2998290", + "friendly_name": "Mother of Dragons", + "grandparent_rating_key": "1219", + "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", + "grandparent_title": "Game of Thrones", + "height": "1078", + "indexes": 1, + "ip_address": "xxx.xxx.xxx.xxx", + "labels": [], + "machine_id": "83f189w617623ccs6a1lqpby", + "media_index": "1", + "media_type": "episode", + "parent_media_index": "6", + "parent_rating_key": "153036", + "parent_thumb": "/library/metadata/153036/thumb/1462175062", + "parent_title": "", + "platform": "Chrome", + "player": "Plex Web (Chrome)", + "progress_percent": "0", + "rating_key": "153037", + "section_id": "2", + "session_key": "291", + "state": "playing", + "throttled": "1", + "thumb": "/library/metadata/153037/thumb/1462175060", + "title": "The Red Woman", + "transcode_audio_channels": "2", + "transcode_audio_codec": "aac", + "transcode_container": "mkv", + "transcode_height": "1078", + "transcode_key": "tiv5p524wcupe8nxegc26s9k9", + "transcode_progress": 2, + "transcode_protocol": "http", + "transcode_speed": "0.0", + "transcode_video_codec": "h264", + "transcode_width": "1920", + "user": "DanyKhaleesi69", + "user_id": 8008135, + "user_thumb": "https://plex.tv/users/568gwwoib5t98a3a/avatar", + "video_codec": "h264", + "video_decision": "copy", + "video_framerate": "24p", + "video_resolution": "1080", + "view_offset": "", + "width": "1920", "year": "2016" }, {...}, @@ -3419,13 +3530,13 @@ class WebInterface(object): Returns: json: - [{"art": "/:/resources/show-fanart.jpg", - "child_count": "3745", - "count": "62", - "parent_count": "240", + [{"art": "/:/resources/show-fanart.jpg", + "child_count": "3745", + "count": "62", + "parent_count": "240", "section_id": "2", - "section_name": "TV Shows", - "section_type": "show", + "section_name": "TV Shows", + "section_type": "show", "thumb": "/:/resources/show.png" }, {...}, @@ -3457,17 +3568,17 @@ class WebInterface(object): Returns: json: - [{"email": "Jon.Snow.1337@CastleBlack.com", - "filter_all": "", - "filter_movies": "", - "filter_music": "", - "filter_photos": "", - "filter_tv": "", - "is_allow_sync": null, - "is_home_user": "1", - "is_restricted": "0", - "thumb": "https://plex.tv/users/k10w42309cynaopq/avatar", - "user_id": "328871", + [{"email": "Jon.Snow.1337@CastleBlack.com", + "filter_all": "", + "filter_movies": "", + "filter_music": "", + "filter_photos": "", + "filter_tv": "", + "is_allow_sync": null, + "is_home_user": "1", + "is_restricted": "0", + "thumb": "https://plex.tv/users/k10w42309cynaopq/avatar", + "user_id": "328871", "username": "Jon Snow" }, {...}, @@ -3499,26 +3610,26 @@ class WebInterface(object): Returns: json: - [{"content_type": "video", - "device_name": "Tyrion's iPad", - "failure": "", - "friendly_name": "Tyrion Lannister", - "item_complete_count": "0", - "item_count": "1", - "item_downloaded_count": "0", - "item_downloaded_percent_complete": 0, - "metadata_type": "movie", - "music_bitrate": "192", - "photo_quality": "74", - "platform": "iOS", - "rating_key": "154092", - "root_title": "Deadpool", - "state": "pending", - "sync_id": "11617019", - "title": "Deadpool", - "total_size": "0", - "user_id": "328871", - "username": "DrukenDwarfMan", + [{"content_type": "video", + "device_name": "Tyrion's iPad", + "failure": "", + "friendly_name": "Tyrion Lannister", + "item_complete_count": "0", + "item_count": "1", + "item_downloaded_count": "0", + "item_downloaded_percent_complete": 0, + "metadata_type": "movie", + "music_bitrate": "192", + "photo_quality": "74", + "platform": "iOS", + "rating_key": "154092", + "root_title": "Deadpool", + "state": "pending", + "sync_id": "11617019", + "title": "Deadpool", + "total_size": "0", + "user_id": "328871", + "username": "DrukenDwarfMan", "video_quality": "60" }, {...}, @@ -3576,22 +3687,22 @@ class WebInterface(object): {"stat_id": "top_tv", "stat_type": "total_plays", "rows": - [{"content_rating": "TV-MA", - "friendly_name": "", - "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", - "labels": [], - "last_play": 1462380698, - "media_type": "episode", - "platform": "", - "platform_type": "", - "rating_key": 1219, - "row_id": 1116, - "section_id": 2, - "thumb": "", - "title": "Game of Thrones", - "total_duration": 213302, - "total_plays": 69, - "user": "", + [{"content_rating": "TV-MA", + "friendly_name": "", + "grandparent_thumb": "/library/metadata/1219/thumb/1462175063", + "labels": [], + "last_play": 1462380698, + "media_type": "episode", + "platform": "", + "platform_type": "", + "rating_key": 1219, + "row_id": 1116, + "section_id": 2, + "thumb": "", + "title": "Game of Thrones", + "total_duration": 213302, + "total_plays": 69, + "user": "", "users_watched": "" }, {...}, @@ -3699,4 +3810,4 @@ class WebInterface(object): def check_pms_updater(self): pms_connect = pmsconnect.PmsConnect() result = pms_connect.get_update_staus() - return result \ No newline at end of file + return result diff --git a/plexpy/webstart.py b/plexpy/webstart.py index 0ffc6a8a..fc28d5ca 100644 --- a/plexpy/webstart.py +++ b/plexpy/webstart.py @@ -75,7 +75,7 @@ def initialize(options): plexpy.HTTP_ROOT = options['http_root'] = '/' else: plexpy.HTTP_ROOT = options['http_root'] = '/' + options['http_root'].strip('/') + '/' - + cherrypy.config.update(options_dict) conf = { @@ -178,6 +178,17 @@ def initialize(options): 'tools.auth.on': False, 'tools.sessions.on': False }, + '/pms_image_proxy': { + 'tools.staticdir.on': True, + 'tools.staticdir.dir': os.path.join(plexpy.CONFIG.CACHE_DIR, 'images'), + 'tools.caching.on': True, + 'tools.caching.force': True, + 'tools.caching.delay': 0, + 'tools.expires.on': True, + 'tools.expires.secs': 60 * 60 * 24 * 30, # 30 days + 'tools.auth.on': False, + 'tools.sessions.on': False + }, '/favicon.ico': { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.abspath(os.path.join(plexpy.PROG_DIR, 'data/interfaces/default/images/favicon.ico')),