diff --git a/data/css/config.css b/data/css/config.css index 4412896544e18e0878095a54db0bf8419edc6266..a32d1a22a8e16f9ad8fcfdb4783f482478f1fd2a 100644 --- a/data/css/config.css +++ b/data/css/config.css @@ -24,8 +24,8 @@ #config div.field-pair{margin:.9em 0 1.4em;} #config div.field-pair input{float:left;margin-right:6px;} #config label.nocheck,#config div.providerDiv,#config div #customQuality{padding-left:20px;} -#config label span.component-title{font-size:1.2em;font-weight:700;float:left;width:160px;margin-right:10px;} -#config label span.component-desc{font-size:1.1em;} +#config span.component-title{font-size:1.2em;font-weight:700;float:left;width:160px;margin-right:10px;} +#config span.component-desc{font-size:1.1em;} #config div.field-pair select{font-size:1.1em;border:1px solid #d4d0c8;} #config div.field-pair select option{line-height:1.4;padding:0 10px; border-bottom: 1px dotted #D7D7D7;} #config-settings{float:right;width:200px;background:#fffae5;border-bottom:1px dotted #666;border-top:1px solid #999;margin-right:20px;padding:20px 0 30px;} diff --git a/data/interfaces/default/config_postProcessing.tmpl b/data/interfaces/default/config_postProcessing.tmpl index d8d0e211182ea84c5d25df1352a1fd604ee896ae..6c785a636ecb9e6e56f823d35fecfda1599aec6c 100644 --- a/data/interfaces/default/config_postProcessing.tmpl +++ b/data/interfaces/default/config_postProcessing.tmpl @@ -4,6 +4,8 @@ #from sickbeard import config #from sickbeard import metadata #from sickbeard.metadata.generic import GenericMetadata +#from sickbeard import naming + #set global $title = "Config - Post Processing" #set global $header = "Post Processing Configuration" @@ -19,10 +21,235 @@ <div id="config-content"> <h5>All non-absolute folder locations are relative to " <span class="path">$sickbeard.DATA_DIR</span> "</h5> -<form id="configForm" action="savePostProcessing" method="post"> +<form id="configForms" action="savePostProcessing" method="post"> <div id="config-components"> + <div id="core-component-group3" class="component-group clearfix"> + <div class="component-group-desc"> + <h3>Naming</h3> + <p>How Sick Beard will name and sort your episodes.</p> + </div> + + <fieldset class="component-group-list"> + + <div> + + <div class="clearfix"> + <span class="component-title">File Name</span> + <span class="component-desc"> + <select id="name_presets" class="naming_preset_select"> + #set $is_custom = True + #for $cur_preset in $naming.name_presets: + #set $tmp = $naming.test_name($cur_preset) + #if $cur_preset == $sickbeard.NAMING_NAME_PATTERN: + #set $is_custom = False + #end if + <option id="$cur_preset" #if $cur_preset == $sickbeard.NAMING_NAME_PATTERN then "selected" else ""#>$tmp['name']</option> + #end for + <option id="custom" #if $is_custom then "selected" else ""#>Custom...</option> + </select><br /> + <br /> + </span> + <div class="clearfix"></div> + <span class="naming_custom_span"> + <span class="component-title"> </span> + <span id="naming_name_custom" class="component-desc"> + <input type="text" size="60" name="naming_name_pattern" id="naming_name_pattern" class="naming_pattern" value="$sickbeard.NAMING_NAME_PATTERN" /><br /> + <br /> + </span> + </span> + </div> + + </div> + + <div> + + <div class="clearfix"> + <span class="component-title">Folder Name</span> + <span class="component-desc"> + <select id="dir_presets" class="naming_preset_select"> + <option id="none" #if not $sickbeard.NAMING_DIR_PATTERN then "selected" else ""#>None</option> + #set $is_custom = True + #for $cur_preset in $naming.dir_presets: + #set $tmp = $naming.test_name($cur_preset) + #if $cur_preset == $sickbeard.NAMING_DIR_PATTERN: + #set $is_custom = False + #end if + <option id="$cur_preset" #if $cur_preset == $sickbeard.NAMING_DIR_PATTERN then "selected" else ""#>$tmp['name']</option> + #end for + <option id="custom" #if $is_custom and $sickbeard.NAMING_DIR_PATTERN then "selected" else ""#>Custom...</option> + </select><br /> + <br /> + </span> + <div class="clearfix"></div> + <span class="naming_custom_span"> + <span class="component-title"> </span> + <span id="naming_dir_custom" class="component-desc"> + <input type="text" size="60" name="naming_dir_pattern" id="naming_dir_pattern" class="naming_pattern" value="$sickbeard.NAMING_DIR_PATTERN" /><br /> + <br /> + </span> + </span> + </div> + + </div> + + <div style="padding:10px; background: #efefef;"> + <label class="clearfix"> + <span class="component-title jumbo" style="padding-bottom: 5px;"><b>Example:</b></span> + <span class="component-desc jumbo" id="naming_example"></span> + </label> + </div> + + <div class="field-pair"> + <label class="nocheck clearfix" for="naming_multi_ep"> + <span class="component-title">Multi-episode Style</span> + <span class="component-desc"> + <select id="naming_multi_ep" name="naming_multi_ep"> + #for $cur_multi_ep in $multiEpStrings: + <option value="$cur_multi_ep">$multiEpStrings[$cur_multi_ep]</option> + #end for + </select> + </span> + </label> + </div> + + <div style="padding:10px; background: #efefef;"> + <label class="clearfix"> + <span class="component-title jumbo" style="padding-bottom: 5px;"><b>Multi-Ep Example:</b></span> + <span class="component-desc jumbo" id="naming_example_multi"></span> + </label> + </div> + + <br /> + + <div id="naming_custom_help"> +<b>Help</b><br /> +<br /> +<table width="100%" cellpadding="3" cellspacing="0"> + <tr style="background: #efefef;"> + <td><b>Show Name</b></td> + <td>%SN</td> + <td>Show Name</td> + </tr> + <tr style="background: #efefef;"> + <td> </td> + <td>%S.N</td> + <td>Show.Name</td> + </tr> + <tr style="background: #efefef;"> + <td> </td> + <td>%S_N</td> + <td>Show_Name</td> + </tr> + <tr> + <td>Episode Name</td> + <td>%EN</td> + <td>Episode Name</td> + </tr> + <tr> + <td> </td> + <td>%E.N</td> + <td>Episode.Name</td> + </tr> + <tr> + <td> </td> + <td>%E_N</td> + <td>Episode_Name</td> + </tr> + <tr style="background: #efefef;"> + <td><b>Season Number</b></td> + <td>%S</td> + <td>1</td> + </tr> + <tr style="background: #efefef;"> + <td> </td> + <td>%0S</td> + <td>01</td> + </tr> + <tr> + <td><b>Episode Number</b></td> + <td>%E</td> + <td>5</td> + </tr> + <tr> + <td> </td> + <td>%0E</td> + <td>05</td> + </tr> + <tr style="background: #efefef;"> + <td><b>Quality</b></td> + <td>%EQ</td> + <td>720p BluRay</td> + </tr> + <tr style="background: #efefef;"> + <td> </td> + <td>%E.Q</td> + <td>720p.BluRay</td> + </tr> + <tr style="background: #efefef;"> + <td> </td> + <td>%E_Q</td> + <td>720p_BluRay</td> + </tr> + <tr> + <td><b>Date</b></td> + <td>%AD</td> + <td>2010 03 22</td> + </tr> + <tr> + <td> </td> + <td>%A_D</td> + <td>2010_03_22</td> + </tr> + <tr> + <td> </td> + <td>%A.D</td> + <td>2010.03.22</td> + </tr> + <tr> + <td> </td> + <td>%A-D</td> + <td>2010-03-22</td> + </tr> + <tr style="background: #efefef;"> + <td><b>Year</b></td> + <td>%Y</td> + <td>2010</td> + </tr> + <tr> + <td><b>Month</b></td> + <td>%M</td> + <td>03</td> + </tr> + <tr style="background: #efefef;"> + <td><b>Day</b></td> + <td>%D</td> + <td>22</td> + </tr> + <tr> + <td><b>Release Name</b></td> + <td>%RN</td> + <td>Show.Name.S02E03.HDTV.XviD-GROUP</td> + </tr> + <tr style="background: #efefef;"> + <td><b>Release Group</b></td> + <td>%RG</td> + <td>GROUP</td> + </tr> +</table> + +<br /> +Use lower case if you want lower case names (eg. %sn, %e.n, %q etc)<br /> +<br /> + </div> + + <div class="clearfix"></div> + <input type="submit" class="config_submitter" value="Save Changes" /><br/> + + </fieldset> + </div><!-- /component-group3 //--> + <div id="core-component-group3" class="component-group clearfix"> <div class="component-group-desc"> @@ -165,132 +392,6 @@ </fieldset> </div><!-- /component-group2 //--> - <div id="core-component-group4" class="component-group clearfix"> - - <div class="component-group-desc"> - <h3>Episode Naming</h3> - <p>If post-processing 'Rename episodes' is enabled then use these settings.</p> - </div> - - #set $naming_ep_type_text = ("1x02", "s01e02", "S01E02") - #set $naming_multi_ep_type_text = ("extend", "duplicate", "repeat") - - <fieldset class="component-group-list"> - <div class="field-pair"> - <input type="checkbox" name="naming_show_name" id="naming_show_name" #if $sickbeard.NAMING_SHOW_NAME then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_show_name"> - <span class="component-title">Show Name</span> - <span class="component-desc">Include the TV show's name when renaming the file?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_ep_name" id="naming_ep_name" #if $sickbeard.NAMING_EP_NAME then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_ep_name"> - <span class="component-title">Episode Name</span> - <span class="component-desc">Include the TV show's episode title when renaming the file?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_use_periods" id="naming_use_periods" #if $sickbeard.NAMING_USE_PERIODS then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_use_periods"> - <span class="component-title">Use Periods</span> - <span class="component-desc">Replace the spaces with periods in the filename instead?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_quality" id="naming_quality" #if $sickbeard.NAMING_QUALITY then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_quality"> - <span class="component-title">Quality</span> - <span class="component-desc">Append the show quality to the end of the filename?</span> - </label> - </div> - - <div class="field-pair"> - <input type="checkbox" name="naming_dates" id="naming_dates" #if $sickbeard.NAMING_DATES then "checked=\"checked\"" else ""#/> - <label class="clearfix" for="naming_dates"> - <span class="component-title">Air-By-Date Format</span> - <span class="component-desc">Use the date instead of the season/episode format?</span> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">Only applies to air-by-date shows. (eg. 2010-02-15 vs S12E23)</span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix" for="naming_sep_type"> - <span class="component-title">Separator Style</span> - <span class="component-desc"> - <select name="naming_sep_type" id="naming_sep_type"> - #for ($i, $ex) in enumerate($config.naming_sep_type_text): - <option value="$i" #if $i == int($sickbeard.NAMING_SEP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> - #end for - </select> - </span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix" for="naming_ep_type"> - <span class="component-title">Number Style</span> - <span class="component-desc"> - <select name="naming_ep_type" id="naming_ep_type"> - #for ($i, $ex) in enumerate($config.naming_ep_type_text): - <option value="$i" #if $i == int($sickbeard.NAMING_EP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> - #end for - </select> - </span> - </label> - </div> - - <div class="field-pair" style="padding:10px; background: #efefef;"> - <label class="clearfix" for="naming_ep_type"> - <span class="component-title jumbo">Single-Ep Example:</span> - <span class="component-desc jumbo" id="normalExampleText"></span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix" for="naming_multi_ep_type"> - <span class="component-title">Multi-episode Style</span> - <span class="component-desc"> - <select name="naming_multi_ep_type" id="naming_multi_ep_type"> - #for ($i, $ex) in enumerate($config.naming_multi_ep_type_text): - <option value="$i" #if $i == int($sickbeard.NAMING_MULTI_EP_TYPE) then "selected=\"selected\"" else ""#>$ex</option> - #end for - </select> - </span> - </label> - </div> - - <div class="field-pair" style="padding:10px; background: #efefef;"> - <label class="clearfix" for="naming_multi_ep_type"> - <span class="component-title jumbo">Multi-Ep Example:</span> - <span class="component-desc jumbo" id="multiExampleText"></span> - </label> - </div> - - <div class="field-pair"> - <label class="nocheck clearfix"> - <span class="component-title">Season Folder Format</span> - <input type="text" id="season_folders_format" name="season_folders_format" value="$sickbeard.SEASON_FOLDERS_FORMAT" size="15" /> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">Format to use when creating season folders.</span> - </label> - <label class="nocheck clearfix"> - <span class="component-title"> </span> - <span class="component-desc">(eg. 'Season %0d' or 'season%02d')</span> - </label> - </div> - - <input type="submit" class="config_submitter" value="Save Changes" /> - </fieldset> - </div><!-- /component-group4 //--> <br/><input type="submit" class="config_submitter" value="Save Changes" /><br/> </div><!-- /config-components --> diff --git a/data/js/configPostProcessing.js b/data/js/configPostProcessing.js index b23562daf508ce2a1dd4fb20a1e67939f49f24bc..a17e3116177b4659ace9a63a37293afd6de831e3 100644 --- a/data/js/configPostProcessing.js +++ b/data/js/configPostProcessing.js @@ -1,61 +1,72 @@ $(document).ready(function(){ - $.fn.setExampleText = function() { - - params = {'show_name': $('#naming_show_name').prop('checked')?"1":"0", - 'ep_type': $('#naming_ep_type :selected').val(), - 'multi_ep_type': $('#naming_multi_ep_type :selected').val(), - 'ep_name': $('#naming_ep_name').prop('checked')?"1":"0", - 'use_periods': $('#naming_use_periods').prop('checked')?"1":"0", - 'quality': $('#naming_quality').prop('checked')?"1":"0", - 'sep_type': $('#naming_sep_type :selected').val(), - 'whichTest': 'single' - } - - $.get(sbRoot+"/config/postProcessing/testNaming", params, - function(data){ - $('#normalExampleText').text(data); - }); - - params['whichTest'] = 'multi' - $.get(sbRoot+"/config/postProcessing/testNaming", params, - function(data){ - $('#multiExampleText').text(data); - }); - - return - - }; - - $(this).setExampleText(); - - $('#naming_ep_name').click(function(){ - $(this).setExampleText(); - }); - - $('#naming_show_name').click(function(){ - $(this).setExampleText(); - }); - - $('#naming_use_periods').click(function(){ - $(this).setExampleText(); - }); - - $('#naming_quality').click(function(){ - $(this).setExampleText(); - }); - - $('#naming_multi_ep_type').change(function(){ - $(this).setExampleText(); - }); - - $('#naming_ep_type').change(function(){ - $(this).setExampleText(); - }); - - $('#naming_sep_type').change(function(){ - $(this).setExampleText(); - }); + function fill_examples() { + + var dir_pattern = $('#naming_dir_pattern').val(); + var name_pattern = $('#naming_name_pattern').val(); + + var pattern = dir_pattern + '/' + name_pattern; + var multi = $('#naming_multi_ep :selected').val(); + + $.get(sbRoot+'/config/postProcessing/testNaming', {pattern: pattern}, + function(data){ + $('#naming_example').text(data+'.ext'); + }); + + $.get(sbRoot+'/config/postProcessing/testNaming', {pattern: pattern, multi: multi}, + function(data){ + $('#naming_example_multi').text(data+'.ext'); + }); + } + + function do_custom_help() { + var show_help = false; + $('.naming_custom_span').each(function(){ + if ($(this).is(':visible')) { + show_help = true; + return false; + } + }); + + if (show_help) + $('#naming_custom_help').show(); + else + $('#naming_custom_help').hide(); + } + + function do_preset(me) { + + var preset = $(me+' :selected').attr('id'); + + if (preset == 'none') + preset = ''; + + if (preset == 'custom') + $(me).parent().siblings('.naming_custom_span').show(); + else + $(me).parent().siblings('.naming_custom_span').hide(); + + if (preset != 'custom') + $(me).parent().siblings('.naming_custom_span').children('.component-desc').children('.naming_pattern').val(preset); + + fill_examples(); + + do_custom_help(); + } + + // initialize the presets + do_preset('#dir_presets'); + do_preset('#name_presets'); + + $('.naming_preset_select').change(function(){ + var me = '#'+$(this).attr('id'); + do_preset(me); + }); + + $('#naming_multi_ep').change(fill_examples); + $('.naming_pattern').change(fill_examples); + + // -- start of metadata options div toggle code -- $('#metadataType').change(function(){ diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index c20fc5f8d075fbb9a1e46e5be53a4303493dcc8e..10fe0e6636438cc96c21c34152b3b6fe118beba5 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -36,7 +36,7 @@ from sickbeard import searchCurrent, searchBacklog, showUpdater, versionChecker, from sickbeard import helpers, db, exceptions, show_queue, search_queue, scheduler from sickbeard import logger -from sickbeard.common import * +from common import SD, SKIPPED, NAMING_REPEAT from sickbeard.databases import mainDB, cache_db @@ -117,20 +117,18 @@ METADATA_PS3 = None METADATA_WDTV = None METADATA_TIVO = None +MULTI_FORMAT = NAMING_REPEAT +NAME_FORMATTING = r'%RN/%0Sx%0E - %E.N (%RG)' + QUALITY_DEFAULT = None STATUS_DEFAULT = None SEASON_FOLDERS_FORMAT = None SEASON_FOLDERS_DEFAULT = None PROVIDER_ORDER = [] -NAMING_SHOW_NAME = None -NAMING_EP_NAME = None -NAMING_EP_TYPE = None -NAMING_MULTI_EP_TYPE = None -NAMING_SEP_TYPE = None -NAMING_USE_PERIODS = None -NAMING_QUALITY = None -NAMING_DATES = None +NAMING_MULTI_EP = None +NAMING_DIR_PATTERN = None +NAMING_NAME_PATTERN = None TVDB_API_KEY = '9DAF49C96CBF8DAC' TVDB_BASE_URL = None @@ -386,11 +384,10 @@ def initialize(consoleLogging=True): NZBMATRIX_APIKEY, versionCheckScheduler, VERSION_NOTIFY, PROCESS_AUTOMATICALLY, \ KEEP_PROCESSED_DIR, TV_DOWNLOAD_DIR, TVDB_BASE_URL, MIN_SEARCH_FREQUENCY, \ showQueueScheduler, searchQueueScheduler, ROOT_DIRS, \ - NAMING_SHOW_NAME, NAMING_EP_TYPE, NAMING_MULTI_EP_TYPE, CACHE_DIR, ACTUAL_CACHE_DIR, TVDB_API_PARMS, \ + NAMING_DIR_PATTERN, NAMING_NAME_PATTERN, NAMING_MULTI_EP, CACHE_DIR, ACTUAL_CACHE_DIR, TVDB_API_PARMS, \ RENAME_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \ - NAMING_EP_NAME, NAMING_SEP_TYPE, NAMING_USE_PERIODS, WOMBLE, \ - NZBSRUS, NZBSRUS_UID, NZBSRUS_HASH, NAMING_QUALITY, providerList, newznabProviderList, \ - NAMING_DATES, EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ + NZBSRUS, NZBSRUS_UID, NZBSRUS_HASH, WOMBLE, providerList, newznabProviderList, \ + EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ USE_NOTIFO, NOTIFO_USERNAME, NOTIFO_APISECRET, NOTIFO_NOTIFY_ONDOWNLOAD, NOTIFO_NOTIFY_ONSNATCH, \ USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \ USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_SYNOINDEX, \ @@ -498,14 +495,9 @@ def initialize(consoleLogging=True): PROVIDER_ORDER = check_setting_str(CFG, 'General', 'provider_order', '').split() - NAMING_SHOW_NAME = bool(check_setting_int(CFG, 'General', 'naming_show_name', 1)) - NAMING_EP_NAME = bool(check_setting_int(CFG, 'General', 'naming_ep_name', 1)) - NAMING_EP_TYPE = check_setting_int(CFG, 'General', 'naming_ep_type', 0) - NAMING_MULTI_EP_TYPE = check_setting_int(CFG, 'General', 'naming_multi_ep_type', 0) - NAMING_SEP_TYPE = check_setting_int(CFG, 'General', 'naming_sep_type', 0) - NAMING_USE_PERIODS = bool(check_setting_int(CFG, 'General', 'naming_use_periods', 0)) - NAMING_QUALITY = bool(check_setting_int(CFG, 'General', 'naming_quality', 0)) - NAMING_DATES = bool(check_setting_int(CFG, 'General', 'naming_dates', 1)) + NAMING_DIR_PATTERN = check_setting_str(CFG, 'General', 'naming_dir_pattern', '') + NAMING_NAME_PATTERN = check_setting_str(CFG, 'General', 'naming_ep_name', '') + NAMING_MULTI_EP = check_setting_int(CFG, 'General', 'naming_multi_ep', 1) TVDB_BASE_URL = 'http://www.thetvdb.com/api/' + TVDB_API_KEY @@ -1005,14 +997,9 @@ def save_config(): new_config['General']['season_folders_default'] = int(SEASON_FOLDERS_DEFAULT) new_config['General']['provider_order'] = ' '.join([x.getID() for x in providers.sortedProviderList()]) new_config['General']['version_notify'] = int(VERSION_NOTIFY) - new_config['General']['naming_ep_name'] = int(NAMING_EP_NAME) - new_config['General']['naming_show_name'] = int(NAMING_SHOW_NAME) - new_config['General']['naming_ep_type'] = int(NAMING_EP_TYPE) - new_config['General']['naming_multi_ep_type'] = int(NAMING_MULTI_EP_TYPE) - new_config['General']['naming_sep_type'] = int(NAMING_SEP_TYPE) - new_config['General']['naming_use_periods'] = int(NAMING_USE_PERIODS) - new_config['General']['naming_quality'] = int(NAMING_QUALITY) - new_config['General']['naming_dates'] = int(NAMING_DATES) + new_config['General']['naming_ep_name'] = NAMING_DIR_PATTERN + new_config['General']['naming_show_name'] = NAMING_NAME_PATTERN + new_config['General']['naming_multi_ep_type'] = int(NAMING_MULTI_EP) new_config['General']['launch_browser'] = int(LAUNCH_BROWSER) new_config['General']['use_banner'] = int(USE_BANNER) diff --git a/sickbeard/common.py b/sickbeard/common.py index 51f2bf86c98cd94292a711e0d67349be73c19be0..f3ddb43d7ac8f453c2d85ad703c386490f385a46 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -53,6 +53,15 @@ ARCHIVED = 6 # episodes that you don't have locally (counts toward download comp IGNORED = 7 # episodes that you don't want included in your download stats SNATCHED_PROPER = 9 # qualified with quality +NAMING_REPEAT = 1 +NAMING_EXTEND = 2 +NAMING_DUPLICATE = 4 + +multiEpStrings = {} +multiEpStrings[NAMING_REPEAT] = "Repeat" +multiEpStrings[NAMING_DUPLICATE] = "Duplicate" +multiEpStrings[NAMING_EXTEND] = "Extend" + class Quality: NONE = 0 diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index 05b70cf7c749e8f84a1cfb00795c85480c60349b..1edad2f320d9e96a70cfff467cee4a6a6cb4f687 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -356,10 +356,53 @@ class PopulateRootDirs (AddLang): self.incDBVersion() -class AddSizeAndSceneNameFields(SetNzbTorrentSettings): +class SetNzbTorrentSettings(PopulateRootDirs): + def test(self): + return self.checkDBVersion() >= 8 + + def execute(self): + + use_torrents = False + use_nzbs = False + + for cur_provider in sickbeard.providers.sortedProviderList(): + if cur_provider.isEnabled(): + if cur_provider.providerType == GenericProvider.NZB: + use_nzbs = True + logger.log(u"Provider "+cur_provider.name+" is enabled, enabling NZBs in the upgrade") + break + elif cur_provider.providerType == GenericProvider.TORRENT: + use_torrents = True + logger.log(u"Provider "+cur_provider.name+" is enabled, enabling Torrents in the upgrade") + break + + sickbeard.USE_TORRENTS = use_torrents + sickbeard.USE_NZBS = use_nzbs + + sickbeard.save_config() + + self.incDBVersion() + +class FixAirByDateSetting(SetNzbTorrentSettings): + def test(self): return self.checkDBVersion() >= 9 + + def execute(self): + + shows = self.connection.select("SELECT * FROM tv_shows") + + for cur_show in shows: + if cur_show["genre"] and "talk show" in cur_show["genre"].lower(): + self.connection.action("UPDATE tv_shows SET air_by_date = ? WHERE tvdb_id = ?", [1, cur_show["tvdb_id"]]) + + self.incDBVersion() + +class AddSizeAndSceneNameFields(FixAirByDateSetting): + + def test(self): + return self.checkDBVersion() >= 10 def execute(self): @@ -425,45 +468,4 @@ class AddSizeAndSceneNameFields(SetNzbTorrentSettings): break self.incDBVersion() - class SetNzbTorrentSettings(PopulateRootDirs): - def test(self): - return self.checkDBVersion() >= 8 - - def execute(self): - - use_torrents = False - use_nzbs = False - - for cur_provider in sickbeard.providers.sortedProviderList(): - if cur_provider.isEnabled(): - if cur_provider.providerType == GenericProvider.NZB: - use_nzbs = True - logger.log(u"Provider "+cur_provider.name+" is enabled, enabling NZBs in the upgrade") - break - elif cur_provider.providerType == GenericProvider.TORRENT: - use_torrents = True - logger.log(u"Provider "+cur_provider.name+" is enabled, enabling Torrents in the upgrade") - break - - sickbeard.USE_TORRENTS = use_torrents - sickbeard.USE_NZBS = use_nzbs - - sickbeard.save_config() - - self.incDBVersion() - -class FixAirByDateSetting(SetNzbTorrentSettings): - - def test(self): - return self.checkDBVersion() >= 9 - - def execute(self): - - shows = self.connection.select("SELECT * FROM tv_shows") - - for cur_show in shows: - if cur_show["genre"] and "talk show" in cur_show["genre"].lower(): - self.connection.action("UPDATE tv_shows SET air_by_date = ? WHERE tvdb_id = ?", [1, cur_show["tvdb_id"]]) - - self.incDBVersion() diff --git a/sickbeard/tv.py b/sickbeard/tv.py index a2f08116f64dc4a2d5397631135717cc90fbc5b3..fb6f4e4b8e73ee7f8b93b1cd45412e386f15f606 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -44,6 +44,7 @@ from sickbeard import encodingKludge as ek from common import Quality, Overview from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED, UNKNOWN +from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_REPEAT class TVShow(object): @@ -1480,3 +1481,143 @@ class TVEpisode(object): return finalName + def _replace_map(self): + def dot(name): + return name.replace(' ','.') + + def us(name): + return name.replace(' ','_') + + def release_group(name): + if not name or '-' not in name: + return '' + return name.split('-')[-1] + + epStatus, epQual = Quality.splitCompositeStatus(self.status) #@UnusedVariable + + return { + '%SN': self.show.name, + '%S.N': dot(self.show.name), + '%S_N': us(self.show.name), + '%EN': self.name, + '%E.N': dot(self.name), + '%E_N': us(self.name), + '%QN': Quality.qualityStrings[epQual], + '%Q.N': dot(Quality.qualityStrings[epQual]), + '%Q_N': us(Quality.qualityStrings[epQual]), + '%S': str(self.season), + '%0S': '%02d' % self.season, + '%E': str(self.episode), + '%0E': '%02d' % self.episode, + '%RN': self.release_name, + '%RG': release_group(self.release_name), + } + + def _formatted_string(self, pattern=None, multi=None): + + replace_map = self._replace_map() + + if pattern == None: + pattern = sickbeard.NAME_FORMATTING + + if multi == None: + multi = sickbeard.MULTI_FORMAT + + # split off ep name part only + name_groups = re.split(r'[\\/]', pattern) + + result_name = pattern + + # figure out the double-ep naming style for each group, if applicable + for cur_name_group in name_groups: + + season_format = sep = ep_sep = ep_format = None + + season_ep_regex = ''' + (?P<pre_sep>[ _.-]*) + ((?:s(?:eason|eries)?\s*)?%0?S(?![._]?N)) + (.*?) + (%0?E(?![._]?N)) + (?P<post_sep>[ _.-]*) + ''' + ep_only_regex = '(%0?E(?![._]?N))' + + # try the normal way + season_ep_match = re.search(season_ep_regex, cur_name_group, re.I|re.X) + ep_only_match = re.search(ep_only_regex, cur_name_group, re.I|re.X) + + # if we have a season and episode then collect the necessary data + if season_ep_match: + season_format = season_ep_match.group(2) + ep_sep = season_ep_match.group(3) + ep_format = season_ep_match.group(4) + sep = season_ep_match.group('pre_sep') + if not sep: + sep = season_ep_match.group('post_sep') + if not sep: + sep = ' ' + + # force 2-3-4 format if they chose to extend + if multi == NAMING_EXTEND: + ep_sep = '-' + + # if there's no season then there's not much choice so we'll just force them to use 03-04-05 style + elif ep_only_match: + season_format = '' + ep_sep = '-' + ep_format = ep_only_match.group(1) + sep = '' + + # we need at least this much info to continue + if not ep_sep or not ep_format: + continue + + # start with the ep string, eg. E03 + ep_string = replace_map[ep_format.upper()] + for other_eps in self.relatedEps: + if multi == NAMING_DUPLICATE: + # add " - S01" + ep_string += sep + season_format + # add "E04" + ep_string += ep_sep + ep_string += other_eps._replace_map()[ep_format.upper()] + + # fill out the template for this piece and then insert this piece into the actual pattern + cur_name_group_result = cur_name_group.replace(ep_format, ep_string) + result_name = result_name.replace(cur_name_group, cur_name_group_result) + + # do the replacements + for cur_replacement in sorted(replace_map.keys(), reverse=True): + result_name = result_name.replace(cur_replacement, replace_map[cur_replacement]) + result_name = result_name.replace(cur_replacement.lower(), replace_map[cur_replacement].lower()) + + return result_name + + def formatted_dir(self, pattern=None, multi=None): + """ + Just the folder name of the episode + """ + + if pattern == None: + pattern = sickbeard.NAME_FORMATTING + + # split off the dirs only, if they exist + name_groups = re.split(r'[\\/]', pattern) + + if len(name_groups) == 1: + return '' + else: + return self._formatted_string(os.sep.join(name_groups[:-1]), multi) + + def formatted_filename(self, pattern=None, multi=None): + """ + Just the filename of the episode, formatted based on the naming settings + """ + + if pattern == None: + pattern = sickbeard.NAME_FORMATTING + + # split off the filename only, if they exist + name_groups = re.split(r'[\\/]', pattern) + + return self._formatted_string(name_groups[-1], multi) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index fb65fa17d5691801aeba4ca10064c6e9373c57f8..12428aff872dfbeb384f6c75699575d00e0bc2c5 100755 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -39,6 +39,7 @@ from sickbeard import logger, helpers, exceptions, classes, db from sickbeard import encodingKludge as ek from sickbeard import search_queue from sickbeard import image_cache +from sickbeard import naming from sickbeard.providers import newznab from sickbeard.common import Quality, Overview, statusStrings @@ -823,9 +824,7 @@ class ConfigPostProcessing: return _munge(t) @cherrypy.expose - def savePostProcessing(self, season_folders_format=None, naming_show_name=None, naming_ep_type=None, - naming_multi_ep_type=None, naming_ep_name=None, naming_use_periods=None, - naming_sep_type=None, naming_quality=None, naming_dates=None, + def savePostProcessing(self, season_folders_format=None, naming_dir_pattern=None, naming_name_pattern=None, naming_multi_ep=None, xbmc_data=None, mediabrowser_data=None, sony_ps3_data=None, wdtv_data=None, tivo_data=None, use_banner=None, keep_processed_dir=None, process_automatically=None, rename_episodes=None, move_associated_files=None, tv_download_dir=None): @@ -835,31 +834,6 @@ class ConfigPostProcessing: if not config.change_TV_DOWNLOAD_DIR(tv_download_dir): results += ["Unable to create directory " + os.path.normpath(tv_download_dir) + ", dir not changed."] - if naming_show_name == "on": - naming_show_name = 1 - else: - naming_show_name = 0 - - if naming_ep_name == "on": - naming_ep_name = 1 - else: - naming_ep_name = 0 - - if naming_use_periods == "on": - naming_use_periods = 1 - else: - naming_use_periods = 0 - - if naming_quality == "on": - naming_quality = 1 - else: - naming_quality = 0 - - if naming_dates == "on": - naming_dates = 1 - else: - naming_dates = 0 - if use_banner == "on": use_banner = 1 else: @@ -898,14 +872,9 @@ class ConfigPostProcessing: sickbeard.SEASON_FOLDERS_FORMAT = season_folders_format - sickbeard.NAMING_SHOW_NAME = naming_show_name - sickbeard.NAMING_EP_NAME = naming_ep_name - sickbeard.NAMING_USE_PERIODS = naming_use_periods - sickbeard.NAMING_QUALITY = naming_quality - sickbeard.NAMING_DATES = naming_dates - sickbeard.NAMING_EP_TYPE = int(naming_ep_type) - sickbeard.NAMING_MULTI_EP_TYPE = int(naming_multi_ep_type) - sickbeard.NAMING_SEP_TYPE = int(naming_sep_type) + sickbeard.NAMING_DIR_PATTERN = naming_dir_pattern + sickbeard.NAMING_NAME_PATTERN = naming_name_pattern + sickbeard.NAMING_MULTI_EP = int(naming_multi_ep) sickbeard.USE_BANNER = use_banner @@ -922,86 +891,18 @@ class ConfigPostProcessing: redirect("/config/postProcessing/") @cherrypy.expose - def testNaming(self, show_name=None, ep_type=None, multi_ep_type=None, ep_name=None, - sep_type=None, use_periods=None, quality=None, whichTest="single"): - - if show_name == None: - show_name = sickbeard.NAMING_SHOW_NAME - else: - if show_name == "0": - show_name = False - else: - show_name = True - - if ep_name == None: - ep_name = sickbeard.NAMING_EP_NAME - else: - if ep_name == "0": - ep_name = False - else: - ep_name = True - - if use_periods == None: - use_periods = sickbeard.NAMING_USE_PERIODS - else: - if use_periods == "0": - use_periods = False - else: - use_periods = True - - if quality == None: - quality = sickbeard.NAMING_QUALITY - else: - if quality == "0": - quality = False - else: - quality = True - - if ep_type == None: - ep_type = sickbeard.NAMING_EP_TYPE - else: - ep_type = int(ep_type) - - if multi_ep_type == None: - multi_ep_type = sickbeard.NAMING_MULTI_EP_TYPE - else: - multi_ep_type = int(multi_ep_type) - - if sep_type == None: - sep_type = sickbeard.NAMING_SEP_TYPE - else: - sep_type = int(sep_type) - - class TVShow(): - def __init__(self): - self.name = "Show Name" - self.genre = "Comedy" - self.air_by_date = 0 - - # fake a TVShow (hack since new TVShow is coming anyway) - class TVEpisode(tv.TVEpisode): - def __init__(self, season, episode, name): - self.relatedEps = [] - self._name = name - self._season = season - self._episode = episode - self.show = TVShow() - - - # make a fake episode object - ep = TVEpisode(1,2,"Ep Name") - ep._status = Quality.compositeStatus(DOWNLOADED, Quality.HDTV) + def testNaming(self, pattern=None, multi=False): - if whichTest == "multi": - ep._name = "Ep Name (1)" - secondEp = TVEpisode(1,3,"Ep Name (2)") - ep.relatedEps.append(secondEp) + if multi != False: + multi = int(multi) - # get the name - name = ep.prettyName(show_name, ep_type, multi_ep_type, ep_name, sep_type, use_periods, quality) - - return name + result = naming.test_name(pattern, multi) + + result = ek.ek(os.path.join, result['dir'], result['name']) + return result + + class ConfigProviders: @cherrypy.expose