diff --git a/plexpy/__init__.py b/plexpy/__init__.py index b82818a3..2ab7c792 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -438,7 +438,8 @@ def dbcheck(): 'parent_media_index INTEGER, section_id 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)' + 'duration INTEGER DEFAULT 0, guid TEXT, directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT, ' + 'labels TEXT)' ) # users table :: This table keeps record of the friends list @@ -692,6 +693,15 @@ def dbcheck(): 'ALTER TABLE session_history_metadata ADD COLUMN section_id INTEGER' ) + # Upgrade session_history_metadata table from earlier versions + try: + c_db.execute('SELECT labels FROM session_history_metadata') + except sqlite3.OperationalError: + logger.debug(u"Altering database. Updating database table session_history_metadata.") + c_db.execute( + 'ALTER TABLE session_history_metadata ADD COLUMN labels TEXT' + ) + # Upgrade session_history_media_info table from earlier versions try: c_db.execute('SELECT transcode_decision FROM session_history_media_info') diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py index 21bd92f9..8691762b 100644 --- a/plexpy/activity_processor.py +++ b/plexpy/activity_processor.py @@ -278,6 +278,7 @@ class ActivityProcessor(object): writers = ";".join(metadata['writers']) actors = ";".join(metadata['actors']) genres = ";".join(metadata['genres']) + labels = ";".join(metadata['labels']) # Build media item title if session['media_type'] == 'episode' or session['media_type'] == 'track': @@ -292,9 +293,9 @@ class ActivityProcessor(object): 'grandparent_rating_key, title, parent_title, grandparent_title, full_title, media_index, ' \ 'parent_media_index, section_id, 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) VALUES ' \ - '(last_insert_rowid(), ' \ - '?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' + 'summary, tagline, rating, duration, guid, directors, writers, actors, genres, studio, labels) ' \ + 'VALUES (last_insert_rowid(), ' \ + '?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' args = [session['rating_key'], session['parent_rating_key'], session['grandparent_rating_key'], session['title'], session['parent_title'], session['grandparent_title'], full_title, @@ -303,7 +304,7 @@ class ActivityProcessor(object): metadata['year'], metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'], metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['tagline'], metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres, - metadata['studio']] + metadata['studio'], labels] # logger.debug(u"PlexPy ActivityProcessor :: Writing session_history_metadata transaction...") self.db.action(query=query, args=args) diff --git a/plexpy/config.py b/plexpy/config.py index 49ca6f67..0b0adf15 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -418,6 +418,7 @@ _CONFIG_DEFINITIONS = { 'TWITTER_ON_PMSUPDATE': (int, 'Twitter', 0), 'UPDATE_DB_INTERVAL': (int, 'General', 24), 'UPDATE_SECTION_IDS': (int, 'General', 1), + 'UPDATE_LABELS': (int, 'General', 1), 'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1), 'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1), 'XBMC_ENABLED': (int, 'XBMC', 0), diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index dce76ce6..1e979cd2 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -1155,6 +1155,7 @@ class DataFactory(object): writers = ";".join(metadata['writers']) actors = ";".join(metadata['actors']) genres = ";".join(metadata['genres']) + labels = ";".join(metadata['labels']) #logger.info(u"PlexPy DataFactory :: Updating metadata in the database for rating key: %s." % new_rating_key) monitor_db = database.MonitorDatabase() diff --git a/plexpy/libraries.py b/plexpy/libraries.py index 785767f8..24d9bd7a 100644 --- a/plexpy/libraries.py +++ b/plexpy/libraries.py @@ -18,18 +18,9 @@ from plexpy import logger, datatables, common, database, helpers, session def update_section_ids(): from plexpy import pmsconnect, activity_pinger - #import threading plexpy.CONFIG.UPDATE_SECTION_IDS = -1 - #logger.debug(u"PlexPy Libraries :: Disabling monitoring while update in progress.") - #plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions', - # hours=0, minutes=0, seconds=0) - #plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items', - # hours=0, minutes=0, seconds=0) - #plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response', - # hours=0, minutes=0, seconds=0) - monitor_db = database.MonitorDatabase() try: @@ -44,9 +35,6 @@ def update_section_ids(): logger.warn(u"PlexPy Libraries :: Unable to update section_id's in database.") plexpy.CONFIG.UPDATE_SECTION_IDS = 1 plexpy.CONFIG.write() - - #logger.debug(u"PlexPy Libraries :: Re-enabling monitoring.") - #plexpy.initialize_scheduler() return None if not history_results: @@ -54,13 +42,7 @@ def update_section_ids(): plexpy.CONFIG.write() return None - logger.info(u"PlexPy Libraries :: Updating section_id's in database.") - - # Add thread filter to the logger - #logger.debug(u"PlexPy Libraries :: Disabling logging in the current thread while update in progress.") - #thread_filter = logger.NoThreadFilter(threading.current_thread().name) - #for handler in logger.logger.handlers: - # handler.addFilter(thread_filter) + logger.debug(u"PlexPy Libraries :: Updating section_id's in database.") # Get rating_key: section_id mapping pairs key_mappings = {} @@ -94,11 +76,6 @@ def update_section_ids(): else: error_keys.add(item['rating_key']) - # Remove thread filter from the logger - #for handler in logger.logger.handlers: - # handler.removeFilter(thread_filter) - #logger.debug(u"PlexPy Libraries :: Re-enabling logging in the current thread.") - if error_keys: logger.info(u"PlexPy Libraries :: Updated all section_id's in database except for rating_keys: %s." % ', '.join(str(key) for key in error_keys)) @@ -108,11 +85,87 @@ def update_section_ids(): plexpy.CONFIG.UPDATE_SECTION_IDS = 0 plexpy.CONFIG.write() - #logger.debug(u"PlexPy Libraries :: Re-enabling monitoring.") - #plexpy.initialize_scheduler() + return True + +def update_labels(): + from plexpy import pmsconnect + + plexpy.CONFIG.UPDATE_LABELS = -1 + + monitor_db = database.MonitorDatabase() + + try: + query = 'SELECT section_id, section_type FROM library_sections' + library_results = monitor_db.select(query=query) + except Exception as e: + logger.warn(u"PlexPy Libraries :: Unable to execute database query for update_labels: %s." % e) + + logger.warn(u"PlexPy Libraries :: Unable to update labels in database.") + plexpy.CONFIG.UPDATE_LABELS = 1 + plexpy.CONFIG.write() + return None + + if not library_results: + plexpy.CONFIG.UPDATE_LABELS = 0 + plexpy.CONFIG.write() + return None + + logger.debug(u"PlexPy Libraries :: Updating labels in database.") + + # Get rating_key: section_id mapping pairs + key_mappings = {} + + pms_connect = pmsconnect.PmsConnect() + for library in library_results: + section_id = library['section_id'] + section_type = library['section_type'] + + if section_type != 'photo': + library_children = [] + library_labels = pms_connect.get_library_label_details(section_id=section_id) + + if library_labels: + for label in library_labels: + library_children = pms_connect.get_library_children_details(section_id=section_id, + section_type=section_type, + label_key=label['label_key']) + + if library_children: + children_list = library_children['childern_list'] + rating_key_list = [child['rating_key'] for child in children_list] + + for rating_key in [child['rating_key'] for child in children_list]: + if key_mappings.get(rating_key): + key_mappings[rating_key].append(label['label_title']) + else: + key_mappings[rating_key] = [label['label_title']] + + else: + logger.warn(u"PlexPy Libraries :: Unable to get a list of library items for section_id %s." + % section_id) + + error_keys = set() + for rating_key, labels in key_mappings.iteritems(): + try: + labels = ';'.join(labels) + monitor_db.action('UPDATE session_history_metadata SET labels = ? ' + 'WHERE rating_key = ? OR parent_rating_key = ? OR grandparent_rating_key = ? ', + args=[labels, rating_key, rating_key, rating_key]) + except: + error_keys.add(rating_key) + + if error_keys: + logger.info(u"PlexPy Libraries :: Updated all labels in database except for rating_keys: %s." % + ', '.join(str(key) for key in error_keys)) + else: + logger.info(u"PlexPy Libraries :: Updated all labels in database.") + + plexpy.CONFIG.UPDATE_LABELS = 0 + plexpy.CONFIG.write() return True + class Libraries(object): def __init__(self): diff --git a/plexpy/plexwatch_import.py b/plexpy/plexwatch_import.py index e9d6b13e..2ddbe673 100644 --- a/plexpy/plexwatch_import.py +++ b/plexpy/plexwatch_import.py @@ -148,6 +148,12 @@ def extract_plexwatch_xml(xml=None): for i in genre_elem: genres.append(helpers.get_xml_attr(i, 'tag')) + labels = [] + if a.getElementsByTagName('Lables'): + label_elem = a.getElementsByTagName('Lables') + for i in label_elem: + labels.append(helpers.get_xml_attr(i, 'tag')) + output = {'added_at': added_at, 'art': art, 'duration': duration, @@ -196,7 +202,8 @@ def extract_plexwatch_xml(xml=None): 'writers': writers, 'actors': actors, 'genres': genres, - 'studio': studio + 'studio': studio, + 'labels': labels } return output @@ -380,6 +387,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval 'actors': extracted_xml['actors'], 'genres': extracted_xml['genres'], 'studio': extracted_xml['studio'], + 'labels': extracted_xml['labels'], 'full_title': row['full_title'] } diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index b25705f7..7ab65a84 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -84,6 +84,13 @@ def refresh_libraries(): # Start library section_id update on it's own thread threading.Thread(target=libraries.update_section_ids).start() + if plexpy.CONFIG.UPDATE_LABELS == 1 or plexpy.CONFIG.UPDATE_LABELS == -1: + from plexpy import libraries + import threading + + # Start library labels update on it's own thread + threading.Thread(target=libraries.update_labels).start() + logger.info(u"PlexPy Pmsconnect :: Libraries list refreshed.") else: logger.warn(u"PlexPy Pmsconnect :: Unable to refresh libraries list.") @@ -105,7 +112,16 @@ class PmsConnect(object): port = plexpy.CONFIG.PMS_PORT self.protocol = 'http' - token = token if token else plexpy.CONFIG.PMS_TOKEN + if token == 'admin': + token = plexpy.CONFIG.PMS_TOKEN + elif not token: + # Check if we should use the admin token, or the guest server token + if session.get_session_user_id(): + user_data = users.Users() + user_tokens = user_data.get_tokens(user_id=session.get_session_user_id()) + token = user_tokens['server_token'] + else: + token = plexpy.CONFIG.PMS_TOKEN self.request_handler = http_handler.HTTPHandler(host=hostname, port=port, @@ -293,7 +309,7 @@ class PmsConnect(object): return request - def get_library_list(self, section_id='', list_type='all', count='0', sort_type='', output_format=''): + def get_library_list(self, section_id='', list_type='all', count='0', sort_type='', label_key='', output_format=''): """ Return list of items in library on server. @@ -302,8 +318,25 @@ class PmsConnect(object): Output: array """ count = '&X-Plex-Container-Size=' + count if count else '' + label_key = '&label=' + label_key if label_key else '' - uri = '/library/sections/' + section_id + '/' + list_type + '?X-Plex-Container-Start=0' + count + sort_type + uri = '/library/sections/' + section_id + '/' + list_type + '?X-Plex-Container-Start=0' + count + sort_type + label_key + request = self.request_handler.make_request(uri=uri, + proto=self.protocol, + request_type='GET', + output_format=output_format) + + return request + + def get_library_labels(self, section_id='', output_format=''): + """ + Return list of labels for a library on server. + + Optional parameters: output_format { dict, json } + + Output: array + """ + uri = '/library/sections/' + section_id + '/label' request = self.request_handler.make_request(uri=uri, proto=self.protocol, request_type='GET', @@ -539,26 +572,31 @@ class PmsConnect(object): section_id = helpers.get_xml_attr(a, 'librarySectionID') library_name = helpers.get_xml_attr(a, 'librarySectionTitle') - genres = [] - actors = [] - writers = [] directors = [] + writers = [] + actors = [] + genres = [] + labels = [] - if metadata_main.getElementsByTagName('Genre'): - for genre in metadata_main.getElementsByTagName('Genre'): - genres.append(helpers.get_xml_attr(genre, 'tag')) - - if metadata_main.getElementsByTagName('Role'): - for actor in metadata_main.getElementsByTagName('Role'): - actors.append(helpers.get_xml_attr(actor, 'tag')) + if metadata_main.getElementsByTagName('Director'): + for director in metadata_main.getElementsByTagName('Director'): + directors.append(helpers.get_xml_attr(director, 'tag')) if metadata_main.getElementsByTagName('Writer'): for writer in metadata_main.getElementsByTagName('Writer'): writers.append(helpers.get_xml_attr(writer, 'tag')) - if metadata_main.getElementsByTagName('Director'): - for director in metadata_main.getElementsByTagName('Director'): - directors.append(helpers.get_xml_attr(director, 'tag')) + if metadata_main.getElementsByTagName('Role'): + for actor in metadata_main.getElementsByTagName('Role'): + actors.append(helpers.get_xml_attr(actor, 'tag')) + + if metadata_main.getElementsByTagName('Genre'): + for genre in metadata_main.getElementsByTagName('Genre'): + genres.append(helpers.get_xml_attr(genre, 'tag')) + + if metadata_main.getElementsByTagName('Label'): + for labels in metadata_main.getElementsByTagName('Label'): + labels.append(helpers.get_xml_attr(labels, 'tag')) if metadata_type == 'movie': metadata = {'media_type': metadata_type, @@ -588,10 +626,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'genres': genres, - 'actors': actors, + 'directors': directors, 'writers': writers, - 'directors': directors + 'actors': actors, + 'genres': genres, + 'labels': labels } metadata_list = {'metadata': metadata} @@ -623,10 +662,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'writers': writers, 'directors': directors, + 'writers': writers, + 'actors': actors, 'genres': genres, - 'actors': actors + 'labels': labels } metadata_list = {'metadata': metadata} @@ -660,10 +700,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'genres': show_details['metadata']['genres'], - 'actors': show_details['metadata']['actors'], + 'directors': show_details['metadata']['directors'], 'writers': show_details['metadata']['writers'], - 'directors': show_details['metadata']['directors'] + 'actors': show_details['metadata']['actors'], + 'genres': show_details['metadata']['genres'], + 'labels': show_details['metadata']['labels'] } metadata_list = {'metadata': metadata} @@ -697,10 +738,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'genres': show_details['metadata']['genres'], - 'actors': show_details['metadata']['actors'], + 'directors': directors, 'writers': writers, - 'directors': directors + 'actors': show_details['metadata']['actors'], + 'genres': show_details['metadata']['genres'], + 'labels': show_details['metadata']['labels'] } metadata_list = {'metadata': metadata} @@ -732,10 +774,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'writers': writers, 'directors': directors, + 'writers': writers, + 'actors': actors, 'genres': genres, - 'actors': actors + 'labels': labels } metadata_list = {'metadata': metadata} @@ -769,10 +812,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'genres': genres, - 'actors': actors, + 'directors': directors, 'writers': writers, - 'directors': directors + 'actors': actors, + 'genres': genres, + 'labels': labels } metadata_list = {'metadata': metadata} @@ -806,10 +850,11 @@ class PmsConnect(object): 'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'), 'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'), 'guid': helpers.get_xml_attr(metadata_main, 'guid'), - 'genres': album_details['metadata']['genres'], - 'actors': actors, + 'directors': directors, 'writers': writers, - 'directors': directors + 'actors': actors, + 'genres': album_details['metadata']['genres'], + 'labels': album_details['metadata']['labels'] } metadata_list = {'metadata': metadata} @@ -1611,7 +1656,8 @@ class PmsConnect(object): return output - def get_library_children_details(self, section_id='', section_type='', list_type='all', count='', rating_key='', get_media_info=False): + def get_library_children_details(self, section_id='', section_type='', list_type='all', count='', + rating_key='', label_key='', get_media_info=False): """ Return processed and validated server library items list. @@ -1645,7 +1691,7 @@ class PmsConnect(object): sort_type = '' if str(section_id).isdigit(): - library_data = self.get_library_list(str(section_id), list_type, count, sort_type, output_format='xml') + library_data = self.get_library_list(str(section_id), list_type, count, sort_type, label_key, output_format='xml') elif str(rating_key).isdigit(): library_data = self.get_children_list(str(rating_key), output_format='xml') else: @@ -1792,6 +1838,33 @@ class PmsConnect(object): return server_library_stats + def get_library_label_details(self, section_id=''): + labels_data = self.get_library_labels(section_id=str(section_id), output_format='xml') + + try: + xml_head = labels_data.getElementsByTagName('MediaContainer') + except Exception as e: + logger.warn(u"PlexPy Pmsconnect :: Unable to parse XML for get_library_label_details: %s." % e) + return None + + labels_list = [] + + for a in xml_head: + if a.getAttribute('size'): + if a.getAttribute('size') == '0': + logger.debug(u"PlexPy Pmsconnect :: No labels data.") + return labels_list + + if a.getElementsByTagName('Directory'): + labels_main = a.getElementsByTagName('Directory') + for item in labels_main: + label = {'label_key': helpers.get_xml_attr(item, 'key'), + 'label_title': helpers.get_xml_attr(item, 'title') + } + labels_list.append(label) + + return labels_list + def get_image(self, img=None, width=None, height=None): """ Return image data as array.