def test_tvdb(): assert Identifier.get_ids( Guid.parse('com.plexapp.agents.thetvdb://123456')) == { 'tvdb': 123456 } assert Identifier.get_ids( Guid.parse('com.plexapp.agents.thetvdb://123456')) == { 'tvdb': 123456 }
def test_invalid_format(): guids = ['com.plexapp.unsupported://', '://12345', None] for item in guids: r = Guid.parse(item) assert r.valid is False
def create_session(self, session_key, state): """ :type session_key: str :type state: str :rtype: WatchSession or None """ log.debug('Creating a WatchSession for the current media') item = Plex['status'].sessions().get(session_key) if not item: log.warn('Unable to find session with key "%s"', session_key) return None # Metadata metadata = Metadata.get(item.rating_key) # Guid guid = Guid.parse(metadata.guid) if metadata else None # Create WatchSession ws = WatchSession.from_session(item.session, metadata, guid, item.rating_key, state) ws.skip = not metadata # Fetch client by `machineIdentifier` ws.client = Plex.clients().get(item.session.player.machine_identifier) ws.save() log.debug('created session: %s', ws) return ws
def set_identifier(cls, data, guid, strict=True): if not guid: return None if type(guid) is str: # Parse raw guid guid = Guid.parse(guid) if 'ids' not in data: data['ids'] = {} ids = data['ids'] if guid.agent == 'imdb': ids['imdb'] = guid.sid elif guid.agent == 'tmdb': ids['tmdb'] = try_convert(guid.sid, int) elif guid.agent == 'tvdb': ids['tvdb'] = try_convert(guid.sid, int) elif not strict: log.info('Unknown Guid agent: "%s"', guid.agent) else: log.info('Unknown Guid agent: "%s" [strict]', guid.agent) return None return data
def create_session(self, info): if not info.get('ratingKey'): log.warn('Invalid ratingKey provided from activity info') return None # Metadata metadata = Metadata.get(info['ratingKey']) # Guid guid = Guid.parse(metadata.guid) if metadata else None ws = WatchSession.from_info(info, metadata, guid, info['ratingKey']) ws.skip = not metadata # Fetch client by `machineIdentifier` ws.client = Plex.clients().get(info['machineIdentifier']) if not ws.client: # Create dummy client from `info` ws.client = Client(Plex.client, 'clients') ws.client.name = info.get('client', None) ws.client.machine_identifier = info.get('machineIdentifier', None) ws.client.address = info.get('address', None) ws.client.port = info.get('port', None) # Create dummy user from `info` ws.user = User(Plex.client, 'accounts') ws.user.id = info['user_id'] ws.user.title = info['user_name'] ws.save() log.debug('created session: %s', ws) return ws
def build_request(cls, session, part=None, rating_key=None, view_offset=None): # Retrieve metadata for session if part is None: part = session.part if rating_key is None: rating_key = session.rating_key # Retrieve metadata metadata = Metadata.get(rating_key) # Validate metadata if not metadata: log.warn('Unable to retrieve metadata for rating_key %r', rating_key) return None if metadata.type not in ['movie', 'episode']: log.info('Ignoring session with type %r for rating_key %r', metadata.type, rating_key) return None # Apply library/section filter if not Filters.is_valid_metadata_section(metadata): log.info('Ignoring session in filtered section: %r', metadata.section.title) return None # Parse guid guid = Guid.parse(metadata.guid, strict=True) if not guid or not guid.valid: log_unsupported_guid(log, guid) return None # Build request from guid/metadata if type(metadata) is Movie: result = cls.build_movie(metadata, guid, part) elif type(metadata) is Episode: result = cls.build_episode(metadata, guid, part) else: log.warn('Unknown metadata type: %r', type(metadata)) return None if not result: log.info('Unable to build request for session: %r', session) return None # Retrieve media progress if view_offset is not None: # Calculate progress from `view_offset` parameter progress = UpdateSession.get_progress( metadata.duration, view_offset, part, session.part_count, session.part_duration ) else: # Use session progress progress = session.progress # Merge progress into request return merge(result, { 'progress': progress })
def test_unsupported_episode(): guids = ['com.plexapp.unsupported://12345/3/2?lang=en'] for item in guids: r = Guid.parse(item) assert r.service == 'unsupported'
def test_no_match_episode(): guids = ['com.plexapp.basic://12345/3/2?lang=en'] for item in guids: r = Guid.parse(item, match=False) assert r.service == 'basic' assert r.id == '12345'
def test_youtube_movie(): guids = ['com.plexapp.agents.youtube://7Pq-S557XQU?lang=xn'] for item in guids: r = Guid.parse(item, strict=True) assert r is not None assert r.service == 'youtube'
def test_invalid_show(): guids = ['com.plexapp.agents.thetvdb://12345'] for item in guids: r = Guid.parse(item, media='movie') assert r.service == 'thetvdb' assert r.id == '12345'
def finish(self): if self.current.kwargs.get('section'): # Collection cleaning disabled for individual syncs return # Increment progress steps self.current.progress.group(Movies, 'missing:movies').add(len(self.current.pending['movies'].keys)) # Iterate over movies for pk in list(self.current.pending['movies'].keys): # Increment one step self.current.progress.group(Movies, 'missing:movies').step() # Iterate over data handlers triggered = False for data in self.get_data(SyncMedia.Movies): if data not in [SyncData.Collection]: continue # Retrieve movie t_movie = self.trakt[(SyncMedia.Movies, data)].get(pk) if not t_movie: continue log.debug('Found movie missing from plex: %r [data: %r]', pk, SyncData.title(data)) # Trigger handler self.execute_handlers( self.mode, SyncMedia.Movies, data, key=None, guid=Guid.construct(*pk, matched=True), p_item=None, t_item=t_movie ) # Mark triggered triggered = True # Check if action was triggered if not triggered: continue # Remove movie from `pending` set self.current.pending['movies'].keys.remove(pk) # Stop progress group self.current.progress.group(Movies, 'missing:movies').stop() # Report pending movies (no actions triggered) self.log_pending( log, 'Unable to find %d movie(s) in Plex, list has been saved to: %s', self.current.account, 'movies', self.current.pending['movies'].keys )
def test_strict_movie(): guids = [ 'com.plexapp.agents.unsupported://c5a059f2ba654c580cd8fe322379c7fb5b62c370?lang=xn' ] for item in guids: r = Guid.parse(item, media='movie', strict=True) assert r.valid is False
def test_unsupported_episode(): guids = [ 'com.plexapp.unsupported://12345/3/2?lang=en' ] for item in guids: r = Guid.parse(item) assert r.service == 'unsupported'
def get_item(key): metadata = Metadata.get(key) print "metadata:", metadata if type(metadata) is Episode: guid = Guid.parse(metadata.guid) print "guid:", guid identifier = Matcher.process(metadata) print "identifier:", identifier
def test_youtube_movie(): guids = [ 'com.plexapp.agents.youtube://7Pq-S557XQU?lang=xn' ] for item in guids: r = Guid.parse(item, strict=True) assert r is not None assert r.service == 'youtube'
def test_invalid_show(): guids = [ 'com.plexapp.agents.thetvdb://12345' ] for item in guids: r = Guid.parse(item, media='movie') assert r.service == 'thetvdb' assert r.id == '12345'
def test_no_match_episode(): guids = [ 'com.plexapp.basic://12345/3/2?lang=en' ] for item in guids: r = Guid.parse(item, match=False) assert r.service == 'basic' assert r.id == '12345'
def test_kinopoisk_movie(): guids = ['com.plexapp.agents.kinopoiskru://1234?lang=ru'] for item in guids: r = Guid.parse(item) assert r.service == 'kinopoisk' assert r.id == 1234 assert r.language == 'ru'
class UpdateSession(Update, Base): @staticmethod def get_account(result): # Try retrieve account from client client = result.get('client') try: client_account_id = client.account_id if client else None except KeyError: client_account_id = None if client_account_id: # Valid account found return client_account_id # Try retrieve account from user user = result.get('user') try: user_account_id = user.account_id if user else None except KeyError: user_account_id = None if user_account_id: # Valid account found return user_account_id return None @staticmethod def get_metadata(rating_key): # Retrieve metadata for `rating_key` try: metadata = Metadata.get(rating_key) except NotImplementedError, e: log.debug('%r, ignoring session', e.message) return None, None # Ensure metadata was returned if not metadata: return None, None # Queue flush for metadata cache Metadata.cache.flush_queue() # Validate metadata if metadata.type not in ['movie', 'episode']: log.info('Ignoring metadata with type %r for rating_key %r', metadata.type, rating_key) return metadata, None # Parse guid guid = Guid.parse(metadata.guid) return metadata, guid
def test_imdb(): guids = [ 'com.plexapp.agents.imdb://tt12345', 'com.plexapp.agents.xbmcnfotv://tt12345' ] for item in guids: r = Guid.parse(item) assert r.agent == 'imdb' assert r.sid == 'tt12345'
def test_tmdb(): guids = [ 'com.plexapp.agents.standalone://12345', 'com.plexapp.agents.themoviedb://12345' ] for item in guids: r = Guid.parse(item) assert r.agent == 'tmdb' assert r.sid == '12345'
def test_invalid_format(): guids = [ 'com.plexapp.unsupported://', '://12345', None ] for item in guids: r = Guid.parse(item) assert r.valid is False
def process_missing_shows(self): if self.current.kwargs.get('section'): # Collection cleaning disabled for individual syncs return # Increment progress steps self.current.progress.group(Shows, 'missing:shows').add(len(self.p_shows_pending)) # Iterate over trakt shows (that aren't in plex) for pk in list(self.p_shows_pending): # Increment one step self.current.progress.group(Shows, 'missing:shows').step() # Iterate over data handlers triggered = False for data in self.get_data(SyncMedia.Shows): if data not in [SyncData.Collection]: continue # Retrieve show t_show = self.trakt[(SyncMedia.Shows, data)].get(pk) if not t_show: continue log.debug('Found show missing from plex: %r [data: %r]', pk, SyncData.title(data)) # Trigger handler self.execute_handlers( SyncMedia.Shows, data, key=None, guid=Guid.construct(*pk), p_item=None, t_item=t_show ) # Mark triggered triggered = True # Check if action was triggered if not triggered: continue # Remove movie from `pending` set self.p_shows_pending.remove(pk) # Stop progress group self.current.progress.group(Shows, 'missing:shows').stop() self.log_pending('Unable to find %d show(s) in Plex\n%s', self.p_shows_pending)
def process_missing_shows(self): if self.current.kwargs.get('section'): # Collection cleaning disabled for individual syncs return # Increment progress steps self.current.progress.group(Shows, 'missing:shows').add( len(self.p_shows_pending)) # Iterate over trakt shows (that aren't in plex) for pk in list(self.p_shows_pending): # Increment one step self.current.progress.group(Shows, 'missing:shows').step() # Iterate over data handlers triggered = False for data in self.get_data(SyncMedia.Shows): if data not in [SyncData.Collection]: continue # Retrieve show t_show = self.trakt[(SyncMedia.Shows, data)].get(pk) if not t_show: continue log.debug('Found show missing from plex: %r [data: %r]', pk, SyncData.title(data)) # Trigger handler self.execute_handlers(SyncMedia.Shows, data, key=None, guid=Guid.construct(*pk), p_item=None, t_item=t_show) # Mark triggered triggered = True # Check if action was triggered if not triggered: continue # Remove movie from `pending` set self.p_shows_pending.remove(pk) # Stop progress group self.current.progress.group(Shows, 'missing:shows').stop() self.log_pending('Unable to find %d show(s) in Plex\n%s', self.p_shows_pending)
def test_none_movie(): guids = [ 'com.plexapp.agents.none://c5a059f2ba654c580cd8fe322379c7fb5b62c370?lang=xn' ] for item in guids: r = Guid.parse(item, media='movie', strict=True) assert r is not None assert r.service == 'none' assert r.id == 'c5a059f2ba654c580cd8fe322379c7fb5b62c370'
def test_kinopoisk_movie(): guids = [ 'com.plexapp.agents.kinopoiskru://1234?lang=ru' ] for item in guids: r = Guid.parse(item) assert r.service == 'kinopoisk' assert r.id == 1234 assert r.language == 'ru'
def test_tmdb_movie(): guids = [ 'com.plexapp.agents.standalone://12345', 'com.plexapp.agents.themoviedb://12345' ] for item in guids: r = Guid.parse(item) assert r.service == 'tmdb' assert r.id == 12345 assert r.language is None
def test_kinopoisk_episode(): guids = ['com.plexapp.agents.kinopoiskrushow://1234/1/71?lang=ru'] for item in guids: r = Guid.parse(item) assert r.service == 'kinopoisk' assert r.id == 1234 assert r.season == 1 assert r.episode == 71 assert r.language == 'ru'
def test_tvdb_show(): guids = ['com.plexapp.agents.thetvdb://12345?lang=en'] for item in guids: r = Guid.parse(item) assert r.service == 'tvdb' assert r.id == 12345 assert r.season is None assert r.episode is None assert r.language == 'en'
def test_tvdb_show(): guids = [ 'com.plexapp.agents.thetvdb://12345?lang=en' ] for item in guids: r = Guid.parse(item) assert r.agent == 'tvdb' assert r.sid == '12345' assert r.season is None assert r.episode is None
def test_imdb_episode(): guids = ['com.plexapp.agents.hama://imdb-12345/1/71?lang=en'] for item in guids: r = Guid.parse(item) assert r.service == 'imdb' assert r.id == '12345' assert r.season == 1 assert r.episode == 71 assert r.language == 'en'
def test_imdb_movie(): guids = [ 'com.plexapp.agents.imdb://tt12345', 'com.plexapp.agents.xbmcnfotv://tt12345' ] for item in guids: r = Guid.parse(item) assert r.service == 'imdb' assert r.id == 'tt12345' assert r.language is None
def build_request(cls, session, rating_key=None, view_offset=None): # Retrieve metadata for session if rating_key is None: rating_key = session.rating_key # Retrieve metadata metadata = Metadata.get(rating_key) # Queue a flush for the metadata cache Metadata.cache.flush_queue() # Validate metadata if not metadata: log.warn('Unable to retrieve metadata for rating_key %r', rating_key) return None if metadata.type not in ['movie', 'episode']: log.info('Ignoring session with type %r for rating_key %r', metadata.type, rating_key) return None # Apply library/section filter if not Filters.is_valid_metadata_section(metadata): return None # Parse guid guid = Guid.parse(metadata.guid) # Build request from guid/metadata if type(metadata) is Movie: result = cls.build_movie(metadata, guid) elif type(metadata) is Episode: result = cls.build_episode(metadata, guid) else: return None if not result: return None # Retrieve media progress if view_offset is not None: # Calculate progress from `view_offset` parameter progress = UpdateSession.get_progress(metadata.duration, view_offset) else: # Use session progress progress = session.progress # Merge progress into request return merge(result, {'progress': progress})
def build_request(cls, session, rating_key=None, view_offset=None): # Retrieve metadata for session if rating_key is None: rating_key = session.rating_key # Retrieve metadata metadata = Metadata.get(rating_key) # Queue a flush for the metadata cache Metadata.cache.flush_queue() # Validate metadata if not metadata: log.warn('Unable to retrieve metadata for rating_key %r', rating_key) return None if metadata.type not in ['movie', 'episode']: log.info('Ignoring session with type %r for rating_key %r', metadata.type, rating_key) return None # Apply library/section filter if not Filters.is_valid_metadata_section(metadata): return None # Parse guid guid = Guid.parse(metadata.guid) # Build request from guid/metadata if type(metadata) is Movie: result = cls.build_movie(metadata, guid) elif type(metadata) is Episode: result = cls.build_episode(metadata, guid) else: return None if not result: return None # Retrieve media progress if view_offset is not None: # Calculate progress from `view_offset` parameter progress = UpdateSession.get_progress(metadata.duration, view_offset) else: # Use session progress progress = session.progress # Merge progress into request return merge(result, { 'progress': progress })
def map_movie(self, guid, movie, progress=None, part=None, resolve_mappings=True): # Ensure guid has been parsed if type(guid) is str: guid = Guid.parse(guid, strict=True) # Ensure parsed guid is valid if not guid or not isinstance(guid, Guid) or not guid.valid: return False, None # Try match movie against database return self.map( guid.service, guid.id, resolve_mappings=resolve_mappings )
def test_kinopoisk_episode(): guids = [ 'com.plexapp.agents.kinopoiskrushow://1234/1/71?lang=ru' ] for item in guids: r = Guid.parse(item) assert r.service == 'kinopoisk' assert r.id == 1234 assert r.season == 1 assert r.episode == 71 assert r.language == 'ru'
def test_imdb_episode(): guids = [ 'com.plexapp.agents.hama://imdb-12345/1/71?lang=en' ] for item in guids: r = Guid.parse(item) assert r.service == 'imdb' assert r.id == '12345' assert r.season == 1 assert r.episode == 71 assert r.language == 'en'
def get_ids(cls, guid, strict=True): ids = {} if not guid: return ids if type(guid) is str: # Parse raw guid guid = Guid.parse(guid, strict=strict) if guid and guid.valid and guid.service in GUID_SERVICES: ids[guid.service] = guid.id elif strict: return None return ids
def test_myanimelist_episode(): guids = [ 'net.devvsbugs.coding.plex.myanimelist://1234/1/71?lang=en', 'net.fribbtastic.coding.plex.myanimelist://1234/1/71?lang=en' ] for item in guids: r = Guid.parse(item) assert r.service == 'myanimelist' assert r.id == 1234 assert r.season == 1 assert r.episode == 71 assert r.language == 'en'
def test_tmdb_episode(): guids = [ 'com.plexapp.agents.themoviedb://12345/3/2?lang=en', 'com.plexapp.agents.hama://tmdb-12345/3/2?lang=en' ] for item in guids: r = Guid.parse(item) assert r.service == 'tmdb' assert r.id == 12345 assert r.season == 3 assert r.episode == 2 assert r.language == 'en'
def from_event(cls, info): account_key = try_convert(info.get('account_key'), int) rating_key = info.get('rating_key') if account_key is None or rating_key is None: log.warn('Invalid action format: %s', info) return None if account_key != 1: log.debug('Ignoring action from shared account') return None if WatchSession.is_active(rating_key, lambda ws: not ws.update_required): log.debug('Ignoring action, item is currently being watched') return False metadata = Metadata.get(rating_key) if not metadata: log.debug('Ignoring action, unable to retrieve metadata') return False section = metadata.section.title.lower() f_allow, _ = get_filter('filter_sections') if f_allow is not None and section not in f_allow: log.debug('Ignoring action, section has been filtered') return False guid = Guid.parse(metadata.guid) request = {} if type(metadata) is Movie: request = cls.from_movie(metadata, guid) elif type(metadata) is Season: request = cls.from_season(metadata, guid) elif type(metadata) is Episode: request = cls.from_episode(metadata, guid) else: log.warn('Unsupported metadata type: %r', metadata) return None log.debug('request: %r', request) return request
def test_anidb_episode(): guids = [ 'com.plexapp.agents.anidb://1234/1/71?lang=en', 'com.plexapp.agents.hama://anidb-1234/1/71?lang=en', 'com.plexapp.agents.hama://1234/1/71?lang=en' ] for item in guids: r = Guid.parse(item) assert r.service == 'anidb' assert r.id == 1234 assert r.season == 1 assert r.episode == 71 assert r.language == 'en'
def test_tvdb_episode(): guids = [ 'com.plexapp.agents.abstvdb://12345/13/52?lang=en', 'com.plexapp.agents.thetvdb://12345/13/52?lang=en', 'com.plexapp.agents.thetvdbdvdorder://12345/13/52?lang=en', 'com.plexapp.agents.xbmcnfotv://12345/13/52?lang=en', 'com.plexapp.agents.mcm://MCM_TV_A_12345/13/52?lang=en' ] for item in guids: r = Guid.parse(item) assert r.agent == 'tvdb' assert r.sid == '12345' assert r.season == 13 assert r.episode == 52
def assert_ignored(handler, key, sid, **kwargs): __tracebackhide__ = True # Determine media type artifact_key = get_artifact_key(handler) # Add item handler.on_added(key, Guid.parse('com.plexapp.agents.imdb://%s' % sid), **kwargs) # Ensure item wasn't added item = dict_path(handler.current.artifacts.artifacts, [ SyncData.Collection, 'add', artifact_key, ('imdb', sid) ]) if item != {}: pytest.fail("Artifact found, expected the item to be ignored")
def get_ids(cls, guid, strict=True): ids = {} if not guid: return ids if type(guid) is str: # Parse raw guid guid = Guid.parse(guid) if guid.service in GUID_SERVICES: ids[guid.service] = guid.id elif not strict: log.info('Unknown identifier service: "%s"', guid.service) else: log.info('Unknown identifier service: "%s" [strict]', guid.service) return None return ids
def test_tvdb_episode(): guids = [ 'com.plexapp.agents.abstvdb://12345/13/52?lang=en', 'com.plexapp.agents.hama://tvdb-12345/13/52?lang=en', 'com.plexapp.agents.thetvdb://12345/13/52?lang=en', 'com.plexapp.agents.thetvdbdvdorder://12345/13/52?lang=en', 'com.plexapp.agents.xbmcnfotv://12345/13/52?lang=en', 'com.plexapp.agents.mcm://MCM_TV_A_12345/13/52?lang=en' ] for item in guids: r = Guid.parse(item) assert r.service == 'tvdb' assert r.id == 12345 assert r.season == 13 assert r.episode == 52 assert r.language == 'en'
def assert_added(handler, key, sid, expected, **kwargs): __tracebackhide__ = True # Determine media type artifact_key = get_artifact_key(handler) # Set default attributes if expected: expected['ids'] = {'imdb': sid} # Add item handler.on_added(key, Guid.parse('com.plexapp.agents.imdb://%s' % sid), **kwargs) # Ensure item was added item = dict_path(handler.current.artifacts.artifacts, [ SyncData.Collection, 'add', artifact_key, ('imdb', sid) ]) if item != expected: pytest.fail("Artifact %r doesn't match the expected item %r" % (item, expected))
def get_metadata(rating_key): # Retrieve metadata for `rating_key` try: metadata = Metadata.get(rating_key) except NotImplementedError as ex: log.debug('%r, ignoring session', ex.message) return None, None # Ensure metadata was returned if not metadata: return None, None # Validate metadata if metadata.type not in ['movie', 'episode']: log.info('Ignoring metadata with type %r for rating_key %r', metadata.type, rating_key) return metadata, None # Parse guid guid = Guid.parse(metadata.guid, strict=True) return metadata, guid
def test_imdb(): assert Identifier.get_ids( Guid.parse('com.plexapp.agents.imdb://tt123456')) == { 'imdb': 'tt123456' }
def test_unknown_strict(): assert Identifier.get_ids(Guid.parse('example://123456'), strict=True) is None