def check_file_access(m): """Check if we can reach the file directly or if we have to download it via PMS. Args: m (plexapi.video.Episode) Return: filepath or http to the file. """ LOG.debug('Checking if we can reach %s directly', m._prettyfilename()) files = list(m.iterParts()) for file in files: if os.path.exists(file.file): LOG.debug('Found %s', file.file) return file.file else: LOG.warning('Downloading from pms..') try: # for plexapi 3.0.6 and above. return PMS.url('%s?download=1' % file.key, includeToken=True) except TypeError: return PMS.url('%s?download=1' % file.key)
def download_subtitle(episode): import srt episode.reload() LOG.debug('Downloading subtitle from PMS') pms = episode._server to_dl = [] all_subs = [] for part in episode.iterParts(): if part.subtitleStreams(): for sub in part.subtitleStreams(): if sub.key and sub.codec == 'srt': to_dl.append( pms.url('%s?download=1' % sub.key, includeToken=True)) for dl_url in to_dl: r = requests.get(dl_url) r.raise_for_status() if r: try: a_sub = list(srt.parse(r.text)) all_subs.append(a_sub) except ValueError: LOG.exception('Failed to parse subtitle') return all_subs
def check_file_access(m): """Check if we can reach the file directly or if we have to download it via PMS. Args: m (plexapi.video.Episode) Return: filepath or http to the file. """ LOG.debug('Checking if we can reach %s directly', m._prettyfilename()) files = list(m.iterParts()) # Now we could get the "wrong" file here. # If the user has duplications we might return the wrong file # CBA with fixing this as it requires to much work :P # And the use case is rather slim, you should never have dupes. # If the user has they can remove them using plex-cli. for file in files: if os.path.exists(file.file): LOG.debug('Found %s', file.file) return file.file else: LOG.warning('Downloading from pms..') try: # for plexapi 3.0.6 and above. return PMS.url('%s?download=1' % file.key, includeToken=True) except TypeError: return PMS.url('%s?download=1' % file.key)
def add_ref_frame(fp, t, tvdbid, timestamp, gui): import cv2 if gui: from bw_plex.tools import play play(fp, key=tvdbid) return if fp.endswith(('.mp4', '.mkv', '.avi')) and timestamp: cap = cv2.VideoCapture(fp) ms = to_ms(timestamp) cap.set(cv2.CAP_PROP_POS_MSEC, ms) ret, frame = cap.read() else: # So its a image... frame = fp frames_hash = create_imghash(frame) frames_hex = ''.join(hex(i) for i in frames_hash.flatten()) with session_scope() as se: try: se.query(Reference_Frame).filter_by(hex=frames_hex).one() click.echo('This frame already exist in the db') except NoResultFound: frm = Reference_Frame(hex=frames_hex, type=t, tvdbid=tvdbid) se.add(frm) LOG.debug('Added %s to Reference_Frame table hex %s tvdbid %s', fp, frames_hex, tvdbid)
def get_pms(url=None, token=None, username=None, password=None, servername=None, verify_ssl=None): # pragma: no cover url = url or CONFIG['server'].get('url') token = token or CONFIG['server'].get('token') verify_ssl = verify_ssl or CONFIG['server'].get('verify_ssl', False) servername = servername or CONFIG['server'].get('name') if url and token: sess = requests.Session() if not verify_ssl: sess.verify = False PMS = PlexServer(url, token, sess) elif username and password and servername: acc = MyPlexAccount(username, password) PMS = acc.resource(servername).connect() assert PMS is not None, 'You need to add a url and token or username password and servername' LOG.debug('Getting server %s', PMS.friendlyName) return PMS
def get_offset_end(vid, hashtable, check_if_missing=False): an = analyzer() match = matcher() start_time = -1 end_time = -1 t_hop = an.n_hop / float(an.target_sr) rslts, dur, nhash = match.match_file(an, hashtable, vid, 1) # The number does not matter... for (tophitid, nhashaligned, aligntime, nhashraw, rank, min_time, max_time) in rslts: end_time = max_time * t_hop start_time = min_time * t_hop LOG.info( 'Match %s rank %s aligntime %s theme song %s started at %s (%s) in ended at %s (%s)' % (tophitid, rank, aligntime, hashtable.names[tophitid], start_time, to_time(start_time), end_time, to_time(end_time))) if len(rslts): best = rslts[0] end_time = best[6] * t_hop start_time = best[5] * t_hop LOG.debug('Best match was %s', hashtable.names[best[0]]) return start_time, end_time LOG.debug('no result just returning -1') return start_time, end_time
def get_offset_end(vid, hashtable, check_if_missing=False): an = analyzer() match = matcher() # Or should we just check here?? untested. if vid not in hashtable.names and check_if_missing: an.ingest(vid, hashtable) start_time = -1 end_time = -1 t_hop = an.n_hop / float(an.target_sr) rslts, dur, nhash = match.match_file(an, hashtable, vid, 1) # The number does not matter... for (tophitid, nhashaligned, aligntime, nhashraw, rank, min_time, max_time) in rslts: # print(tophitid, nhashaligned, aligntime, nhashraw, rank, min_time, max_time) end_time = max_time * t_hop start_time = min_time * t_hop LOG.debug( 'Theme song started at %s (%s) in ended at %s (%s)' % (start_time, to_time(start_time), end_time, to_time(end_time))) return start_time, end_time LOG.debug('no result just returning -1') return start_time, end_time
def get_pms(url=None, token=None, username=None, password=None, servername=None, verify_ssl=None): from plexapi.myplex import MyPlexAccount from plexapi.server import PlexServer url = url or CONFIG.get('url') token = token or CONFIG.get('token') verify_ssl = verify_ssl or CONFIG.get('verify_ssl', False) if url and token: sess = requests.Session() if not verify_ssl: sess.verify = False PMS = PlexServer(url, token, sess) elif username and password and servername: acc = MyPlexAccount(username, password) PMS = acc.resource(servername).connect() LOG.debug('Getting server %s', PMS.friendlyName) return PMS
def edl_dict_to_metadata_file(path, eld): """Convert a .edl file to a ffmepg metadata file. This way we can add chapters to shows as this isnt suppored by plex Args: path (str): path to the edl we should use. Return path to metadata file. """ # Should we check if this file has metadata/chapters so we dont overwrite it # Lets come back to this later. #if not os.path.isfile(path) and path.endswith('.edl'): # return header = ';FFMETADATA1\ntitle=%s\nartist=Made by bw_plex\n\n' % os.path.splitext(os.path.basename(path))[0] chapter_template = """[CHAPTER]\nTIMEBASE=1/1000\nSTART=%s\nEND=%s\ntitle=%s\n\n""" meta_name = os.path.splitext(path)[0] + '.metadata' with open(meta_name, 'w') as mf: mf.write(header) for key, value in eld.items(): mf.write(chapter_template % (float(value[0]) * 1000, float(value[1]) * 1000, key)) LOG.debug('Created a metadatafile %s', meta_name) return meta_name
def convert_and_trim_to_mp3(afile, fs=8000, trim=None, outfile=None): # pragma: no cover if outfile is None: tmp = tempfile.NamedTemporaryFile(mode='r+b', prefix='offset_', suffix='.mp3') tmp_name = tmp.name tmp.close() outfile = tmp_name if os.name == 'nt' and '://' not in afile: q_file = '"%s"' % afile else: q_file = afile cmd = [ 'ffmpeg', '-i', q_file, '-ss', '0', '-t', str(trim), '-codec:a', 'libmp3lame', '-qscale:a', '6', outfile ] LOG.debug('calling ffmepg with %s' % ' '.join(cmd)) if os.name == 'nt': cmd = '%s' % ' '.join(cmd) psox = subprocess.Popen(cmd, stderr=subprocess.PIPE) o, e = psox.communicate() if not psox.returncode == 0: print(e) raise Exception("FFMpeg failed") return outfile
def add_theme_to_hashtable(threads, directory): """ Create a hashtable from the themes. Args: theads (int): How many threads to use. directory (None, str): If you don't pass a directory will select the default one Returns: None """ from bw_plex.audfprint.audfprint import multiproc_add global HT HT = get_hashtable() a = analyzer() all_files = [] for root, _, files in os.walk(directory or THEMES): for f in files: fp = os.path.join(root, f) # We need to check this since when themes are downloaded # They sometimes get a 0b files. if os.path.exists(fp) and os.path.getsize(fp): all_files.append(fp) def report(s): # this shitty reporter they want sucks balls.. pass #print(s) LOG.debug('Creating hashtable, this might take a while..') multiproc_add(a, HT, iter(all_files), report, threads) if HT and HT.dirty: HT.save(FP_HASHES)
def download_subtitle(episode): episode.reload() LOG.debug('Downloading subtitle from PMS') pms = episode._server to_dl = [] all_subs = [] for part in episode.iterParts(): if part.subtitleStreams(): for sub in part.subtitleStreams(): if sub.key and sub.codec in FILE_EXTENSION_TO_FORMAT_IDENTIFIER.values( ): to_dl.append( pms.url('%s?download=1' % sub.key, includeToken=True)) for dl_url in to_dl: r = episode._server._session.get(dl_url) r.raise_for_status() if r: try: subt = [ sub for sub in SSAFile.from_string(r.text, encoding=r.encoding) ] all_subs.append(subt) except (IOError, pysubs2.exceptions.UnknownFPSError, pysubs2.exceptions.UnknownFormatIdentifierError, pysubs2.exceptions.FormatAutodetectionError): LOG.exception('Failed to parse subtitle') return all_subs
def get_pms(url=None, token=None, username=None, password=None, servername=None, verify_ssl=None): # pragma: no cover url = url or CONFIG['server'].get('url') token = token or CONFIG['server'].get('token') verify_ssl = verify_ssl or CONFIG['server'].get('verify_ssl', False) servername = servername or CONFIG['server'].get('name') if url and token: url = url.rstrip('/') sess = requests.Session() if not verify_ssl: # Disable the urllib3 warning as the user # has choosen to not verify the http request. # why the f**k isnt this default? requests.packages.urllib3.disable_warnings() sess.verify = False PMS = PlexServer(url, token, sess) elif username and password and servername: acc = MyPlexAccount(username, password) PMS = acc.resource(servername).connect() assert PMS is not None, 'You need to add a url and token or username password and servername' LOG.debug('Getting server %s', PMS.friendlyName) return PMS
def get_hashtable(): LOG.debug('Getting hashtable') from bw_plex.audfprint.hash_table import HashTable # Patch HashTable. try: import cPickle as pickle except ImportError: import pickle import gzip def load(self, name=None): if name is None: self.__filename = name self.load_pkl(name) return self def save(self, name=None, params=None, file_object=None): LOG.debug('Saving HashTable') # Merge in any provided params if params: for key in params: self.params[key] = params[key] if file_object: f = file_object self.__filename = f.name else: if name is None: f = self.__filename else: self.__filename = f = name f = gzip.open(f, 'wb') pickle.dump(self, f, pickle.HIGHEST_PROTOCOL) self.dirty = False return self HashTable.save = save HashTable.load = load if os.path.exists(FP_HASHES): LOG.info('Loading existing files in db') HT = HashTable(FP_HASHES) HT.__filename = FP_HASHES else: LOG.info('Creating new hashtable db') HT = HashTable() HT.__filename = FP_HASHES HT.save(FP_HASHES) HT.load(FP_HASHES) return HT
def match_event(event): val = radio.value_selected h = ImageHash(create_imghash(CURR_FRAME)) with session_scope() as se: rf = Reference_Frame(hex=str(h), type=val, tvdbid=KEY) LOG.debug('Added %s %s from %s in %s', str(h), val, POS, first) se.add(rf)
def load(self, name=None): if name is None: self.__filename = name self.load_pkl(name) LOG.debug('Files in the hashtable') for n in self.names: LOG.debug(n) return self
def download_theme(media, ht, theme_source=None, url=None): if media.TYPE == 'show': name = media.title rk = media.ratingKey _theme = media.theme else: name = media.grandparentTitle rk = media.grandparentRatingKey _theme = media.grandparentTheme if _theme is None: _theme = media.show().theme pms = media._server if theme_source is None: theme_source = CONFIG['tv'].get('theme_source', 'all') if theme_source == 'youtube': theme = search_for_theme_youtube(name, rk, THEMES, url=url) elif theme_source == 'tvtunes': theme = search_tunes(name, rk, url=url) theme = list(itertools.chain.from_iterable(theme.values())) elif theme_source == 'plex' and _theme is not None: theme = pms.url(_theme, includeToken=True) LOG.debug('Downloading theme via plex %s', theme) elif theme_source == 'all': theme = [] st = search_tunes(name, rk, url=url) st_res = list(itertools.chain.from_iterable(st.values())) theme.extend(st_res) if _theme is not None: theme.append(pms.url(_theme, includeToken=True)) theme.append(search_for_theme_youtube(name, rk, THEMES, url=url)) if not isinstance(theme, list): theme = [theme] final = [] for th in theme: LOG.debug('Download theme using source %s', th) # Filename is just added so we can pass a url to convert_and_trim th = convert_and_trim(th, fs=11025, theme=True, filename='%s__%s__%s' % (name, rk, int(time.time()))) analyzer().ingest(ht, th) final.append(th) return final
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'): return theme = get_theme(media) LOG.debug('task theme %s', theme) LOG.debug('Download the first 10 minutes of %s as .wav', media._prettyfilename()) vid = convert_and_trim(check_file_access(media), fs=11025, trim=600) # Check if this shows theme exist in the hash table. # We should prop just check if x in HT.names try: HT.name_to_id(theme) except ValueError: LOG.debug('No fingerprint for theme %s does exists in the %s', os.path.basename(theme), FP_HASHES) analyzer().ingest(HT, theme) #HT = HT.save_then_reload(FP_HASHES) start, end = get_offset_end(vid, HT) ffmpeg_end = find_offset_ffmpeg(check_file_access(media)) if end != -1 or ffmpeg_end != 1: # End is -1 if not found. Or a positiv int. process_to_db(media, theme=theme, vid=vid, start=start, end=end, ffmpeg_end=ffmpeg_end) try: os.remove(vid) LOG.debug('Deleted %s', vid) except IOError: LOG.excetion('Failed to delete %s', vid) nxt = find_next(media) if nxt: process_to_db(nxt) try: IN_PROG.remove(item) except ValueError: LOG.debug('Failed to remove %s from IN_PROG', item)
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 get_theme(self, media): if media.TYPE == 'show': name = media.title rk = media.ratingKey else: rk = media.grandparentRatingKey name = media.grandparentTitle d = self.get_themes().get(rk, []) LOG.debug('%s has %s themes', name, len(d)) return d
def manually_correct_theme(name, url, type, rk, just_theme): """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 url to the correct theme. rk (str): ratingkey of that show. Pass auto if your lazy. just_theme (bool): just add the theme song. 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 # I dont think think themes be removed anymore as one show can now have several themes # also it dont remove the files in THEMES, this is intended!. 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. theme_path = download_theme(items[0], HT, theme_source=type, url=url) to_pp = [] # THis should be removed, if you just want to download the theme. use find theme. if just_theme: return if rk: with session_scope() as se: # Find all episodes of this show. item = se.query(Preprocessed).filter_by(grandparentRatingKey=rk) for i in item: to_pp.append(PMS.fetchItem(i.ratingKey)) # Prob should have 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 load_themes(): LOG.debug('Loading themes') items = os.listdir(THEMES) for i in items: fp = os.path.join(THEMES, i) LOG.debug(fp) if fp: try: show_rating = i.split('__')[1].split('.')[0] SHOWS[int(show_rating)] = fp except IndexError: pass
def write_chapters_to_file(path, input_edl=None, replace=True, cleanup=True): """Use ffmpeg to add chapters to a videofile.mf_file Args: path(str): path to the video file we should add chapters to input_edl (str): path the the edl. replace (bool): Default True cleanup(bool): Default False, remove the .metadatafile after chapters has been added. Return: path """ if input_edl is None: input_edl = has_edl(path) mf_file = edl_to_metadata_file(input_edl) mf_file = str(mf_file) outfile, ext = os.path.splitext(path) outfile = outfile + '__bw_plex_meta' + ext cmd = [ 'ffmpeg', '-i', path, '-i', mf_file, '-map_metadata', '1', '-codec', 'copy', outfile ] proc = subprocess.Popen(cmd) code = proc.wait() if code != 0: LOG.debug('Failed to write_chapters_to_file %s', code) # Try to replace the orginal with the one with have added # chapters too. if replace: for i in range(3): try: shutil.move(outfile, path) break except OSError: time.sleep(1) if cleanup: os.remove(mf_file) LOG.debug('Deleted %s', mf_file) return path
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))
def client_jump_to(offset=None, sessionkey=None): """Seek the client to the offset. Args: offset(int): Default None sessionkey(int): So we made sure we control the correct client. Returns: None """ global JUMP_LIST LOG.debug('Called jump with %s %s %s', offset, to_time(offset), sessionkey) if offset == -1: return conf_clients = CONFIG.get('clients', []) conf_users = CONFIG.get('users', []) for media in PMS.sessions(): # Find the client.. This client does not have the correct address # or 'protocolCapabilities' so we have to get the correct one. # or we can proxy thru the server.. if sessionkey and int(sessionkey) == media.sessionKey: client = media.players[0] user = media.usernames[0] # LOG.debug('client %s %s', client.title, (media.viewOffset / 1000)) # Check that this client is allowed. if conf_clients and client.title not in conf_clients: LOG.debug('Client %s is not whitelisted', client.title) return # Check that this user is allowed. if conf_users and user not in conf_users: LOG.debug('User %s is not whitelisted', user) return # To stop processing. from func task if we have used to much time.. # This will not work if/when credits etc are added. Need a better way. # if offset <= media.viewOffset / 1000: # LOG.debug('Didnt jump because of offset') # return # This does not work on plex web since the f****r returns # the local url.. client = PMS.client(client.title).connect() client.proxyThroughServer() client.seekTo(int(offset * 1000)) LOG.debug('Jumped %s %s to %s %s', user, client.title, offset, media._prettyfilename()) # Some clients needs some time.. # time.sleep(0.2) # client.play() JUMP_LIST.remove(sessionkey) #time.sleep(1) return
def set_manual_theme_time(showname, season, episode, type, start, end): # pragma: no cover """Set a manual start and end time for a theme. Args: showname(str): name of the show you want to find season(int): season number fx 1 episode(int): episode number 1 type(str): theme, credit # Still TODO Stuff for credits start(int, str): This can be in seconds or MM:SS format start(int, str): This can be in seconds or MM:SS format Returns: None """ LOG.debug('Trying to set manual time') result = PMS.search(showname) if result: items = choose('Select show', result, 'title') show = items[0] ep = show.episode(season=season, episode=episode) if ep: with session_scope() as se: item = se.query(Processed).filter_by( ratingKey=ep.ratingKey).one() start = to_sec(start) end = to_sec(end) if type == 'ffmpeg': item.correct_ffmpeg = end elif type == 'theme': if start: item.correct_time_start = start if end: item.correct_time_end = end elif type == 'credits': if start: item.correct_credits_start = start if end: item.correct_credits_end = end LOG.debug('Set correct_time %s for %s to start %s end %s', type, ep._prettyfilename(), start, end)
def manually_correct_theme(name, url, type, rk, just_theme): """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 url to the correct theme. rk (str): ratingkey of that show. Pass auto if your lazy. just_theme (bool): just add the theme song. Returns: None """ global HT HT = get_hashtable() # Assist for the lazy bastards.. if rk == 'auto': item = PMS.search(name) item = choose('Select correct show', item, lambda x: '%s %s' % (x.title, x.TYPE)) if item: rk = item[0].ratingKey theme_path = search_for_theme_youtube(name, rk=rk, url=url, save_path=THEMES) for fp in HT.names: if os.path.basename(fp).lower() == name.lower() and os.path.exists(fp): LOG.debug('Removing %s from the hashtable', fp) HT.remove(fp) analyzer().ingest(HT, theme_path) # HT.save(FP_HASHES) to_pp = [] if just_theme: return if rk: with session_scope() as se: item = se.query(Preprocessed).filter_by(grandparentRatingKey=rk) for i in item: to_pp.append(PMS.fetchItem(i.ratingKey)) # Prob should have 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 users_pms(pms, user): # pragma: no cover """Login on your server using the users access credentials.""" from plexapi.exceptions import NotFound LOG.debug('Logging in on PMS as %s', user) acc = pms._server.myPlexAccount() try: usr = acc.user(user) except NotFound: # We fail to find the correct user if the passed user is the owner.. # We we simply return the owner pms as we already have that. # TODO this might be a issue, see if we cant handle this another way using plexapi. LOG.debug('returning org pms') return pms token = usr.get_token(pms.machineIdentifier) users_pms = PlexServer(pms._baseurl, token) return users_pms
def write_chapters_to_file(path, input_edl=None, replace=True, cleanup=True): """Use ffmpeg to add chapters to a videofile.mf_file Args: path(str): path to the video file we should add chapters to input_edl (str): path the the edl. replace (bool): Default True cleanup(bool): Default False, remove the .metadatafile after chapters has been added. Return: path """ if 'https://' or 'http://' in path: LOG.debug( "Can't add chapters to as we dont have access to the file on the file system" ) mf_file = edl_dict_to_metadata_file(path, input_edl) mf_file = str(mf_file) outfile, ext = os.path.splitext(path) outfile = outfile + '__bw_plex_meta' + ext cmd = [ 'ffmpeg', '-i', path, '-i', mf_file, '-map_metadata', '1', '-codec', 'copy', outfile ] proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) code = proc.wait() if code != 0: LOG.debug('Failed to write_chapters_to_file %s', code) # Try to replace the orginal with the one with have added # chapters too. if replace: for _ in range(3): try: shutil.move(outfile, path) break except OSError: time.sleep(1) if cleanup: os.remove(mf_file) LOG.debug('Deleted %s', mf_file) LOG.debug('writing chapters to file using command %s', ' '.join(cmd)) return path
def edl_to_metadata_file(path): """Convert a .edl file to a ffmepg metadata file. This way we can add chapters to shows as this isnt suppored by plex Args: path (str): path to the edl we should use. Return path to metadata file. """ # Should we check if this file has metadata/chapters so we dont overwrite it # Lets come back to this later. #if not os.path.isfile(path) and path.endswith('.edl'): # return header = ';FFMETADATA1\ntitle=%s\nartist=Made by bw_plex\n\n' % os.path.splitext( os.path.basename(path))[0] chapter_template = """[CHAPTER]\nTIMEBASE=1/1000\nSTART=%s\nEND=%s\ntitle=%s\n\n""" meta_name = os.path.splitext(path)[0] + '.metadata' with open(path, 'rb') as e: lines = e.readlines() lines = [i.split() for i in lines if i] with open(meta_name, 'w') as mf: last_en = len(lines) - 1 mf.write(header) for en, l in enumerate(lines): if en == 0: title = 'first' elif en == last_en: title = 'last' else: title = '' mf.write(chapter_template % (int(l[0]) * 1000, int(l[1]) * 1000, title)) LOG.debug('Created a metadatafile %s', meta_name) return meta_name