def manually_correct_theme(name, url, type, rk, just_theme, remove_old_theme): # pragma: no cover """Set the correct fingerprint of the show in the hashes.db and process the eps of that show in the db against the new theme fingerprint. Args: name (str): name of the show url (str): the youtube/tvtunes url or filepath to the correct theme. type (str): What source to use for themes. rk (str): ratingkey of that show. Pass auto if your lazy. just_theme (bool): just add the theme song not reprocess stuff. remove_old_theme (bool): Removes all the old themes of this show Returns: None """ global HT HT = get_hashtable() # Assist for the lazy bastards.. if rk == 'auto': items = PMS.search(name) items = [i for i in items if i and i.TYPE == 'show'] items = choose('Select correct show', items, lambda x: '%s %s' % (x.title, x.TYPE)) if items: rk = items[0].ratingKey if remove_old_theme: themes = HT.get_theme(items[0]) for th in themes: LOG.debug('Removing %s from the hashtable', th) HT.remove(th) # Download the themes depending on the manual option or config file. download_theme(items[0], HT, theme_source=type, url=url) to_pp = [] if just_theme: return if rk: with session_scope() as se: # Find all episodes of this show. item = se.query(Processed).filter_by(grandparentRatingKey=rk) for i in item: to_pp.append(PMS.fetchItem(i.ratingKey)) # Prob should have used edit, but we do this so we can use process_to_db. se.delete(i) for media in to_pp: process_to_db(media)
def task(item, sessionkey): """Main func for processing a episode. Args: item(str): a episode's ratingkey sessionkey(str): streams sessionkey Returns: None """ global HT media = PMS.fetchItem(int(item)) LOG.debug('Found %s', media._prettyfilename()) if media.TYPE not in ('episode', 'show', 'movie'): # pragma: no cover return if media.TYPE == 'episode': LOG.debug('Download the first 10 minutes of %s as .wav', media._prettyfilename()) vid = convert_and_trim(check_file_access(media), fs=11025, trim=CONFIG['tv'].get('check_for_theme_sec', 600)) process_to_db(media, vid=vid) try: os.remove(vid) LOG.debug('Deleted %s', vid) except IOError: # pragma: no cover LOG.exception('Failed to delete %s', vid) elif media.TYPE == 'movie': process_to_db(media) try: IN_PROG.remove(item) except ValueError: # pragma: no cover LOG.debug('Failed to remove %s from IN_PROG', item) nxt = find_next(media) if nxt: process_to_db(nxt)
def check(data): global JUMP_LIST if data.get('type') == 'playing' and data.get( 'PlaySessionStateNotification'): sess = data.get('PlaySessionStateNotification')[0] if sess.get('state') != 'playing': return ratingkey = int(sess.get('ratingKey')) sessionkey = int(sess.get('sessionKey')) progress = sess.get('viewOffset', 0) / 1000 # converted to sec. mode = CONFIG['general'].get('mode', 'skip_only_theme') no_wait_tick = CONFIG['general'].get('no_wait_tick', 0) def best_time(item): """Find the best time in the db.""" if item.type == 'episode' and item.correct_theme_end and item.correct_theme_end != 1: sec = item.correct_theme_end elif item.correct_ffmpeg and item.correct_ffmpeg != 1: sec = item.correct_ffmpeg elif item.type == 'episode' and item.theme_end and item.theme_end != -1: sec = item.theme_end elif item.ffmpeg_end and item.ffmpeg_end != -1: sec = item.ffmpeg_end else: sec = -1 return sec def jump(item, sessionkey, sec=None, action=None): # pragma: no cover if sec is None: sec = best_time(item) if action: POOL.apply_async(client_action, args=(sec, sessionkey, action)) return if sessionkey not in JUMP_LIST: LOG.debug('Called jump with %s %s %s %s', item.prettyname, sessionkey, sec, action) JUMP_LIST.append(sessionkey) POOL.apply_async(client_action, args=(sec, sessionkey, action)) with session_scope() as se: try: item = se.query(Processed).filter_by(ratingKey=ratingkey).one() if item: bt = best_time(item) LOG.debug( 'Found %s theme start %s, theme end %s, ffmpeg_end %s progress %s ' 'best_time %s credits_start %s credits_end %s', item.prettyname, item.theme_start_str, item.theme_end_str, item.ffmpeg_end_str, to_time(progress), to_time(bt), item.credits_start_str, item.credits_end_str) if (item.type == 'episode' and CONFIG['tv'].get('check_credits') is True and CONFIG['tv'].get('check_credits_action') == 'stop' or item.type == 'movie' and CONFIG['movie'].get('check_credits') is True and CONFIG['movie'].get('check_credits_action') == 'stop'): # todo check for correct credits too if item.credits_start and item.credits_start != -1 and progress >= item.credits_start: LOG.debug('We found the start of the credits.') return jump(item, sessionkey, item.credits_start, action='stop') # Let's try to not wait for the next tick. progress = progress + no_wait_tick # If recap is detected just instantly skip to intro end. # Now this can failed is there is: recap, new episode stuff, intro, new episode stuff # So thats why skip_only_theme is default as its the safest option. if (mode == 'skip_if_recap' and item.type == 'episode' and item.has_recap is True and bt != -1): return jump(item, sessionkey, bt) # This mode will allow playback until the theme starts so it should be faster then skip_if_recap. if mode == 'skip_only_theme': # For manual corrected themes.. if item.type == 'episode' and item.correct_theme_end and item.correct_theme_start: if progress > item.correct_theme_start and progress < item.correct_theme_end: LOG.debug( '%s is in the correct time range correct_theme_end', item.prettyname) return jump(item, sessionkey, item.correct_theme_end) elif item.type == 'episode' and item.theme_end and item.theme_start: if progress > item.theme_start and progress < item.theme_end: LOG.debug( '%s is in the correct time range theme_end', item.prettyname) return jump(item, sessionkey, item.theme_end) except NoResultFound: if ratingkey not in IN_PROG: IN_PROG.append(ratingkey) LOG.debug('Failed to find ratingkey %s in the db', ratingkey) ret = POOL.apply_async(task, args=(ratingkey, sessionkey)) return ret elif data.get('type') == 'timeline': timeline = data.get('TimelineEntry')[0] state = timeline.get('state') ratingkey = timeline.get('itemID') title = timeline.get('title') metadata_type = timeline.get('type') identifier = timeline.get('identifier') metadata_state = timeline.get('metadataState') if (metadata_type in (1, 4) and state == 0 and metadata_state == 'created' and identifier == 'com.plexapp.plugins.library'): LOG.debug('%s was added to %s', title, PMS.friendlyName) # Youtubedl can fail if we batch add loads of eps at the same time if there is no # theme. if (metadata_type == 1 and not CONFIG['movie'].get('process_recently_added') or metadata_state == 4 and not CONFIG['tv'].get('process_recently_added')): LOG.debug( "Didnt start to process %s is process_recently_added is disabled" ) return if ratingkey not in IN_PROG: IN_PROG.append(ratingkey) ep = PMS.fetchItem(int(ratingkey)) ret = POOL.apply_async(process_to_db, args=(ep, )) return ret elif (metadata_type in (1, 4) and state == 9 and metadata_state == 'deleted'): if (metadata_type == 1 and not CONFIG['movie'].get('process_deleted') or metadata_state == 4 and not CONFIG['tv'].get('process_deleted')): LOG.debug( "Didnt start to process %s is process_deleted is disabled for" ) return with session_scope() as se: try: item = se.query(Processed).filter_by( ratingKey=ratingkey).one() item.delete() LOG.debug('%s was deleted from %s and from media.db', title, PMS.friendlyName) except NoResultFound: LOG.debug('%s was deleted from %s', title, PMS.friendlyName)
def check_db(client_name, skip_done): # pragma: no cover """Do a manual check of the db. This will start playback on a client and seek the video file where we have found theme start/end and ffmpeg_end. You will be asked if its a correct match, press y or set the correct time in mm:ss format. Args: client_name (None, str): Name of the client you want to use (watch) skip_done (bool): Skip episodes that already exist in the db. Returns: None """ if client_name is None: client = choose('Select what client to use', PMS.clients(), 'title') if len(client): client = client[0] else: click.echo('No client to check with.. Aborting') return else: client = PMS.client(client_name).connect() client.proxyThroughServer() with session_scope() as se: items = se.query(Processed).all() click.echo('') for item in sorted(items, key=lambda k: k.ratingKey): click.echo('%s %s' % (click.style('Checking', fg='white'), click.style(item.prettyname, bold=True, fg='green'))) click.echo('theme_start %s theme_end %s ffmpeg_end %s' % (item.theme_start, item.theme_end_str, item.ffmpeg_end)) click.echo('*%s*' % ('-' * 80)) click.echo('') media = PMS.fetchItem(item.ratingKey) if item.theme_start == -1 or item.theme_end == -1: click.echo( 'Exists in the db but the start of the theme was not found.' ' Check the audio file and run it again. Use cmd manually_correct_theme' ) if item.theme_end != -1: if (not skip_done and item.correct_theme_start ) or not item.correct_theme_start: click.echo('Found theme_start at %s %s theme_end %s %s' % (item.theme_start, item.theme_start_str, item.theme_end, item.theme_end_str)) client.playMedia(media, offset=item.theme_start * 1000) time.sleep(1) start_match = click.prompt( 'Was theme_start at %s correct? [y or MM:SS]' % item.theme_start_str) if start_match: if start_match in ['y', 'yes']: item.correct_theme_start = item.theme_start else: item.correct_theme_start = to_sec(start_match) if (not skip_done and item.correct_theme_end) or not item.correct_theme_end: client.playMedia(media, offset=item.theme_end * 1000) end_match = click.prompt( 'Was theme_end at %s correct? [y or MM:SS]' % item.theme_end_str) if end_match: if end_match in ['y', 'yes']: item.correct_theme_end = item.theme_end else: item.correct_theme_end = to_sec(end_match) if item.ffmpeg_end: if (not skip_done and item.correct_ffmpeg) or not item.correct_ffmpeg: click.echo('Found ffmpeg_end at sec %s time %s' % (item.ffmpeg_end, item.ffmpeg_end_str)) if item.ffmpeg_end > 30: j = item.ffmpeg_end - 20 else: j = item.ffmpeg_end client.playMedia(media, offset=j * 1000) time.sleep(1) match = click.prompt( 'Was ffmpeg_end at %s correct? [y or MM:SS]' % item.ffmpeg_end_str) if match: if match.lower() in ['y', 'yes']: item.correct_ffmpeg = item.ffmpeg_end else: item.correct_ffmpeg = to_sec(match) # This needs to be tested manually. if item.credits_start and item.credits_start != 1: if (not skip_done and item.correct_credits_start ) or not item.correct_credits_start: click.echo('Found credits start as sec %s time %s' % (item.credits_start, item.credits_start_str)) client.playMedia(media, offset=item.credits_start - 10) time.sleep(1) match = click.prompt( 'Did the credits start at %s correct? [y or MM:SS]' % item.credits_start_str) if match: if match.lower() in ['y', 'yes']: item.correct_credits_start = item.credits_start else: item.correct_credits_start = to_sec(match) click.clear() # Commit this shit after each loop. if se.dirty: se.commit() click.echo('Done')