#!/usr/bin/python """ Update cache or -n (--noupdate) and search cache for rec_ids: surlatablo.py -q search_pattern Update cache with new recording and deletions surlatablo.py Search for recordings and convert them surlatablo.py -n -q search_pattern -c General format surlatablo.py [-ncdCyvhtBz] [-o =] [-s [-]] [-i ] [-k @|] [-q ] [-b [::][,...]] [-f ] [-E [::|::][,...]] [-S [::|::][,...]] [-Q ] [-O ] [[+]]... Use option --help for detailed usage. """ SLT_GLOBAL = {} SLT_GLOBAL['PGM_VERSION'] = '2.0' # Sur la Tablo (on the Tablo) SLT_GLOBAL['PGM_NAME'] = 'surlatablo' # License SLT_GLOBAL['PGM_LICENSE'] = 'GPLv2' # Configuration file # You can override option variables below by putting your into # this file. SLT_GLOBAL['CONF'] = '~/surlatablo2.conf' ### PROGRAM LOCATIONS # Replace with full path location of your programs FFMPEG = '/usr/bin/ffmpeg' CCEXTRACTOR = '/usr/local/bin/ccextractor' ### IMPORTANT LOCAL SETTINGS SURLATABLO_ROOT = '/SurLaTablo' # Cache db goes here TABLO_IPS = ['127.0.0.1'] # Should we detect? # Use a python list of Tablo IPs. LOCAL_TIMEZONE = 'US/Central' # Your current timezone NOZEROTV = True # Substitutes se for when # season or episode are zero (for meta_type TV) # Also sets episode title # Not sure if I'll ever handle True here, so leave as False for now. # The reason? Windows users do not deal with "real" filenames well. PRESERVE_FILENAMES = False # Preserve Unicode filenames by renaming files # after all actions are done. This makes the filenames # look good, and allows us to "print" the safe/ascii # names to the console before that while program operates # on data. There are just too many variables to make # this work well in Windows, but I might do this at # some point. import os if (os.name == 'nt'): # Warning, path to font file in Windows cannot contain drive-letter: GIF_FONTFILE = '/Windows/Fonts/arial.ttf' # Populates dynamic meta var ${gif_fontfile} termenc = 'cp437' else: GIF_FONTFILE = '/usr/share/fonts/truetype/DejaVuSans.ttf' # Populates dynamic meta var ${gif_fontfile} termenc = 'utf-8' ### CONF FILE PROCESSING # You can override and/or augment anything above this line using # the contents of the config file. # # Open and read the conf data into a variable so we can exec it # again later. So that substitutions can be done to config items # after this line. # try: if (os.path.isfile(os.path.expanduser(SLT_GLOBAL['CONF']))): with open(os.path.expanduser(SLT_GLOBAL['CONF']),'r') as f: confdata = f.read() exec(confdata) except: pass ### DIRECTORY AND FILE CREATION PATTERNS # Patterns for output handling by meta_type (e.g. TV, Sports, Movie, Manual) # Patterns can contain meta information made by this program using # ${meta-name}. Just like TabloTV, this programs metadata is non-uniform # by media type (aka meta_type) # BASE_DIRS={'Default':'./${meta_type}', 'Sports':'./${meta_type}/${sport_type}/Season ${season_number}', 'TV':'./${meta_type}/${Eseries}/Season ${season_number}', 'Movie':'./${meta_type}/${Etitle} (${release_year})' } # Everything but the final extension of the final transcoded file. See TRANSCODER_OPTS last element. FILENAME_PATS={'Default':'${Etitle}', 'TV':'${Eseries} - s${plex_season_number}e${plex_episode_number} - ${Etitle}', 'Sports':'${sport_type} - s${plex_season_number}e${plex_episode_number} - ${Etitle}', 'Movie':'${Etitle} (${release_year})' } ############################################################################### # BE CAREFUL ABOUT MODS TO OPTIONS BELOW ############################################################################### # CHANGELOG # # 2.0 - Fixes. Added -U which means skip query matches of things unwatched. Added -P which means # skip query matches of things protected. Those are VERY expensive options as a check to the Tablo # is done for each matched item to see if they should be skipped or not. These options might be # valuable combined with the Delete or DeleteX "transcoders". Because these are expensive, # SurLaTablo defaults to deleting things even if protected. Therefore I recommend using at least # the -P option when doing deletes. There are metadata changes, remove your files under # SURLATABLO_ROOT and do full reindex. # # 2.0b4 - Fixes. Addition of options['titles_from_description'] which defaults to false. If set, # if there is a resource with no title, it will see if it can construct something from the description # up to the first comma or semi-colon. Otherwise and by default title will set to lair_date YYYY-mm-dd. # Sports with no season will no longer have empty parens appended to the title. As with TV, worst # case for Sports will append lair_date YYYY-mm-dd. # # 2.0b3 - Fixes. Addition of Mp4zap1 and Mp4zap2 "transcoders". Addition of a remove_format similar # to query_format and a formatted_remove boolean to trigger it. This gives you the option of # creating a better format for the db cache REMOVES than the normal full JSON dump. # # 2.0b2 - Bug fixes mostly. However there are two new transcoders, Delete (which prompts) # and DeleteX. If you haven't guessed, this will delete the matched items. While you could tack # it onto your TRANSCODER_DEFAULT array or add in addition to whatever you have define by +DeleteX # on the command line, my preference would be to pull and then delete (just saying). # # -i now works ok... Tablo separated the data, so it tries to do "right" things # when searching for the record. You can search using a full path or a number. I try to dump # all that is interesting out of the Tablo, however, it's not just one big item anymore, it can # be 2 or 3 items now per "rec_id". # # 2.0b1 - First release for new 2.2.12 firmware (Tablo's official "rest" api). Note: Tablo no longer has # key meta data like genre and original_air_date like before. The SURLATABLO_CONF file now defaults # to surlatablo2.conf in your home directory. The SurLaTablo meta data and recording cache files # have been renamed (they begin with slt2) because both formats are different... thus on first # run, it will re-index all of your recordings into the local cache... could take many many minutes. # # 2 - Note: Created info for channels (channel_map) no longer needed or used. Created info for genres # (genre_map) no longer handled. # # 1.9p1 - patch for character handling # 1.9 - (genres sorta leave) # 1. Genres are now handled via a genre_map that you can include in your surlatablo.conf file. # To generate your own you will need sqlite3 and then you should locate your Chrome browser's # http_my.tablotv.com_0 folder and the database under it (should be a number, mine was 4). # Then you can do: # sqlite3 4 # .output genre.txt # .separator ":" # select objectID,JSON from genre; # Then take the output in genre.txt and wrap inside a couple of lines like so in your surlatablo.conf: # ... # genre_map = { # # } # 2. Similarly you can create your channel_map: # sqlite3 4 # .output channel.txt # .separator ":" # select objectID,JSON from recChannel; # Then take the output in channel.txt and wrap inside a couple of lines like so in your surlatablo.conf: # ... # channel_map = { # # } # 1.8 - (genres return... well it's a beginning) # 1. A genre_map is included now in SurLaTablo. It's probably not complete, but is an attempt to make genre # meta data possible again (${genres}). You'll have to remove your SURLATABLO_ROOT files and regen # (just do surlatablo.py to regen). # # 1.7 - (channels return... sort of) # 1. You can add back channel handling by creating your own mapping of Tablo meta_channel ids to channel_num data. # You can add the mapping to your surlatablo.conf. The meta_channel is now a piece of metadata you can query which might # help you in creating the mapping like so (you'd have to watch/record shows to get the meta_channel numbers). # The meta_channel is called recChannel in the original Tablo metadata: # channel_map = { # 110247: { 'channel_num': '27.1' }, # 173111: { 'channel_num': '13.1' }, # 26697: { 'channel_num': '33.1' }, # 39153: { 'channel_num': '5.1' }, # 39243: { 'channel_num': '11.1' }, # 46752: { 'channel_num': '4.1' }, # 50615: { 'channel_num': '68.1' }, # 630311: { 'channel_num': '8.1' } # } # 2. Escape forward slashes for when a show/series title has slashes in it (to prevent inadvertent folder creations). # # 1.6 - (quick release) # 1. Fix for Mp4W transcode when width is "wrong"... there may be more problems than this. # 2. Added options['no_utf8_names'] for when an OS might handle it but the filesytem chosen cannot. # # 1.5 - (patient in hospital release) # 1. Fixed some metadata problems. # 2. Better tags handling for mp4 metadata. # 3. Set some pre-defined transcoders as being --zapcommercials safe. # # 1.4 - (the sadness release) # 1. With removal of extended metadata by Nuvyyo, the following changes had to be made: # - gamedate (and gamedate_year, gamedate_month, etc.) is simply replace by the local # air date data. Which is definitely not the same, but the best we can now do. # - long_description will now simply inherit the short or regular description. # - channel_* all channel metadata is gone. # - genre is gone. # - rating is gone. # - qualifiers_ list is gone. # 2. I have added season and episode metadata into ffmpeg transcoded files. # 3. Added a --ts option which allows makes the assumption that the pulled Tablo mpeg4_ts # file pull has already been done. Useful for trying things out without have to # do the slow re-pull of the mpeg4_ts off the Tablo. # # Note: due to metadata changes, you may want to remove your cache (surlatablo will rebuild) # if you have recorded any Sports type of program especially after Dec 1. Existing metadata for # shows prior to Nuvyyo's changes to the metadata are currently not affected and the elements # mentioned above will still work for those shows. # # I still believe there is some hope to get back some of the lost functionality. # # 1.3 - # 1. When using -C always save the .srt file into the transcode target directory. # Copy both into your plex area and then you can use the SRT from Roku (for example). # 2. PGM_VERSION was never change in 1.2, fixed to 1.3 in this version. # # 1.2 - # 1. Added ability to set a default queryformat using options['queryformat']. # 2. Added new dynamic queryformat variables, ${Wdescription}, ${Wlong_description} and # ${friendly_date}. # 3. Added options['wrapwidth'], options['wrapindent'] and options['wrapsubindent'] to control # how ${Wdescription} or ${Wlong_description} will be wrapped on output. # # 1.1 - # 1. Added another algorithm for use with -z. Switch by setting options['zap_algorithm'] = 2 # in surlatablo.conf or use -o zap_algorithm=2 along with -z option. # 2. Added warnings about use of truncate with -z. Warning about not using a zap compatible # transcoder like Mp4z when using -z. # 3. Added options['truncate_ts']. This is better for -z than using truncate. A # value of options['truncate_ts'] = 75 should clip off about 5 min. off the original # ts from the Tablo, and commercial processing will come after that. # # 1.0 - # 1. Fixed a bug in article detection for sort_title (thanks @alexbunk). # 2. Added options['truncate'] to allow clipping off some seconds from end # of show. Many people might set options['truncate'] = 300 in their # surlatablo.conf file (truncate last 5 minutes). # 3. Add fix to commercial zap code that might prevent an infinite loop. # 4. Added timers so you can see how long steps take. # 5. Added Mp4z trancode option that is relatively fast, but a full # transcode. Useful when using -z to zap commercials (gets rid of # the audio/video out of sync problem for zapped shows at a bit of a # performance and quality hit). # # 0.9 - # 1. Better file handling for Windows. That is, strip out everything that # is not ASCII (sigh). It is weird because NTFS handles unicode (roughly) # filenames, but Windows shells do not handle it. Python 2.7 also has # some issues, just don't look at Python 3 to fix everything (it doesn't). # 2. Made some tweaks to commercial handling. Added backtoback to join # close segments together. # # 0.8 - # 1. Moved 2nd exec of SLT_GLOBAL['CONF'] (surlatablo.conf) to happening # after the module imports. In particular this allows you to set # tempfile.tempdir = 'temp-file-dir'. # 2. Added commercial removal (-z). I have not found a good way of # creating subtitles that work with such a file though. So for now, # you cannot specify both -C -z, if you do the -C will get tossed. # You can notice the "stitch" points in the resulting file. The # analysis and creation of the commercial-less file means twice the # ammount of .ts (defaults to temp storage) is required. # 3. You can now override elements of the "options" array. This is # mainly so you can tweak the commercial detection and extraction # variables. Set with -o name=value: # blackdetect_d (.03) - This is length of black frames in seconds # to be considered a valid black frame. I can see cases # where raising this to .04 is ok. I wouldn't make any # other adjustment to this one. # blackdetect_pic_th (1) - Ratio (coverage) to be considered as "black". I'd # leave this alone. # blackdetect_pix_th (.1) - How black is black? Again, I would leave this # alone. # commercials_max_time (500) - If the routines seems to be stuck in # commercials for more than this length in seconds, start adjusting # the "interval" by "interval_reduce" for "reduce_attempts" times in # order to try to find some show content. Probably best to keep in # the range of 300 - 600. # interval (230) - Length in seconds from end of last black segment to next # black segment. If you make this too small, you'll end up with # commercials. If you make this too high, you may miss some show. # I'd keep this in the range of 200-300. # reduce_attempts (3) - Number of times to attempt interval reduce in # cases where we believe we may have missed some show. # interval_reduce (30) - This is subtracted from the interval when # it is believed that we are missing some show elements. Done # for "reduce_attempts" number of times before giving up. # postscript (false) - Some TV shows in particular may have a very # short postscript (at the end) situated in the midst of a sea of commercials. # Since this almost impossible to detect, setting this option will # grab the content from the last known black interval after the last # known show segment to the end of the file. Yes, you will get commercials, # but you will also get the postscript segment. Set this to a non # empty string value to make it true. # Other values could be added to the options array using -o. I'd be very # careful about adjusting other values. # # 0.7 - # 1. -E, -S, now gerneralized and long formats renamed to --episode and --season. # These can now be used to force season and episode data in a made up way # for handling TV by series name values where no season/episode meta data # was provided. # e.g. -S Gidget::1 -E Gidget::1 # When queried, converting, this arranges the series matches for Gidget # by original air date and numbers season as "1" and episodes starting # at "1" and auto-incrementing. # The behavior for making up meta data for Sports shows has not changed. # 2. Removed some extraneous debug prints. # 3. Added the -B option to allow converting/processing of shows that are # stuck on the Tablo in a non-"finished" state. Not sure what causes this. # Some say it happens when things go "bad", but I think I've seen this # just randomly. Anyhow, if you have stuck shows that always say they # are *RECORDING/BUSY*, this option will allow you to ignore that # (use with caution... don't ignore shows that you know are really # recording, and if you do, avoid converting them until they are # truly done recording). # 4. Fixed bug, sort_title was not working. You will want to remove your # SURLATABLO_ROOT/ cache files and regenerate. # 5. Added -s (--sortkeys) option to allow you to specify what metakeys to # to sort by (default is sort by lair_date descending). Warning: this # is also a filter. Records that do not have all the metakeys specified # will not be output. For example if you have -q . (everything) and -s # series, only TV shows will be shown since they are the only records # containing series. # 6. Query format (-Q) may now specify urls. In which case they are templated # (that is, metakey substitutions are done). # There are two formats. url('location1','location2',...) and # URL('location1','location2',...). The second variant, URL, forces # metakey substitution on each url prior to inclusion. If the resource # scheme begins with '1' (e.g. 1http://...) then include that resource # only once for all matches. Resource schemes allowed are (http: and file:, # some others might work...) # # 0.6 - # 1. Fix surlatablo.conf bug, put back trap around it, read into a variable # so that it can be exec'd twice, before major options and after. # 2. Changed meaning of -O (offset) to be offset for whatever matches, just # and offset value. The default setting is to apply the options['liveoffset'] # value to Live shows (from new meta type qualifiers_). # 3. Successfully tested on OSX. Not really a "bug", just a statement. # # 0.5 - # Quick fix to correct Eseries bug. # # 0.4 - # 1. You may now have a SLT_GLOBAL['CONF'] file (defaults to homedir # surlatablo.conf) which can be used to override the configuration variables. # This way you won't have to keep editing the source file for local site # changes. # 2. TV shows with season 0 and episode 0 (e.g. News) will now default # to se and Unknown titles will default # to Episode - --. You can # disable this by setting NOZERTV = False in your .conf file (see #1). # original_air_date cannot be used for this because quite often it is wrong. # 3. Pathnames for output files are now santized for Windows. # 4. surlatablo.py now takes arugments beyond switches which are to specify # the transcodes to do on the downloaded data. Default if not present is # just Mp4. You can override the default by setting in you .conf file: # TRANSCODER_DEFAULT = [ 'Mp4', 'Gif', etc. ] # You can specify on the command like: # surlatablo.py .... Mp4 Gif Json # or # surlatablo.py .... +Gif +Json # The '+' means in addition to the TRANSCODER_DEFAULT values. # Use '-t' option to see brief description of available transcoders. # 5. Options --wcrop (-w) and --cropformat (-W) have been removed. Create and # use a new TRANSCODER_OPTS definition for this. # 6. You can now use --keepdir @ to say you want to save the .ts and .srt file # in the output directory with the other files. # 7. New predefined TRANSCODER_OPTS have been added, created. (see -t option) # a. Mp4 - (the norm) to create a passthrough .mp4 # b. Gif - create short duration animated gif # c. Flash - create an .flv # d. HardSubs - (assumes use of -C) to embed subitles directly in .mp4 # e. Shrink - create a more compressed .mp4 # f. Json - create .json file containing SurLaTablo meta information for program # g. Null - used mainly to say "no transcode", for times when you want to use # --keepdir @ to output and save just the .ts and (optionally) .srt # h. ... (more) # 8. Replace sanitizeFilename with sltSanitize for dynamic meta type strings # like Etitle, Eseries, essentially things that could be used in path and filenames. # Special dynamic variable Efriendly_title2 is a two line escaped string for # internal ffmpeg option use (see Gif). # 9. Bug fixes.... etc... # #Sat Jan 17 14:24:17 CST 2015 # 0.3 - New metatype, sort_title, which is the title minus leading # a/an/the article. Added a sanitizeFilename function, mainly for # Windows. # #Thu Jan 15 00:00:26 CST 2015 # 0.2 - Defined some timezone stuff for when there is no pytz. Doesn't handle # everything. Handle escapes like \n, \t in -Q. Check for ability to # create SURLATABLO_ROOT and exit with a message instead of fail with stack # trace. New meta-type, friendly_title, added to make it easier to use # friendly_title~='Series - s01' for easy season selection. ### COMMAND OPTIONS ### TRANSCODER options # Allowed functions that can be called as transcoder. TRANSCODER_FUNCS = [ 'shutil.copyfile', 'dumpJson', 'doNothing', 'deleteResource' ] # Default transcode(s) to provide if none provided TRANSCODER_DEFAULT = [ 'Mp4' ] # The allowed transcoders # All data options here need to be strings. TRANSCODER_OPTS = { 'Mp4z': { 'help': [ 'Ultrafast Mp4 re-transcode often used with -z.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-preset', 'ultrafast' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [], 'ext': [ '-z.mp4' ] }, 'Mp4zap1': { 'help': [ 'Ultrafast Mp4 re-transcode that force -z 1.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-preset', 'ultrafast' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [ '1' ], 'ext': [ '-z.mp4' ] }, 'Mp4zap2': { 'help': [ 'Ultrafast Mp4 re-transcode that force -z 1.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-preset', 'ultrafast' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [ '2' ], 'ext': [ '-z.mp4' ] }, 'Mp4': { 'help': [ 'Standard pass through conversion of video and audio from Tablo .ts to .mp4.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-bsf:a', 'aac_adtstoasc', '-c:v', 'copy', '-c:a', 'copy' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'ext': [ '.mp4' ] }, 'Mp4W': { 'help': [ 'Crop a 4:3 480i channel presentation of a 16:9 widescreen movie.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-bsf:a', 'aac_adtstoasc', '-vf', 'crop=in_w:360:0:60', '-c:a', 'copy' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [], 'ext': [ '-w.mp4' ] }, 'Gif': { 'help': [ 'Create an short animated Gif' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-ss', '${gifstart}', '-i', '${ts_filename}', '-t', '${gifduration}' ], 'options': [ '-loop', '0', '-r', '${gifrate}', '-s', '${gifsize}', '-vf', 'drawtext=fontfile=${gif_fontfile}:text=\'${Efriendly_title2}\':fontcolor=white@1.0:fontsize=30:x=10:y=40' ], 'ext': [ '.gif' ] }, 'Flash': { 'help': [ 'Create a flash .flv file' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'options': [ '-ab', '56', '-ar', '44100', '-r', '25', '-qmin', '3', '-qmax', '6', '-s', '320x240' ], 'zap': [], 'ext': [ '.flv' ] }, 'HardSubs': { 'help': [ 'For use with -C, burn the subtitles directly into the video of the .mp4' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'options': [], 'subtitleoptions': [ '-vf', 'subtitles=\'${srt_filename}\'' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'ext': [ '-hs.mp4' ] }, 'Shrink': { 'help': [ 'Create a smaller .mp4 file.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-crf', '25' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [], 'ext': [ '-sm.mp4' ] }, # This is here more or less as an example, normally you would do this with -k @ and Null # An example of using a python libray module function instead of an executable. 'Ts': { 'help': [ 'Testing, do not use. Use --keepdir @ instead.' ], 'command': [ 'shutil.copyfile' ], 'inputfile': [ '${ts_filename}' ], 'ext': [ '.ts' ] }, # This is so that if we transcode, and then go back an delete off of the Tablo, # we can have a way to preserve the meta data for the program. # This is an example of calling a global procedure defined here. 'Json': { 'help': [ 'Save json meta data into .json file.' ], 'command': [ 'dumpJson' ], 'inputfile': [ '"${rec_id}": ${json}' ], 'ext': [ '.json' ], 'skip_ts': [ 'True' ] }, # Delete with confirmation prompt 'Delete': { 'help': [ 'Delete from Tablo prompting interactively to confirm.' ], 'command': [ 'deleteResource' ], 'options' : [ '${tablo_ip}', '${path}', '${friendly_title}', 'N' ], 'ext': [ '.always' ], 'skip_ts': [ 'True' ] }, # Delete without confirmation 'DeleteX': { 'help': [ 'Delete from Tablo.' ], 'command': [ 'deleteResource' ], 'options' : [ '${tablo_ip}', '${path}', '${friendly_title}', 'Y' ], 'ext': [ '.always' ], 'skip_ts': [ 'True' ] }, 'Null': { 'help': [ 'To specify no transcodes. Useful when combined with --keepdir @.' ], 'command': [ 'doNothing' ], 'ext': [ '.always' ] }, # Slow 'Glow': { 'help': [ 'Apply the Frei04 Glow affect to video.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-vf', 'frei0r=glow:0.5' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [], 'ext': [ '-glow.mp4' ] }, # Slow 'h265': { 'help': [ 'Product an HEVC h265 .mp4 file.' ], 'command': [ FFMPEG ], 'threads': [ '-threads', '0' ], 'inputfile': [ '-i', '${ts_filename}' ], 'subtitlefile': [ '-f', 'srt', '-i', '${srt_filename}' ], 'options': [ '-bsf:a', 'aac_adtstoasc', '-c:v', 'libx265', '-preset', 'medium', '-x265-params', 'crf=28', '-c:a', 'copy' ], 'subtitleoptions': [ '-c:s', 'mov_text', '-metadata:s:s:0', 'language=${lang3}' ], 'audiooptions': [ '-metadata:s:a:0', 'language=${lang3}' ], 'metadata': [], 'zap': [], 'ext': [ '-h265.mp4' ] } } ### CCEXTRACTOR options CCEXTRACTOR_OPTS = { 'Default': { 'command': [ CCEXTRACTOR ], 'inputfile': [ '${ts_filename}' ], 'options': [ '--gui_mode_reports', '-nogt' ], 'outputfile': [ '-o', '${srt_filename}' ] } } ### OTHER MODIFIERS # Tablo url templates TABLO_REC_IDS='http://${tablo_ip}:8885/recordings/airings' TABLO_REC_INFO='http://${tablo_ip}:8885{}' TABLO_SEGS='http://${tablo_ip}{}' ### OPTION DEFAULTS options = { 'backtoback' : '20', 'blackdetect_d' : '.03', 'blackdetect_pic_th' : '1', 'blackdetect_pix_th' : '0.10', 'busyignore' : False, 'ccaption' : False, 'ccaptionopts' : CCEXTRACTOR_OPTS['Default'], 'ccoffset': '', 'commercials_max_time' : '350', 'existing_ts' : False, 'formatted_remove' : False, 'friendly_date_format' : '%A, %B %d, %Y at %I:%M %p', 'gifstart' : '00:01:45', 'gifduration' : '10', 'gifrate' : '10', 'gifsize' : '320x200', 'use_existing_tsfile' : False, 'idebug' : '', 'ignorecase' : 0, 'interval': 300, 'interval_reduce': '40', 'keepsrt': 'Yes', 'keepdir': '', 'liveoffset': '4', 'maxshow' : '400', 'noclobber' : True, 'nocommercials' : '', 'noprotected' : False, 'nounwatched' : False, 'no_utf8_names' : '', 'postscript' : '', 'queryformat' : '', 'reduce_attempts' : '3', 'removeformat' : 'Removed: (${rec_id}) ${friendly_title}', 'srtsubtitle' : '', 'srtmovtext' : '', 'titles_from_description' : '', 'titles_from_description_max' : 50, 'truncate' : 0, 'truncate_ts' : 0, 'ts_parallel' : 1, 'update' : True, 'wrapindent' : '', 'wrapsubindent' : '', 'wrapwidth' : 80, 'zap_algorithm' : '1' } def firstThings(): helpstring = """ FIRST THINGS ------------ Edit this program and change the values for (near top of file, or, better, use surlatablo2.conf in your home directory folder).: # Location of your ffmpeg program (optional if you won't convert or transcode) # Linux or OSX (example): FFMPEG = '/usr/bin/ffmpeg' # OR if Windows (example): FFMPEG = 'C:/Program Files/ffmpeg/bin/ffmpeg.exe' # Location of your ccextractor program (optional) # Linux or OSX (example): CCEXTRACTOR = '/usr/local/bin/ccextractor' # OR if Windows (example): CCEXTRACTOR = 'c:/Program Files (x86)/CCExtractor/ccextractorwin.exe' # Location directory where meta db caches go SURLATABLO_ROOT = '/SurLaTablo' # Local timezone. LOCAL_TIMEZONE = 'US/Central' # List of your Tablo IPs, this program does not auto detect today. # Once you set this, the FIRST THINGS will no longer print and program # assumes everything is setup. TABLO_IPS = ['192.168.1.73'] """ print(Template(helpstring).safe_substitute(SLT_GLOBAL)) def detailedHelp(): helpstring = """ ${PGM_NAME} ${PGM_VERSION} ${PGM_LICENSE} UPDATE AND SEARCH ----------------- Update cache db (need metadata db to do anything) (typically you do this first, first run will take a bit) surlatablo.py Update and search db surlatablo.py --query Green\ Acres Do not update cache, just search db (note matches Green Acres anywhere in the metadata) surlatablo.py --noupdate --query Green\ Acres Search db by metadata # Find all for TV series called Green Acres surlatablo.py --noupdate --query series~=Green\ Acres # Find all Sports records surlatablo.py --noupdate --query meta_type~=Sports Use a query format string to change output # Find all TV shows and user a query format for output # (note --queryformat will not do anything combined with --convert) surlatablo.py --noupdate --query meta_type~=TV --queryformat '${lair_date}\t${rec_id}\t${series}\t${title}' Display full TabloTV metadata for a specific recording id. # (note --rec_id can be used with --convert) surlatablo.py --noupdate --rec_id 28191 Query and sort # Find all TV shows and sort by series and then by local air date. surlatablo.py --noupdate --query meta_type~=TV --sortkeys series,lair_date Note: You can easily force a full refresh of local cached data by removing the files at SULATABLO_ROOT/Tablo_IP/* CONVERT OR PREVIEW CONVERT (ADD SUBTITLES/CC, MP4 METADATA) ----------------------------------------------------------------- Locate a particular recording, convert with close captioning done as a subtitle. Sample interative output also shown. surlatablo.py --noupdate --query Fuji\ falls --convert --ccaption --clobber Working on: [./TV/McHale's Navy/Season 3/McHale's Navy - s03e15 - Fuji's Big Romance.mp4] Retrieving Tablo Data (28191): [####################] 100% Extracting CC as Subtitle: [####################] 100% Transcoding: [####################] 100% Locate all sports and use default season of game_year and use supplied episode start number by sport_type. Defaulting starting with episode number 1 for unspecified types. surlatablo.py --noupdate --query meta_type~=Sports -E "NFL Football::3,NBA Basketball::5,1" --convert Use the --debug (probably misnamed) to show what surlatablo.py would do, but don't do it (option to be combined with --convert) surlatablo.py --noupdate --query Fuji\ falls --convert --ccaption --clobber --debug [ ./TV/McHale's Navy/Season 3/McHale's Navy - s03e15 - Fuji's Big Romance.mp4 ] [ http://192.168.1.73:18080/pvr/28191/segs -> /tmp/TabloE1_zkh.ts] ['/usr/local/bin/ccextractor', '/tmp/TabloE1_zkh.ts', '--gui_mode_reports', '-o', '/tmp/TabloZdkhvf.srt'] ['/usr/bin/ffmpeg', '-i', '/tmp/TabloE1_zkh.ts', '-f', 'srt', '-i', '/tmp/TabloZdkhvf.srt', '-bsf:a', 'aac_adtstoasc', '-c:v', 'copy', '-c:a', 'copy', '-c:s', 'mov_text', '-metadata:s:s:0', 'language=eng', '-metadata:s:a:0', 'language=eng', '-metadata', "title=Fuji's Big Romance", '-metadata', 'date=1964-12-25', '-metadata', 'network=Antenna', '-metadata', "album=McHale's Navy", '-metadata', "show=McHale's Navy", '-metadata', 'genre=Sitcom', '-metadata', 'episode_id=s03e15', '-metadata', "synopsis=Fuji falls in love with a native chief's daughter.", '-metadata', "description=Fuji falls in love with a native chief's daughter.", '-metadata', "comment=Fuji falls in love with a native chief's daughter.", '-y', "./TV/McHale's Navy/Season 3/McHale's Navy - s03e15 - Fuji's Big Romance.mp4"] Note: Better, full output of an actual --convert can be captured simply be redirecting output to a file instead of to screen/terminal. Useful for when things aren't working (bugs in the software). Note: Program uses Python tempfile functions. To adjust your temporary directory place: tempfile.tempdir = '/path-to/my-tempdir' For other notes on how Python finds your temporary file location, see Python documentation. MORE EXAMPLES ------------- Find all Mister Ed episodes and convert them using the Default transoding plug a short animated Gif preview: surlatablo.py -n -q series~='Mister Ed' -C -c +Gif BASIC CONVERT OPTIONS (for use with --convert) --------------------- --ccaption = Pull closed captioning off of Tablo recording and convert it to subtitle format for inclusion with final transcode. --clobber = By default, this program will not convert if the target end result filename already exists. --keepdir = Instead of deleting intermediate temporary files (e.g. the Tablo .ts file, the subtitle .srt file), keep them here. If keepdir is @, files will go to the destination directory for the final transcoded files and be named like --filename. --zapcommercials = Attempt to remove commercials from the Tablo data. --options = = Override elements of the options array. Mainly for tweaking commercial detection. See the CHANGELOG for now. ADVANCED CONVERT OPTIONS ------------------------ As with query formats, you may use metadata keys inside of the paths here. --episode = Format of the form: [::|::] Use episode_number starting with start_number and incrementing by one for every match. Different start_numbers can be specified for the different sport_types. --season = Format of the form: [::|::] Override using the game_date and use supplied season_number as the season for the sporting event, a different season can be specified by each sport_type. --basedir = Format of the form: ::,.. Base directory location for final converted mp4 file based on meta_type. Predefines are (BASE_DIRS): Default = ./${meta_type} (e.g. ./Movie, ./Manual) Sports = ./${meta_type}/${sport_type}/Season ${season_number} (e.g. ./Sports/NFL Football/Season 2014) (remember, you can override the season_number for Sports on the command line with -S) TV = ./${meta_type}/${Eseries}/Season ${season_number} --filename = Format of the form: ::,... (minus the file extension) Predefines are (FILENAME_PATS): Default = ${Etitle} TV = ${Eseries} - s${plex_season_number}e${plex_episode_number} - ${Etitle} Sports = ${sport_type} - s${plex_season_number}e${plex_episode_number} - ${Etitle} Movie = ${Etitle} (${release_year}) Note: The Default applies only in cases where there is no meta_type match. So if we wanted to override the location for a TV show. We could override with, --basedir TV::. --filename TV::MyOwnFilename The output then would simply be placed at ./MyOwnFilename.mp4. If we want Movies to stored inside of directory instead of flat inside of the Default basedir, we could use, --basedir Movie::./${meta_type}/${Etitle} (${release_year}) You can alter the values of BASE_DIRS and FILENAME_PATS in this program to make your own "default" settings persistent. COMMAND LINE SWITCH SUMMARY WITH SHORTCUTS ------------------------------------------ -h, --help = Print this very long help. -t, --transcodehelp = Summary of valid transcode arguments -v, --version = Print this program version. -d, --debug = Turn on "debug" output, which is really just a show without execution flag for --convert. -B, --busyignore = Allows use of --convert on files that Tablo thinks are in a busy state, but are not (you must decide). -c, --convert = Attempt to transcode a matching query set or --rec_id value. -C, --ccaption = Extract subtitle .srt file from closed captioning and insert subtitle stream into final ouput file. -y, --clobber = Overwrite final destination file. Normally, program exits if final output file exists. -n, --noupdate = Do not process new or deletions off Tablo unit, just go off existing cached data. -i, --rec_id = Show TabloTV full metadata for supplied id. -k, --keepdir = Keep temporary files used in --convert at this directory location. Use @ to keep with transcoded files. -q, --query = Query the meta db, in particular format can be of the form metakey~=Value. -b, --basedir = Comma separted list of basedir format patterns, [::]path -f, --filename = Formatted pattern to produce final name (without extension). -z, --zapcommercials = Attempt to extract just the show data (best if used with Mp4z transcoder). -o, --options = = Override elements of the options array. -S, --season = Format is [::|::]season-number instead of using game_year|lair_date_month. -E, --set_episode = Format is [::|::]episode-number. -Q, --queryformat = Customize output for successful queries. Can contain meta_keys of the form: ${meta_keyname}. You can also embed escapes like \t (tab) and \n (newline). Use option['queryformat'] to set a default format. -O, --ccoffset