Merge branch 'release/0.11.0'
17
.codeclimate.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
engines:
|
||||
eslint:
|
||||
enabled: true
|
||||
csslint:
|
||||
enabled: true
|
||||
pep8:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.py"
|
||||
- "**.js"
|
||||
- "**.css"
|
||||
exclude_paths:
|
||||
- lib/**/*
|
||||
- tornado/**/*
|
||||
- gui/slick/css/lib/**/*
|
||||
- gui/slick/js/lib/**/*
|
127
CHANGES.md
|
@ -1,7 +1,122 @@
|
|||
### 0.11.0 (2016-01-10 22:30:00 UTC)
|
||||
|
||||
* Change to only refresh scene exception data for shows that need it
|
||||
* Change reduce aggressive use of scene numbering that was overriding user preference where not needed
|
||||
* Change set "Scene numbering" checkbox and add text to the label tip in third step of add "New Show" if scene numbers
|
||||
are found for the selected show in the search results of the first step
|
||||
* Change label text on edit show page to highlight when manual numbering and scene numbers are available
|
||||
* Fix disabling "Scene numbering" of step three in add "New Show" was ignored when scene episode number mappings exist
|
||||
* Fix don't use scene episode number mappings everywhere when "Scene numbering" is disabled for a show
|
||||
* Fix width of legend underlining on the third step used to bring other display elements into alignment
|
||||
* Change when downloading magnet or nzb files, verify the file in cache dir and then move to blackhole
|
||||
* Fix small cosmetic issue to correctly display "full backlog" date
|
||||
* Add search crawler exclusions
|
||||
* Fix saving default show list group on add new show options page
|
||||
* Remove legacy anime split home option from anime settings tab (new option located in general/interface tab)
|
||||
* Remove "Manage Torrents"
|
||||
* Update Beautiful Soup 4.3.2 to 4.4.0 (r390)
|
||||
* Update dateutil library to 2.4.2 (083f666)
|
||||
* Update chardet packages to 2.3.0 (26982c5)
|
||||
* Update Hachoir library 1.3.3 to 1.3.4 (r1383)
|
||||
* Change configure quiet option in Hachoir to suppress warnings (add ref:hacks.txt)
|
||||
* Add parse media content to determine quality before making final assumptions during re-scan, update, pp
|
||||
* Add a postprocess folder name validation
|
||||
* Update Requests library to 2.7.0 (5d6d1bc)
|
||||
* Update SimpleJSON library 3.7.3 to 3.8.0 (a37a9bd)
|
||||
* Update Tornado Web Server 4.2 to 4.3.dev1 (1b6157d)
|
||||
* Update isotope library 2.0.1 to 2.2.2
|
||||
* Update change to suppress reporting of Tornado exception error 1 to updated package (ref:hacks.txt)
|
||||
* Update fix for API response header for JSON content type and the return of JSONP data to updated package (ref:hacks.txt)
|
||||
* Update TvDB API library 1.09 with changes up to (35732c9) and some pep8 and code cleanups
|
||||
* Fix post processing season pack folders
|
||||
* Fix saving torrent provider option "Seed until ratio" after recent refactor
|
||||
* Change white text in light theme on Manage / Episode Status Management page to black for better readability
|
||||
* Change displayShow page episode colours when a minimum quality is met with "End upgrade on first match"
|
||||
* Add seed time per provider for torrent clients that support seed time per torrent, i.e. currently only uTorrent
|
||||
* Remove seed time display for Transmission in config/Torrent Search page because the torrent client doesn't support it
|
||||
* Add PreToMe torrent provider
|
||||
* Add SceneTime torrent provider
|
||||
* Change TtN provider to parse new layout
|
||||
* Improve recognition of SD quality
|
||||
* Fix halting in mid flow of Add Existing Show which resulted in failure to scan statuses and filesizes
|
||||
* Change default de-referrer url to blank
|
||||
* Change javascript urls in templates to allow proper caching
|
||||
* Change downloads to prevent cache misfiring with "Result is not a valid torrent file"
|
||||
* Add BitMeTV torrent provider
|
||||
* Add Torrenting provider
|
||||
* Add FunFile torrent provider
|
||||
* Add TVChaosUK torrent provider
|
||||
* Add HD-Space torrent provider
|
||||
* Add Shazbat torrent provider
|
||||
* Remove unnecessary call to indexers during nameparsing
|
||||
* Change disable ToTV due to non-deletable yet reported hacker BTC inbox scam and also little to no new content listings
|
||||
* Fix Episode View KeyError: 'state-title' failure for shows without a runtime
|
||||
* Update py-unrar2 library 99.3 to 99.6 (2fe1e98)
|
||||
* Fix py-unrar2 on unix to handle different date formats output by different unrar command line versions
|
||||
* Fix Add and Edit show quality selection when Quality 'Custom' is used
|
||||
* Fix add existing shows from folders that contain a plus char
|
||||
* Fix post process issue where items in history were processed out of turn
|
||||
* Change increase frequency of updating show data
|
||||
* Remove Animenzb provider
|
||||
* Change increase the scope and number of non release group text that is identified and removed
|
||||
* Add general config setting to allow adding incomplete show data
|
||||
* Change to throttle connection rate on thread initiation for adba library
|
||||
* Change default manage episodes selector to Snatched episodes if items exist else Wanted on Episode Status Manage page
|
||||
* Change snatched row colour on Episode Status Manage page to match colour used on the show details page
|
||||
* Change replace trakt with libtrakt for API v2
|
||||
* Change improve robustness of Trakt communications
|
||||
* Change Trakt notification config to use PIN authentication with the service
|
||||
* Add multiple Trakt account support to Config/Notifications/Social
|
||||
* Add setting to Trakt notification to update collection with downloaded episode info
|
||||
* Change trakt notifier logo
|
||||
* Remove all other Trakt deprecated API V1 service features pending reconsideration
|
||||
* Change increase show search capability when using plain text and also add TVDB id, IMDb id and IMDb url search
|
||||
* Change improve existing show page and the handling when an attempt to add a show to an existing location
|
||||
* Change consolidate Trakt Trending and Recommended views into an "Add From Trakt" view which defaults to trending
|
||||
* Change Add from Trakt/"Shows:" with Anticipated, New Seasons, New Shows, Popular, Recommendations, and Trending views
|
||||
* Change Add from Trakt/"Shows:" with Most Watched, Played, and Collected during the last month and year on Trakt
|
||||
* Change add season info to "Show: Trakt New Seasons" view on the Add from Trakt page
|
||||
* Change increase number of displayed Trakt shows to 100
|
||||
* Add genres and rating to all Trakt shows
|
||||
* Add AniDb Random and Hot to Add Show page
|
||||
* Add IMDb Popular to Add Show page
|
||||
* Add version to anime renaming pattern
|
||||
* Add Code Climate configuration files
|
||||
* Change move init-scripts to single folder
|
||||
* Change sickbeard variables to sickgear variables in init-scripts
|
||||
* Change improve the use of multiple plex servers
|
||||
* Change move JS code out of home template and into dedicated file
|
||||
* Change remove branch from window title
|
||||
* Change move JS code out of inc_top template and into dedicated file
|
||||
* Change cleanup torrent providers
|
||||
* Change utilise tvdbid for searching usenet providers
|
||||
* Add setting to provider BTN to Reject Blu-ray M2TS releases
|
||||
* Remove jsonrpclib library
|
||||
* Change consolidate global and per show ignore and require words functions
|
||||
* Change "Require word" title and notes on Config Search page to properly describe its functional logic
|
||||
* Add regular expression capability to ignore and require words by starting wordlist with "regex:"
|
||||
* Add list shows with custom ignore and require words under the global counterparts on the Search Settings page
|
||||
* Fix failure to search for more than one selected wanted episode
|
||||
* Add notice for users with Python 2.7.8 or below to update to latest Python
|
||||
* Change position of parsed qualities to the start of log lines
|
||||
* Change to always display branch and commit hash on 'Help & Info' page
|
||||
* Add option to create season search exceptions from editShow page
|
||||
* Change sab to use requests library
|
||||
* Add "View Changes" to tools menu
|
||||
* Change disable connection attempts and remove UI references to the TVRage info source
|
||||
* Change to simplify xem id fetching
|
||||
* Fix issue on Add Existing Shows page where shows were listed that should not have been
|
||||
* Change get_size helper to also handle files
|
||||
* Change improve handling of a bad email notify setting
|
||||
* Fix provider MTV download URL
|
||||
* Change give provider OMGWTFNZBS more time to respond
|
||||
* Change file browser to permit manually entering a path
|
||||
|
||||
|
||||
### 0.10.0 (2015-08-06 11:05:00 UTC)
|
||||
|
||||
* Remove EZRSS provider
|
||||
* Update Tornado webserver to 4.2 (fdfaf3d)
|
||||
* Update Tornado Web Server to 4.2 (fdfaf3d)
|
||||
* Update change to suppress reporting of Tornado exception error 1 to updated package (ref:hacks.txt)
|
||||
* Update fix for API response header for JSON content type and the return of JSONP data to updated package (ref:hacks.txt)
|
||||
* Update Requests library 2.6.2 to 2.7.0 (8b5e457)
|
||||
|
@ -117,7 +232,7 @@
|
|||
|
||||
### 0.9.0 (2015-05-18 14:33:00 UTC)
|
||||
|
||||
* Update Tornado webserver to 4.2.dev1 (609dbb9)
|
||||
* Update Tornado Web Server to 4.2.dev1 (609dbb9)
|
||||
* Update change to suppress reporting of Tornado exception error 1 to updated package as listed in hacks.txt
|
||||
* Update fix for API response header for JSON content type and the return of JSONP data to updated package as listed in hacks.txt
|
||||
* Change network names to only display on top line of Day by Day layout on Episode View
|
||||
|
@ -234,7 +349,7 @@
|
|||
* Hide year, runtime, genre tags, country flag, or status if lacking valid data to display
|
||||
* Remove redundant CSS color use (all browsers treat 3 identical digits as 6, except for possibly in gradients)
|
||||
* Remove whitespace and semi-colon redundancy from CSS shedding 4.5kb
|
||||
* Add show names to items listed during startup in the loading from database phase
|
||||
* Add show names to items listed during startup in the loading from database phase
|
||||
* Add "Enable IMDb info" option to config/General/Interface
|
||||
* Change to not display IMDb info on UI when "Enable IMDb info" is disabled
|
||||
* Change genre tags on displayShow page to link to IMDb instead of Trakt
|
||||
|
@ -321,7 +436,7 @@
|
|||
* Change anime release groups to in memory storage for lowered latency
|
||||
* Change adjust menu delay and hover styling
|
||||
* Fix provider list color
|
||||
* Add handling of exceptional case with missing network name (NoneType) in Episode View
|
||||
* Add handling of exceptional case with missing network name (NoneType) in Episode View
|
||||
* Fix black and white list initialization on new show creation
|
||||
* Add select all and clear all buttons to testRename template
|
||||
* Fix displayShow topmenu variable to point to a valid menu item
|
||||
|
@ -477,7 +592,7 @@
|
|||
* Change Display Show next/previous when show list is not split to loop around
|
||||
* Fix SQL statements that have dynamic table names to use proper syntax
|
||||
* Fix port checking code preventing startup directly after a SG restart
|
||||
* Add a link from the footer number of snatched to episode snatched overview page. The link to the
|
||||
* Add a link from the footer number of snatched to episode snatched overview page. The link to the
|
||||
Episode Overview page is available on all pages except on the Episode Overview page
|
||||
* Change the default state for all check boxes on the Episode Overview page to not checked
|
||||
* Add validation to Go button to ensure at least one item is checked on Episode Overview page
|
||||
|
@ -631,7 +746,7 @@
|
|||
* Add return code from hardlinking error to log
|
||||
* Fix ABD regex for certain filenames
|
||||
* Change miscellaneous UI fixes
|
||||
* Update Tornado webserver to 4.1dev1 and add the certifi lib dependency
|
||||
* Update Tornado Web Server to 4.1dev1 and add the certifi lib dependency
|
||||
* Fix trending shows page from loading full size poster images
|
||||
* Add "Archive on first match" to Manage, Mass Update, Edit Selected page
|
||||
* Fix searching IPTorrentsProvider
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
Libs with customisations...
|
||||
|
||||
/tornado
|
||||
/lib/cachecontrol/caches/file_cache.py
|
||||
/lib/hachoir_core/config.py
|
||||
/lib/pynma/pynma.py
|
||||
/lib/requests/packages/urllib3/connectionpool.py
|
||||
/lib/requests/packages/urllib3/util/ssl_.py
|
||||
/lib/cachecontrol/caches/file_cache.py
|
||||
/lib/pynma/pynma.py
|
||||
/tornado
|
||||
/lib/unrar2/unix.py
|
||||
/lib/tvdb/tvdb_api.py
|
|
@ -340,7 +340,7 @@ class SickGear(object):
|
|||
logger.ERROR)
|
||||
if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
|
||||
logger.log(u'Launching browser and exiting', logger.ERROR)
|
||||
sickbeard.launchBrowser(self.startPort)
|
||||
sickbeard.launch_browser(self.startPort)
|
||||
os._exit(1)
|
||||
|
||||
# Check if we need to perform a restore first
|
||||
|
@ -377,7 +377,7 @@ class SickGear(object):
|
|||
|
||||
# Launch browser
|
||||
if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon):
|
||||
sickbeard.launchBrowser(self.startPort)
|
||||
sickbeard.launch_browser(self.startPort)
|
||||
|
||||
# main loop
|
||||
while True:
|
||||
|
@ -488,7 +488,7 @@ class SickGear(object):
|
|||
sickbeard.halt()
|
||||
|
||||
# save all shows to DB
|
||||
sickbeard.saveAll()
|
||||
sickbeard.save_all()
|
||||
|
||||
# shutdown web server
|
||||
if self.webserver:
|
||||
|
|
|
@ -20,7 +20,7 @@ inc_top.tmpl
|
|||
}
|
||||
|
||||
.browserDialog.busy .ui-dialog-buttonpane{
|
||||
background:url("../images/loading.gif") 10px 50% no-repeat !important
|
||||
background:url("../images/loading32-dark.gif") 10px 50% no-repeat !important
|
||||
}
|
||||
|
||||
.ui-progressbar .ui-progressbar-overlay{
|
||||
|
@ -61,6 +61,8 @@ inc_top.tmpl
|
|||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default{
|
||||
background:#3d3d3d;
|
||||
color:#fff;
|
||||
border:1px solid #111
|
||||
}
|
||||
|
||||
|
@ -326,15 +328,15 @@ ul#rootDirStaticList li{
|
|||
}
|
||||
|
||||
/* =======================================================================
|
||||
home_trendingShows.tmpl
|
||||
home_browseShows.tmpl
|
||||
========================================================================== */
|
||||
|
||||
.traktContainer{
|
||||
.browse-container{
|
||||
background-color:#333;
|
||||
border:1px solid #111
|
||||
}
|
||||
|
||||
.trakt-image{
|
||||
.browse-image{
|
||||
border-bottom:1px solid #111
|
||||
}
|
||||
|
||||
|
@ -526,7 +528,16 @@ h2.day, h2.network{
|
|||
.carousel-indicators .active{
|
||||
background:#8DBEEE;
|
||||
border-color:#8DBEEE
|
||||
}
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
viewchanges.tmpl
|
||||
========================================================================== */
|
||||
#changes{
|
||||
color:rgb(255,255,255);
|
||||
background-color:rgb(61,61,61);
|
||||
border:1px solid rgb(17,17,17)
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
|
@ -626,6 +637,14 @@ div.metadataDiv .disabled{
|
|||
background:url("../images/warning16.png") no-repeat right 5px center #fff
|
||||
}
|
||||
|
||||
.solid-border{
|
||||
border:1px solid #555
|
||||
}
|
||||
|
||||
.solid-border-top{
|
||||
border-top:1px solid #555
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
manage*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -655,6 +674,7 @@ span.path{
|
|||
background-color:#333
|
||||
}
|
||||
|
||||
#addRootDirTable td label .filepath.red-text,
|
||||
.red-text{
|
||||
color:#d33
|
||||
}
|
||||
|
@ -1159,12 +1179,14 @@ input sizing (for config pages)
|
|||
========================================================================== */
|
||||
|
||||
#pickShow optgroup,
|
||||
#showfilter optgroup,
|
||||
#editAProvider optgroup{
|
||||
color:#eee;
|
||||
background-color:rgb(51, 51, 51)
|
||||
}
|
||||
|
||||
#pickShow optgroup option,
|
||||
#showfilter optgroup option,
|
||||
#editAProvider optgroup option{
|
||||
color:#222;
|
||||
background-color:#fff
|
||||
|
@ -1180,7 +1202,7 @@ browser.css
|
|||
|
||||
#fileBrowserDialog ul li a:hover{
|
||||
color:#09a2ff;
|
||||
background:none
|
||||
background: rgb(61, 61, 61) none
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item{
|
||||
|
@ -1214,6 +1236,10 @@ div.stepsguide .step p{
|
|||
color:#646464
|
||||
}
|
||||
|
||||
#newShowPortal #addShowForm .stepsguide .disabledstep:hover > .smalltext{
|
||||
color:#ccc;
|
||||
}
|
||||
|
||||
div.stepsguide .disabledstep p{
|
||||
border-color:#1178B3
|
||||
}
|
||||
|
@ -1444,4 +1470,4 @@ jquery.confirm.css
|
|||
|
||||
#confirmBox .red:hover{
|
||||
background-color:#A13331
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
@ -16,19 +16,19 @@ inc_top.tmpl
|
|||
}
|
||||
|
||||
.browserDialog.busy .ui-dialog-buttonpane{
|
||||
background:url("../images/loading.gif") 10px 50% no-repeat !important
|
||||
background:url("../images/loading32.gif") 10px 50% no-repeat !important
|
||||
}
|
||||
|
||||
.ui-progressbar .ui-progressbar-overlay{
|
||||
background:url("../css/lib/images/animated-overlay.gif")
|
||||
}
|
||||
|
||||
.ui-dialog,
|
||||
.ui-dialog,
|
||||
.ui-dialog-buttonpane{
|
||||
background:#eceadf url("../css/lib/images/ui-bg_fine-grain_10_eceadf_60x60.png") 50% 50% repeat !important
|
||||
}
|
||||
|
||||
.ui-accordion-content,
|
||||
.ui-accordion-content,
|
||||
.ui-tabs-panel{
|
||||
background:#ededed !important;
|
||||
background-image:none !important
|
||||
|
@ -50,41 +50,41 @@ inc_top.tmpl
|
|||
background:#fff url("../css/lib/images/ui-bg_flat_0_ffffff_40x100.png") 50% 50% repeat-x
|
||||
}
|
||||
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default{
|
||||
background:#fff;
|
||||
border:1px solid #CCC
|
||||
}
|
||||
|
||||
.ui-state-hover,
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-state-hover,
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus{
|
||||
background:#fff
|
||||
}
|
||||
|
||||
.ui-state-active,
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-state-active,
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active{
|
||||
background:#F7F7F7
|
||||
}
|
||||
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight{
|
||||
background:#fbf9ee url("../css/lib/images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x
|
||||
}
|
||||
|
||||
.ui-state-error,
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-state-error,
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error{
|
||||
background:#fef1ec url("../css/lib/images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x
|
||||
}
|
||||
|
||||
.ui-icon,
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon{
|
||||
background-image:url("../css/lib/images/ui-icons_222222_256x240.png")
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ inc_top.tmpl
|
|||
background-image:url("../css/lib/images/ui-icons_8c291d_256x240.png")
|
||||
}
|
||||
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon{
|
||||
background-image:url("../css/lib/images/ui-icons_222222_256x240.png")
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ inc_top.tmpl
|
|||
background-image:url("../css/lib/images/ui-icons_2e83ff_256x240.png")
|
||||
}
|
||||
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon{
|
||||
background-image:url("../css/lib/images/ui-icons_cd0a0a_256x240.png")
|
||||
}
|
||||
|
@ -327,15 +327,15 @@ ul#rootDirStaticList li{
|
|||
}
|
||||
|
||||
/* =======================================================================
|
||||
home_trendingShows.tmpl
|
||||
home_browseShows.tmpl
|
||||
========================================================================== */
|
||||
|
||||
.traktContainer{
|
||||
.browse-container{
|
||||
background-color:#DFDACF;
|
||||
border:1px solid #111
|
||||
}
|
||||
|
||||
.trakt-image{
|
||||
.browse-image{
|
||||
border-bottom:1px solid #111
|
||||
}
|
||||
|
||||
|
@ -345,7 +345,7 @@ home_postprocess.tmpl
|
|||
|
||||
|
||||
/* =======================================================================
|
||||
displayShow.tmpl
|
||||
displayShow.tmpl
|
||||
========================================================================== */
|
||||
|
||||
tr.seasonheader{
|
||||
|
@ -508,7 +508,16 @@ h2.day, h2.network{
|
|||
.carousel-indicators .active{
|
||||
background:#C7DB40;
|
||||
border-color:#C7DB40
|
||||
}
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
viewchanges.tmpl
|
||||
========================================================================== */
|
||||
#changes{
|
||||
color:rgb(51,51,51);
|
||||
background-color:rgb(245,245,245);
|
||||
border:1px solid rgb(204,204,204)
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
|
@ -601,6 +610,14 @@ div.metadataDiv .disabled{
|
|||
background:url("../images/warning16.png") no-repeat right 5px center #fff
|
||||
}
|
||||
|
||||
.solid-border{
|
||||
border:1px solid #ccc
|
||||
}
|
||||
|
||||
.solid-border-top{
|
||||
border-top:1px solid #ccc
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
manage*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -610,7 +627,7 @@ manage*.tmpl
|
|||
}
|
||||
|
||||
a.whitelink{
|
||||
color:#fff
|
||||
color:#000
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
|
@ -630,6 +647,7 @@ span.path{
|
|||
background-color:#f5f1e4
|
||||
}
|
||||
|
||||
#addRootDirTable td label .filepath.red-text,
|
||||
.red-text{
|
||||
color:#d33
|
||||
}
|
||||
|
@ -1125,12 +1143,14 @@ input sizing (for config pages)
|
|||
========================================================================== */
|
||||
|
||||
#pickShow optgroup,
|
||||
#showfilter optgroup,
|
||||
#editAProvider optgroup{
|
||||
color:#eee;
|
||||
background-color:#888
|
||||
}
|
||||
|
||||
#pickShow optgroup option,
|
||||
#showfilter optgroup option,
|
||||
#editAProvider optgroup option{
|
||||
color:#222;
|
||||
background-color:#fff
|
||||
|
@ -1142,7 +1162,7 @@ browser.css
|
|||
|
||||
#fileBrowserDialog ul li a:hover{
|
||||
color:#00f;
|
||||
background:none
|
||||
background: rgb(220, 220, 220) none
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item{
|
||||
|
@ -1180,6 +1200,10 @@ div.stepsguide .disabledstep p{
|
|||
border-color:#8a775e
|
||||
}
|
||||
|
||||
#newShowPortal #addShowForm .stepsguide .disabledstep:hover > .smalltext{
|
||||
color:#8a775e;
|
||||
}
|
||||
|
||||
div.formpaginate .prev, div.formpaginate .next{
|
||||
color:#fff;
|
||||
background:#57442b
|
||||
|
@ -1378,4 +1402,4 @@ jquery.confirm.css
|
|||
|
||||
#confirmBox .red:hover{
|
||||
background-color:#A13331
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ inc_top.tmpl
|
|||
background:url("../css/lib/images/animated-overlay.gif")
|
||||
}
|
||||
|
||||
.ui-dialog,
|
||||
.ui-dialog,
|
||||
.ui-dialog-buttonpane{
|
||||
background:#eceadf url("../css/lib/images/ui-bg_fine-grain_10_eceadf_60x60.png") 50% 50% repeat !important
|
||||
}
|
||||
|
@ -921,7 +921,7 @@ home_newShow.tmpl
|
|||
#newShowPortal,
|
||||
fieldset.sectionwrap,
|
||||
div.formpaginate{
|
||||
width:801px
|
||||
width:831px
|
||||
}
|
||||
|
||||
#addShowForm{
|
||||
|
@ -1054,25 +1054,25 @@ ul#rootDirStaticList li input[type="checkbox"]{
|
|||
}
|
||||
|
||||
/* =======================================================================
|
||||
home_trendingShows.tmpl
|
||||
home_browseShows.tmpl
|
||||
========================================================================== */
|
||||
|
||||
.traktShowTitleIcons{
|
||||
.browse-add-show-holder{
|
||||
float:right;
|
||||
padding-right:4px;
|
||||
padding-bottom:4px
|
||||
}
|
||||
|
||||
.traktContainer p{
|
||||
.browse-container p{
|
||||
padding-top:2px
|
||||
}
|
||||
|
||||
.traktContainer p img{
|
||||
.browse-container p img{
|
||||
position:relative;
|
||||
top:-2px
|
||||
}
|
||||
|
||||
.traktContainer p, .traktContainer i{
|
||||
.browse-container p, .browse-container i{
|
||||
white-space:nowrap;
|
||||
font-size:12px;
|
||||
overflow:hidden;
|
||||
|
@ -1080,15 +1080,16 @@ home_trendingShows.tmpl
|
|||
margin:0
|
||||
}
|
||||
|
||||
.traktContainer{
|
||||
margin:12px;
|
||||
.browse-container{
|
||||
margin:12px 12px 12px 0;
|
||||
width:188px;
|
||||
background-color:#DFDACF;
|
||||
border:1px solid #111;
|
||||
border-radius:6px
|
||||
}
|
||||
|
||||
.trakt-image{
|
||||
.browse-image{
|
||||
display:block;
|
||||
overflow:hidden;
|
||||
height:273px;
|
||||
width:186px;
|
||||
|
@ -1666,6 +1667,10 @@ td.col-search{
|
|||
padding:15px 0 0
|
||||
}
|
||||
|
||||
#addShowForm #editShow.stepDiv span.component-desc{
|
||||
width:639px
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
episodeView.tmpl
|
||||
========================================================================== */
|
||||
|
@ -2098,6 +2103,36 @@ td.col-cache{
|
|||
width:20px
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
viewchanges.tmpl
|
||||
========================================================================== */
|
||||
#changes{
|
||||
display:block;
|
||||
padding:9.5px;
|
||||
border-radius:4px 4px 4px 4px;
|
||||
font:12px/13px "Open Sans",verdana,sans-serif
|
||||
}
|
||||
|
||||
#changes .release{
|
||||
margin:15px 5px 6px 0;
|
||||
padding-bottom:3px;
|
||||
border-bottom:1px solid gray
|
||||
}
|
||||
|
||||
#changes .ver{font:16px/17px "Open Sans",verdana,sans-serif;margin-right:0.2em;}
|
||||
#changes .old{padding-top:15px}
|
||||
|
||||
#changes div{margin:0 0 8px}
|
||||
#changes .btn-text{width:5em;margin-right:0.2em;float:left}
|
||||
#changes .change-text{display:block;margin-left:5.5em;padding-top:2px}
|
||||
|
||||
.change-add{background-color:rgb(63,127,0)}
|
||||
.change-change{background-color:rgb(91,153,13)}
|
||||
.change-fix{background-color:rgb(38,114,182)}
|
||||
.change-port{background-color:rgb(102,102,102)}
|
||||
.change-remove{}
|
||||
.change-update{background-color:rgb(98,25,147)}
|
||||
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
|
@ -2524,6 +2559,32 @@ div.metadataDiv .disabled{
|
|||
margin:6px 4px 0 0
|
||||
}
|
||||
|
||||
#trakt-collection th,#trakt-collection td{
|
||||
padding:3px 5px
|
||||
}
|
||||
|
||||
#trakt-collection .col-1{
|
||||
text-align:left
|
||||
}
|
||||
|
||||
#trakt-collection th,#trakt-collection td.opt{
|
||||
text-align:center
|
||||
}
|
||||
|
||||
#trakt-collection .col-1{
|
||||
width:192px
|
||||
}
|
||||
|
||||
#config #trakt-collection input{
|
||||
float:none;
|
||||
margin:0;
|
||||
vertical-align:middle
|
||||
}
|
||||
|
||||
#config .trakt.component-desc{
|
||||
margin-left:0
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
manage*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -2625,6 +2686,7 @@ span.path{
|
|||
line-height:18px
|
||||
}
|
||||
|
||||
span.btn-text,
|
||||
span.quality{
|
||||
font:12px/13px "Open Sans", verdana, sans-serif;
|
||||
background-image:-webkit-linear-gradient(top, rgba(255, 255, 255, 0.08),rgba(255, 255, 255, 0) 50%,rgba(0, 0, 0, 0) 50%,rgba(0, 0, 0, 0.25));
|
||||
|
@ -2970,6 +3032,10 @@ fieldset[disabled] .navbar-default .btn-link:focus{
|
|||
display:inline
|
||||
}
|
||||
|
||||
#fileBrowserDialog .form-control{background-color:#f5f1e4}
|
||||
#fileBrowserDialog .form-control:active,
|
||||
#fileBrowserDialog .form-control:hover{background-color:#ffffca}
|
||||
|
||||
.btn{
|
||||
display:inline-block;
|
||||
*display:inline;
|
||||
|
@ -3486,7 +3552,7 @@ div.stepsguide{
|
|||
|
||||
div.stepsguide .step{
|
||||
float:left;
|
||||
width:267px;
|
||||
width:277px;
|
||||
font:bold 24px Arial
|
||||
}
|
||||
|
||||
|
@ -3880,7 +3946,7 @@ jquery.confirm.css
|
|||
box-shadow:inset 0 1px rgba(255, 255, 255, 0.1),inset 0 -1px 3px rgba(0, 0, 0, 0.3),inset 0 0 0 1px rgba(255, 255, 255, 0.08),0 1px 2px rgba(0, 0, 0, 0.15)
|
||||
}
|
||||
|
||||
#confirmBox .button:last-child{
|
||||
#confirmBox .button:last-child{
|
||||
margin-right:0
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 726 B |
BIN
gui/slick/images/providers/bitmetv.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
0
gui/slick/images/providers/freshontv.png
Executable file → Normal file
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
gui/slick/images/providers/funfile.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
gui/slick/images/providers/hdspace.png
Normal file
After Width: | Height: | Size: 752 B |
BIN
gui/slick/images/providers/pretome.png
Normal file
After Width: | Height: | Size: 860 B |
BIN
gui/slick/images/providers/scenetime.png
Normal file
After Width: | Height: | Size: 583 B |
BIN
gui/slick/images/providers/shazbat.png
Normal file
After Width: | Height: | Size: 557 B |
BIN
gui/slick/images/providers/torrenting.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 588 B |
BIN
gui/slick/images/providers/tvchaosuk.png
Normal file
After Width: | Height: | Size: 1,002 B |
|
@ -8,8 +8,8 @@
|
|||
sbRoot = "$sbRoot";
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/apibuilder.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/apibuilder.js?v=$sbPID"></script>
|
||||
|
||||
<style type="text/css">
|
||||
<!--
|
||||
|
|
|
@ -8,36 +8,23 @@
|
|||
#set global $sbPath = '..'
|
||||
#set global $topmenu = 'config'
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
##
|
||||
#if $varExists('header')
|
||||
<h1 class='header'>$header</h1>
|
||||
#else
|
||||
<h1 class='title'>$title</h1>
|
||||
#end if
|
||||
|
||||
##set cpu_usage = $psutil.cpu_percent()
|
||||
##set ram = $psutil.phymem_usage()
|
||||
##set ram_total = $ram.total / 2**20
|
||||
##set ram_used = $ram.used / 2**20
|
||||
##set ram_free = $ram.free / 2**20
|
||||
##set ram_percent_used = $ram.percent
|
||||
##set disk = $psutil.disk_usage('/')
|
||||
##set disk_total = $disk.total / 2**30
|
||||
##set disk_used = $disk.used / 2**30
|
||||
##set disk_free = $disk.free / 2**30
|
||||
##set disk_percent_used = $disk.percent
|
||||
##
|
||||
<div id="config-content">
|
||||
<table class="infoTable" cellspacing="1" border="0" cellpadding="0" width="100%">
|
||||
<tr>
|
||||
<td class="infoTableHeader">Version: </td>
|
||||
<td class="infoTableCell">
|
||||
#if $sickbeard.VERSION_NOTIFY
|
||||
BRANCH: ($sickbeard.BRANCH) / COMMIT: ($sickbeard.CUR_COMMIT_HASH) <!-- – build.date //--><br />
|
||||
#else
|
||||
You don't have version checking turned on, see "Check software updates" in Config > General.<br />
|
||||
BRANCH: #echo $sickbeard.BRANCH or 'UNKNOWN'# / COMMIT: #echo $sickbeard.CUR_COMMIT_HASH or 'UNKNOWN'#<br />
|
||||
<em class="red-text">This is BETA software</em><br />
|
||||
#if not $sickbeard.VERSION_NOTIFY:
|
||||
You don't have version checking turned on, see "Check software updates" in Config > General.
|
||||
#end if
|
||||
<em class="red-text">This is BETA software.</em>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td class="infoTableHeader">Config file:</td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
##
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
@ -36,16 +36,6 @@
|
|||
</div>
|
||||
|
||||
<fieldset class="component-group-list">
|
||||
<div class="field-pair">
|
||||
<label for="split_home">
|
||||
<span class="component-title">Split show lists</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" class="enabler" name="split_home" id="split_home" #if $sickbeard.ANIME_SPLIT_HOME then 'checked="checked"' else ""# />
|
||||
<p>separate anime from other shows on the home page</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="anime_treat_as_hdtv">
|
||||
<span class="component-title">Quality control</span>
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
#set $indexer = $sickbeard.INDEXER_DEFAULT
|
||||
#end if
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?v=$sbPID"></script>
|
||||
|
||||
<div id="config">
|
||||
<div id="config-content">
|
||||
|
@ -72,7 +72,7 @@
|
|||
<span class="component-title">Update shows on startup</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="update_shows_on_start" id="update_shows_on_start"#echo ('', $checked)[$sickbeard.UPDATE_SHOWS_ON_START]#>
|
||||
<p>with show data; episode plot, images, air and end dates, etc. Disable for a quicker startup. Show data is scheduled to update during hour <span class="show_update_hour_value">$sickbeard.SHOW_UPDATE_HOUR</span>.</p>
|
||||
<p>with show data; episode plot, images, air and end dates, etc. Disable for a quicker startup. Show data is scheduled to update during hour <span class="show_update_hour_value">$sickbeard.SHOW_UPDATE_HOUR</span></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -86,7 +86,19 @@
|
|||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
#if hasattr($sickbeard, 'ALLOW_INCOMPLETE_SHOWDATA')
|
||||
<div class="field-pair">
|
||||
<label for="allow_incomplete_showdata">
|
||||
<span class="component-title">Allow incomplete show data</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="allow_incomplete_showdata" id="allow_incomplete_showdata"#echo ('', $checked)[$sickbeard.ALLOW_INCOMPLETE_SHOWDATA]#>
|
||||
<p>add partial show data for future updates to complete</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
#end if
|
||||
|
||||
<div class="field-pair">
|
||||
<span class="component-title">Send to trash for actions</span>
|
||||
<span class="component-desc">
|
||||
|
@ -110,22 +122,22 @@
|
|||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#if 1 < $len($indexers)
|
||||
<div class="field-pair">
|
||||
<label for="indexer_default">
|
||||
<span class="component-title">Use initial indexer set to</span>
|
||||
<span class="component-desc">
|
||||
<select id="indexer_default" name="indexer_default" class="form-control input-sm">
|
||||
<option value="0"#echo ('', $selected)[0 == $indexer]#>All Indexers</option>
|
||||
#for $indexer in $sickbeard.indexerApi().indexers
|
||||
#for $indexer in $indexers
|
||||
<option value="$indexer"#echo ('', $selected)[$indexer == $sickbeard.INDEXER_DEFAULT]#>$sickbeard.indexerApi().indexers[$indexer]</option>
|
||||
#end for
|
||||
#end for
|
||||
</select>
|
||||
<span>as the default selection when adding new shows</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#end if
|
||||
<div class="field-pair">
|
||||
<label for="indexer_timeout">
|
||||
<span class="component-title">Timeout show indexer at</span>
|
||||
|
@ -624,7 +636,7 @@
|
|||
<span class="component-title">Use proxy for indexers</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="proxy_indexers" id="proxy_indexers"#echo ('', $checked)[True == $sickbeard.PROXY_INDEXERS]#>
|
||||
<p>use proxy host for connecting to indexers (thetvdb, tvrage)</p>
|
||||
<p>use proxy host for TV info source connections</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#import base64
|
||||
#import sickbeard
|
||||
#import re
|
||||
#from lib.libtrakt import TraktAPI
|
||||
#from sickbeard.helpers import anon_url, starify
|
||||
##
|
||||
#set global $title = 'Config - Notifications'
|
||||
|
@ -9,12 +11,12 @@
|
|||
##
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/configNotifications.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/configNotifications.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
|
@ -154,7 +156,7 @@
|
|||
|
||||
</div><!-- /xbmc component-group //-->
|
||||
|
||||
<div class="component-group">
|
||||
<div class="component-group">
|
||||
<div class="component-group-desc">
|
||||
<img class="notifier-icon" src="$sbRoot/images/notifiers/kodi.png" alt="" title="Kodi" />
|
||||
<h3><a href="<%= anon_url('http://kodi.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Kodi</a></h3>
|
||||
|
@ -274,7 +276,7 @@
|
|||
</div><!-- /content_use_kodi //-->
|
||||
</fieldset>
|
||||
</div><!-- /kodi component-group //-->
|
||||
|
||||
|
||||
<div class="component-group">
|
||||
<div class="component-group-desc">
|
||||
<img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" />
|
||||
|
@ -1465,7 +1467,7 @@
|
|||
<div class="component-group-desc">
|
||||
<img class="notifier-icon" src="$sbRoot/images/notifiers/trakt.png" alt="" title="Trakt"/>
|
||||
<h3><a href="<%= anon_url('http://trakt.tv/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Trakt</a></h3>
|
||||
<p>trakt helps keep a record of what TV shows and movies you are watching. Based on your favorites, trakt recommends additional shows and movies you'll enjoy!</p>
|
||||
<p>Trakt can keep a record of what TV shows you are watching and recommend additional shows based on your show data.</p>
|
||||
</div>
|
||||
<fieldset class="component-group-list">
|
||||
<div class="field-pair">
|
||||
|
@ -1473,42 +1475,85 @@
|
|||
<span class="component-title">Enable</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" class="enabler" name="use_trakt" id="use_trakt" #if $sickbeard.USE_TRAKT then 'checked="checked"' else ''# />
|
||||
<p>should SickGear send Trakt.tv notifications ?</p>
|
||||
<p>should SickGear use Trakt.tv ?</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="content_use_trakt">
|
||||
<div class="field-pair">
|
||||
<label for="trakt_username">
|
||||
<span class="component-title">Trakt username</span>
|
||||
<input type="text" name="trakt_username" id="trakt_username" value="$sickbeard.TRAKT_USERNAME" class="form-control input-sm input250" />
|
||||
<label for="trakt_accounts">
|
||||
<span class="component-title">Trakt account (status):</span>
|
||||
<span class="component-desc">
|
||||
<select name="trakt_accounts" id="trakt_accounts" class="pull-left form-control input-sm">
|
||||
<option value="new" selected="selected">Add account</option>
|
||||
#set $trakt_accounts = $sickbeard.TRAKT_ACCOUNTS
|
||||
#for $void, $account in $trakt_accounts.items()
|
||||
<option value="$account.account_id">$account.account_id - $account.name #if $account.active then '(ok)' else '(inactive)'#</option>
|
||||
#end for
|
||||
</select>
|
||||
<input type="button" class="btn" value="Delete" id="trakt-delete" disabled="disabled" />
|
||||
</span>
|
||||
</label>
|
||||
<label>
|
||||
<span class="component-title"> </span>
|
||||
<span class="component-desc">username of your Trakt account.</span>
|
||||
<label for="trakt_pin">
|
||||
<span class="component-title">Trakt PIN:</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="trakt_pin" id="trakt_pin" value="" class="form-control input-sm input250" />
|
||||
<input type="button" class="btn" value="Connect" id="trakt-authenticate" />
|
||||
<div class="clear-left"><p>get an active PIN using: <a href="<%= anon_url(sickbeard.TRAKT_PIN_URL) %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><b>$sickbeard.TRAKT_PIN_URL</b></a>
|
||||
<br />tip: easily add accounts by using the link in other browsers "signed in" to Trakt
|
||||
</p></div>
|
||||
</span>
|
||||
</label>
|
||||
<div class="testNotification" id="trakt-authentication-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="trakt_password">
|
||||
<span class="component-title">Trakt password</span>
|
||||
<input type="password" name="trakt_password" id="trakt_password" value="#echo '*' * len($sickbeard.TRAKT_PASSWORD)#" class="form-control input-sm input250" />
|
||||
</label>
|
||||
<label>
|
||||
<span class="component-title"> </span>
|
||||
<span class="component-desc">password of your Trakt account.</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label for="trakt_api">
|
||||
<span class="component-title">Trakt API key:</span>
|
||||
<input type="text" name="trakt_api" id="trakt_api" value="<%= starify(sickbeard.TRAKT_API) %>" class="form-control input-sm input250" />
|
||||
</label>
|
||||
<label>
|
||||
<span class="component-title"> </span>
|
||||
<span class="component-desc">get your key at: <a href="<%= anon_url('http://trakt.tv/settings/api') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;"><b>http://trakt.tv/settings/api</b></a></span>
|
||||
</label>
|
||||
<span class="trakt component-desc" style="width:100%">
|
||||
#set num_accounts = len($trakt_accounts)
|
||||
#set $num_columns = (1, num_accounts)[1 < num_accounts]
|
||||
<table id="trakt-collection" class="solid-border" cellpadding="0" cellspacing="0" border="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-1" style="font-size:12px;font-weight:normal" rowspan="2"><i>Update multiple accounts with downloaded episode info</i></th>
|
||||
<th colspan="$num_columns">#echo not len($trakt_accounts) and '<i>Connect New Pin</i>' or 1 < len($trakt_accounts) and 'Trakt accounts' or 'Account'#</th>
|
||||
</tr>
|
||||
<tr>
|
||||
#if not len($trakt_accounts)
|
||||
<th>..</th>
|
||||
#end if
|
||||
#for $void, $account in $trakt_accounts.items()
|
||||
<th class="tid-$account.account_id">$account.name#if $account.active then '' else '<br />(inactive)'#</th>
|
||||
#end for
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
#if not $root_dirs:
|
||||
#set $root_dirs = [{'root_def': False, 'loc': 'all folders. Multiple parent folders will appear here.', 'b64': ''}]
|
||||
#end if
|
||||
#for $root_info in $root_dirs:
|
||||
<tr class="solid-border-top">
|
||||
<td class="col-1"><span style="font-size:13px;font-weight:bold" data-loc="$root_info['b64']">Update collection</span></td>
|
||||
#if not len($trakt_accounts)
|
||||
<td class="opt">..</td>
|
||||
#end if
|
||||
#for $void, $account in $trakt_accounts.items()
|
||||
#set $cur_selected = ('', ' checked="checked"')[$root_info['loc'] in $sickbeard.TRAKT_UPDATE_COLLECTION.get($account.account_id, '')]
|
||||
#set $id_loc = "update_trakt_%s_%s" % ($account.account_id, $root_info['b64'])
|
||||
<td class="opt">
|
||||
<input type="checkbox" id="$id_loc" name="$id_loc"$cur_selected />
|
||||
</td>
|
||||
#end for
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="${1 + $num_columns}">for #if $root_info['root_def'] then '*' else ''#$root_info['loc']</td>
|
||||
</tr>
|
||||
#end for
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
</div>
|
||||
<!--
|
||||
<div class="field-pair">
|
||||
<label for="trakt_default_indexer">
|
||||
<span class="component-title">Default indexer:</span>
|
||||
|
@ -1582,8 +1627,7 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="testNotification" id="testTrakt-result">Click below to test.</div>
|
||||
<input type="button" class="btn" value="Test Trakt" id="testTrakt" />
|
||||
-->
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
</div><!-- /content_use_trakt //-->
|
||||
</fieldset>
|
||||
|
@ -1743,4 +1787,4 @@
|
|||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/configPostProcessing.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/configPostProcessing.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
|
@ -1073,6 +1073,11 @@
|
|||
<td>%RT</td>
|
||||
<td>PROPER</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="align-right"><b>Version:</b></td>
|
||||
<td>%V</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/configProviders.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/configProviders.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
|
||||
#set $methods_notused = []
|
||||
#if not $sickbeard.USE_NZBS
|
||||
|
@ -281,7 +281,7 @@
|
|||
<span class="component-desc">
|
||||
#set $field_name = curNzbProvider.get_id() + '_api_key'
|
||||
<input type="text" name="$field_name" value="<%= starify(curNzbProvider.api_key) %>" class="form-control input-sm input350" />
|
||||
#if callable(getattr(curNzbProvider, 'ui_string'))
|
||||
#if callable(getattr(curNzbProvider, 'ui_string', None))
|
||||
<div class="clear-left"><p>${curNzbProvider.ui_string($field_name)}</p></div>
|
||||
#end if
|
||||
</span>
|
||||
|
@ -347,6 +347,17 @@
|
|||
##
|
||||
#for $curTorrentProvider in [$curProvider for $curProvider in $sickbeard.providers.sortedProviderList() if $curProvider.providerType == $GenericProvider.TORRENT]:
|
||||
<div class="providerDiv" id="${curTorrentProvider.get_id()}Div">
|
||||
#if callable(getattr(curTorrentProvider, 'ui_string', None))
|
||||
#set $field_name = curTorrentProvider.get_id() + '_tip'
|
||||
#set $tip_text = curTorrentProvider.ui_string($field_name)
|
||||
#if $tip_text
|
||||
<div class="field-pair">
|
||||
<span class="component-desc" style="margin:0;width:100%">
|
||||
<div class="clear-left"><p class="grey-text"><span class="red-text">Important! ${curTorrentProvider.name}</span> $tip_text</p></div>
|
||||
</span>
|
||||
</div>
|
||||
#end if
|
||||
#end if
|
||||
#if $hasattr($curTorrentProvider, 'api_key'):
|
||||
<div class="field-pair">
|
||||
<label for="${curTorrentProvider.get_id()}_api_key">
|
||||
|
@ -360,9 +371,13 @@
|
|||
#if $hasattr($curTorrentProvider, 'digest'):
|
||||
<div class="field-pair">
|
||||
<label for="${curTorrentProvider.get_id()}_digest">
|
||||
<span class="component-title">Digest:</span>
|
||||
<span class="component-title">Cookies:</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="${curTorrentProvider.get_id()}_digest" id="${curTorrentProvider.get_id()}_digest" value="$curTorrentProvider.digest" class="form-control input-sm input350" />
|
||||
#set $field_name = curTorrentProvider.get_id() + '_digest'
|
||||
<input type="text" name="$field_name" id="$field_name" value="<%= starify(curTorrentProvider.digest) %>" class="form-control input-sm input350" />
|
||||
#if callable(getattr(curTorrentProvider, 'ui_string', None))
|
||||
<div class="clear-left"><p>${curTorrentProvider.ui_string($field_name)}</p></div>
|
||||
#end if
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -392,7 +407,7 @@
|
|||
<label for="${curTorrentProvider.get_id()}_password">
|
||||
<span class="component-title">Password:</span>
|
||||
<span class="component-desc">
|
||||
<input type="password" name="${curTorrentProvider.get_id()}_password" id="${curTorrentProvider.get_id()}_password" value="#echo '*' * len($curTorrentProvider.password)#" class="form-control input-sm input350" />
|
||||
<input type="password" name="${curTorrentProvider.get_id()}_password" id="${curTorrentProvider.get_id()}_password" value="#echo $curTorrentProvider.password and '*' * len($curTorrentProvider.password) or ''#" class="form-control input-sm input350" />
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -408,14 +423,28 @@
|
|||
</div>
|
||||
#end if
|
||||
#if $hasattr($curTorrentProvider, '_seed_ratio') and 'blackhole' != $sickbeard.TORRENT_METHOD:
|
||||
#set $torrent_method_text = {'blackhole': 'Black hole', 'utorrent': 'uTorrent', 'transmission': 'Transmission', 'deluge': 'Deluge', 'download_station': 'Synology DS', 'rtorrent': 'rTorrent'}
|
||||
#set $torrent_method_text = {'utorrent': 'uTorrent', 'transmission': 'Transmission', 'deluge': 'Deluge', 'download_station': 'Synology DS', 'rtorrent': 'rTorrent'}
|
||||
<div class="field-pair">
|
||||
<label for="${curTorrentProvider.get_id()}_ratio">
|
||||
<span class="component-title" id="${curTorrentProvider.get_id()}_ratio_desc">Seed until ratio (the goal)</span>
|
||||
<span class="component-desc">
|
||||
<input type="number" step="0.1" name="${curTorrentProvider.get_id()}_ratio" id="${curTorrentProvider.get_id()}_ratio" value="$curTorrentProvider._seed_ratio" class="form-control input-sm input75" />
|
||||
<p>this ratio is requested of each download sent to $torrent_method_text[$sickbeard.TORRENT_METHOD]</p>
|
||||
<div class="clear-left"><p>(set -1 to seed forever, or leave blank for the $torrent_method_text[$sickbeard.TORRENT_METHOD] default)</p></div>
|
||||
<input type="number" name="${curTorrentProvider.get_id()}_ratio" id="${curTorrentProvider.get_id()}_ratio" value="$curTorrentProvider._seed_ratio" class="form-control input-sm input75" />
|
||||
<p>this ratio is requested of each item sent to $torrent_method_text[$sickbeard.TORRENT_METHOD]</p>
|
||||
<div class="clear-left"><p>(#if 'Transmission' in $torrent_method_text[$sickbeard.TORRENT_METHOD]#set -1 to seed forever, or #end if#leave blank for the $torrent_method_text[$sickbeard.TORRENT_METHOD] setting)</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
#end if
|
||||
#if $hasattr($curTorrentProvider, 'seed_time') and 'utorrent' == $sickbeard.TORRENT_METHOD:
|
||||
#set $torrent_method_text = {'utorrent': 'uTorrent'}
|
||||
#set $use_default = 'to use the %s min <a href="%s/config/search/#core-component-group3">torrent search setting minumum default</a>' % ($sickbeard.TORRENT_SEED_TIME, $sbRoot) if $sickbeard.TORRENT_SEED_TIME else 'for the %s setting' % $torrent_method_text[$sickbeard.TORRENT_METHOD]
|
||||
<div class="field-pair">
|
||||
<label for="${curTorrentProvider.get_id()}_seed_time">
|
||||
<span class="component-title" id="${curTorrentProvider.get_id()}_seed_time_desc">Seed time (provider default)</span>
|
||||
<span class="component-desc">
|
||||
<input type="number" name="${curTorrentProvider.get_id()}_seed_time" id="${curTorrentProvider.get_id()}_seed_time" value="$curTorrentProvider.seed_time" class="form-control input-sm input75" />
|
||||
<p>set 1 or more minimum minutes for each item sent to $torrent_method_text[$sickbeard.TORRENT_METHOD]</p>
|
||||
<div class="clear-left"><p>(leave blank $use_default)</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -489,6 +518,17 @@
|
|||
</label>
|
||||
</div>
|
||||
#end if
|
||||
#if $hasattr($curTorrentProvider, 'reject_m2ts'):
|
||||
<div class="field-pair">
|
||||
<label for="${curTorrentProvider.get_id()}_reject_m2ts">
|
||||
<span class="component-title">Reject Blu-ray M2TS releases</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="${curTorrentProvider.get_id()}_reject_m2ts" id="${curTorrentProvider.get_id()}_reject_m2ts" <%= html_checked if curTorrentProvider.reject_m2ts else '' %>/>
|
||||
<p>enable to ignore Blu-ray MPEG-2 Transport Stream container releases</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
#end if
|
||||
#if $hasattr($curTorrentProvider, 'enable_recentsearch') and $curTorrentProvider.supportsBacklog:
|
||||
<div class="field-pair">
|
||||
<label for="${curTorrentProvider.get_id()}_enable_recentsearch">
|
||||
|
@ -707,4 +747,4 @@
|
|||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/configSearch.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/configSearch.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
@ -113,18 +113,42 @@
|
|||
<label>
|
||||
<span class="component-title">Ignore result with any word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="ignore_words" value="$sickbeard.IGNORE_WORDS" class="form-control input-sm input350">
|
||||
<input type="text" name="ignore_words" value="$sickbeard.IGNORE_WORDS" class="form-control input-sm input350"><p>(opt: start "regex:")</p>
|
||||
<p class="clear-left note">ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words</p>
|
||||
</span>
|
||||
<span class="component-title">Shows with custom ignores</span>
|
||||
<span class="component-desc">
|
||||
#set $shows = []
|
||||
#for $show in $using_rls_ignore_words
|
||||
#set void = $shows.append('<a href="%s/home/editShow?show=%s" style="vertical-align:middle">%s</a>' % ($sbRoot, $show[0], $show[1]))
|
||||
#end for
|
||||
#if len($using_rls_ignore_words)
|
||||
<p style="line-height:1.2em;margin-top:6px">#echo ', '.join($shows)#</p>
|
||||
#else
|
||||
<p style="line-height:1.2em;margin-top:7px">...will list here when in use</p>
|
||||
#end if
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label>
|
||||
<span class="component-title">Require at least one word</span>
|
||||
<span class="component-title">Require all these words</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="require_words" value="$sickbeard.REQUIRE_WORDS" class="form-control input-sm input350">
|
||||
<p class="clear-left note">ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words</p>
|
||||
<input type="text" name="require_words" value="$sickbeard.REQUIRE_WORDS" class="form-control input-sm input350"><p>(opt: start "regex:")</p>
|
||||
<p class="clear-left note">ignore search result <em class="grey-text">unless its title contains all</em> of these comma seperated words</p>
|
||||
</span>
|
||||
<span class="component-title">Shows with custom requires</span>
|
||||
<span class="component-desc">
|
||||
#set $shows = []
|
||||
#for $show in $using_rls_require_words
|
||||
#set void = $shows.append('<a href="%s/home/editShow?show=%s" style="vertical-align:middle">%s</a>' % ($sbRoot, $show[0], $show[1]))
|
||||
#end for
|
||||
#if len($using_rls_require_words)
|
||||
<p style="line-height:1.2em;margin-top:6px">#echo ', '.join($shows)#</p>
|
||||
#else
|
||||
<p style="line-height:1.2em;margin-top:7px">...will list here when in use</p>
|
||||
#end if
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -473,9 +497,9 @@
|
|||
|
||||
<div class="field-pair" id="torrent_seed_time_option">
|
||||
<label>
|
||||
<span class="component-title">Minimum seeding time is</span>
|
||||
<span class="component-title">Seed time (minimum default)</span>
|
||||
<span class="component-desc"><input type="number" step="0.1" name="torrent_seed_time" id="torrent_seed_time" value="$sickbeard.TORRENT_SEED_TIME" class="form-control input-sm input100">
|
||||
<p>hours. (default:'0' passes blank to client and '-1' passes nothing)</p></span>
|
||||
<p>1 or more minutes. (0 or blank to use the client setting)</p></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/configSubtitles.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/configSubtitles.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/config.js"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tokeninput.js"></script>
|
||||
|
||||
|
|
|
@ -15,19 +15,19 @@
|
|||
#import os.path, os
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.bookmarkscroll.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.bookmarkscroll.js?v=$sbPID"></script>
|
||||
|
||||
<input type="hidden" id="sbRoot" value="$sbRoot">
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/displayShow.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/sceneExceptionsTooltip.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/displayShow.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/sceneExceptionsTooltip.js?v=$sbPID"></script>
|
||||
#if $sickbeard.USE_IMDB_INFO
|
||||
<script type="text/javascript" src="$sbRoot/js/ratingTooltip.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/ratingTooltip.js?v=$sbPID"></script>
|
||||
#end if
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxEpSubtitles.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.collapser.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxEpSubtitles.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.collapser.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
\$(document).ready(function(){
|
||||
|
@ -116,7 +116,7 @@
|
|||
#end if
|
||||
<div id="details-wrapper">
|
||||
<div id="details-right">
|
||||
#if $seasonResults
|
||||
#if 0 < len($seasonResults)
|
||||
##There is a special/season_0?##
|
||||
#set $season_special = (0, 1)[0 == int($seasonResults[-1]['season'])]
|
||||
##
|
||||
|
@ -397,7 +397,11 @@
|
|||
#end if
|
||||
##
|
||||
#if 0 == len($sqlResults)
|
||||
<div style="margin-top:50px"><h3>Episodes no longer exist for this show at the associated indexer</h3></div>
|
||||
<div style="margin-top:50px">
|
||||
<h3>Episodes do not exist for this show at the associated indexer
|
||||
<a class="service" href="<%= anon_url(sickbeard.indexerApi(_show.indexer).config['show_url'], _show.indexerid) %>" onclick="window.open(this.href, '_blank'); return false;" title="Show $sickbeard.indexerApi($show.indexer).name info in new tab">$sickbeard.indexerApi($show.indexer).name</a>
|
||||
</h3>
|
||||
</div>
|
||||
#else:
|
||||
#for $epResult in $sqlResults
|
||||
#set $epStr = '%sx%s' % ($epResult['season'], $epResult['episode'])
|
||||
|
@ -518,7 +522,7 @@
|
|||
<td class="col-name">
|
||||
<img src="$sbRoot/images/info32.png" width="16" height="16" alt="" class="plotInfo#echo '%s" />' %\
|
||||
('None', ('" id="plot_info_%s_%s_%s' % ($show.indexerid, $epResult['season'], $epResult['episode'])))[None is not $epResult['description'] and '' != $epResult['description']]#
|
||||
$epResult['name']
|
||||
<%= '<em class="tba grey-text">TBA</em>' if not epResult['name'] or 'TBA' == epResult['name'] else epResult['name'] %>
|
||||
</td>
|
||||
|
||||
<td class="col-airdate">
|
||||
|
|
|
@ -13,44 +13,14 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
\$(document).ready(function(){
|
||||
|
||||
\$.getJSON('$sbRoot/home/addShows/getIndexerLanguages', {}, function(data) {
|
||||
var resultStr = '';
|
||||
|
||||
if (data.results.length == 0) {
|
||||
flag = ' class="flag" style="background-image:url($sbRoot/images/flags/${show.lang}.png)"';
|
||||
resultStr = '<option value="$show.lang" selected="selected" + flag>$show.lang</option>';
|
||||
} else {
|
||||
var current_lang_added = false;
|
||||
\$.each(data.results, function(index, obj) {
|
||||
|
||||
if (obj == '$show.lang') {
|
||||
selected = ' selected="selected"';
|
||||
current_lang_added = true;
|
||||
}
|
||||
else {
|
||||
selected = '';
|
||||
}
|
||||
|
||||
flag = ' class="flag" style="background-image:url($sbRoot/images/flags/' + obj + '.png);"';
|
||||
resultStr += '<option value="' + obj + '"' + selected + flag + '>' + obj + '</option>';
|
||||
});
|
||||
|
||||
if (!current_lang_added)
|
||||
resultStr += '<option value="$show.lang" selected="selected">$show.lang</option>';
|
||||
|
||||
}
|
||||
\$('#indexerLangSelectEdit').html(resultStr)
|
||||
|
||||
});
|
||||
});
|
||||
//-->
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/editShow.js?v=$sbPID"></script>
|
||||
<script>
|
||||
var config = {
|
||||
show_lang: "$show.lang",
|
||||
show_isanime: #echo ['!1','!0'][$show.is_anime]#
|
||||
}
|
||||
</script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
|
@ -99,15 +69,26 @@
|
|||
<span class="component-title">Scene exception</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
|
||||
<select id="SceneNameSeason" class="form-control form-control-inline input-sm input100" style="#echo ('visibility:hidden','float:left')[$show.anime]#">
|
||||
<option value="-1">Series</option>
|
||||
#if $show.anime:
|
||||
#for $season in $seasonResults:
|
||||
<option value="$season[0]">Season $season[0]</option>
|
||||
#end for
|
||||
#end if
|
||||
</select>
|
||||
<input class="btn btn-inline" type="button" value="Add" id="addSceneName">
|
||||
<p class="clear-left note">add alternative release names found on search providers for <b class="boldest grey-text">$show.name</b></p>
|
||||
#set $addSceneNameText = ('', ' or seasons')[$show.anime]
|
||||
<p class="clear-left note">add alternative release names$addSceneNameText found on search providers for <b class="boldest grey-text">$show.name</b></p>
|
||||
</span>
|
||||
<span class="component-desc">
|
||||
<div id="SceneException">
|
||||
<h4 class="grey-text">Exceptions list (multi-selectable)</h4>
|
||||
<select id="exceptions_list" name="exceptions_list" multiple="multiple" class="input200" style="min-height:90px; float:left" >
|
||||
#for $cur_exception in $show.exceptions:
|
||||
<option value="$cur_exception">$cur_exception</option>
|
||||
<select id="exceptions_list" name="exceptions_list" multiple="multiple" class="input350" style="min-height:90px; float:left" >
|
||||
#for $cur_exception_season in $show.exceptions:
|
||||
#for $cur_exception in $show.exceptions[$cur_exception_season]:
|
||||
<option value="$cur_exception_season|$cur_exception">S#echo ($cur_exception_season, '*')[$cur_exception_season == -1]#: $cur_exception</option>
|
||||
#end for
|
||||
#end for
|
||||
</select>
|
||||
<span><p class="note">this list overrides the original name<br />to search, it doesn't append to it</p></span>
|
||||
|
@ -125,8 +106,8 @@
|
|||
<span class="component-title">Ignore result with any word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350">
|
||||
<p>e.g. [word1,word2, ... ,word_n]</p>
|
||||
<p class="note">ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words</p>
|
||||
<p>e.g. [[regex:]word1, word2, ..., word_n, regex_n]</p>
|
||||
<p class="note">ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words or regular expressions</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -136,8 +117,8 @@
|
|||
<span class="component-title">Require at least one word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350">
|
||||
<p>e.g. [word1,word2, ... ,word_n]</p>
|
||||
<p class="note">ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words</p>
|
||||
<p>e.g. [[regex:]word1, word2, ..., word_n, regex_n]</p>
|
||||
<p class="note">ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words or regular expressions</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -186,7 +167,7 @@
|
|||
<span class="component-title">Scene numbering</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="scene" id="scene"#if $show.scene == 1 then $html_checked else ''#>
|
||||
<p>search for episodes that are numbered by scene groups instead of by the TV network</p>
|
||||
<p>search for episodes numbered by scene groups instead of by the TV network <em class="grey-text">(#if $show_has_scene_map then 'scene/manual numbers' else 'manual numbers only '# available)</em></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -248,73 +229,10 @@
|
|||
#if $show.is_anime:
|
||||
#import sickbeard.blackandwhitelist
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
|
||||
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?v=$sbPID"></script>
|
||||
#end if
|
||||
<input type="submit" id="submit" value="Submit" class="btn btn-primary" />
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
var all_exceptions = new Array;
|
||||
|
||||
jQuery('#location').fileBrowser({ title: 'Select Show Location' });
|
||||
|
||||
\$('#submit').click(function(){
|
||||
all_exceptions = []
|
||||
|
||||
\$('#exceptions_list option').each ( function() {
|
||||
all_exceptions.push( \$(this).val() );
|
||||
});
|
||||
|
||||
\$('#exceptions_list').val(all_exceptions);
|
||||
#if $show.is_anime:
|
||||
generate_bwlist()
|
||||
#end if
|
||||
});
|
||||
|
||||
\$('#addSceneName').click(function() {
|
||||
var scene_ex = \$('#SceneName').val()
|
||||
var option = \$('<option>')
|
||||
all_exceptions = []
|
||||
|
||||
\$('#exceptions_list option').each ( function() {
|
||||
all_exceptions.push( \$(this).val() )
|
||||
});
|
||||
|
||||
\$('#SceneName').val('')
|
||||
|
||||
if (jQuery.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == ''))
|
||||
return
|
||||
|
||||
\$('#SceneException').show()
|
||||
|
||||
option.attr('value',scene_ex)
|
||||
option.html(scene_ex)
|
||||
return option.appendTo('#exceptions_list');
|
||||
});
|
||||
|
||||
\$('#removeSceneName').click(function() {
|
||||
\$('#exceptions_list option:selected').remove();
|
||||
|
||||
\$(this).toggle_SceneException()
|
||||
});
|
||||
|
||||
$.fn.toggle_SceneException = function() {
|
||||
all_exceptions = []
|
||||
|
||||
\$('#exceptions_list option').each ( function() {
|
||||
all_exceptions.push( \$(this).val() );
|
||||
});
|
||||
|
||||
if ('' == all_exceptions)
|
||||
\$('#SceneException').hide();
|
||||
else
|
||||
\$('#SceneException').show();
|
||||
}
|
||||
|
||||
\$(this).toggle_SceneException();
|
||||
//-->
|
||||
</script>
|
||||
</div>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#end if
|
||||
|
||||
#if 'daybyday' != $layout:
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxEpSearch.js?v=$sbPID"></script>
|
||||
#end if
|
||||
|
||||
#if $varExists('header')
|
||||
|
@ -30,7 +30,7 @@
|
|||
#end if
|
||||
|
||||
#if 'daybyday' == $layout:
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
\$(document).ready(function(){
|
||||
|
@ -201,7 +201,7 @@
|
|||
#if 'list' == $layout:
|
||||
<!-- start list view //-->
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
\$.tablesorter.addParser({
|
||||
|
@ -643,6 +643,7 @@
|
|||
#end if
|
||||
#else
|
||||
#set $cur_result['state'] = $state_soon
|
||||
#set $cur_result['state-title'] = ''
|
||||
$shows_soon.append($cur_result)
|
||||
#end if
|
||||
#end for
|
||||
|
|
|
@ -8,158 +8,26 @@
|
|||
#set global $sbPath = '..'
|
||||
#set global $topmenu = 'home'
|
||||
#set global $page_body_attr = 'show-list'
|
||||
#set fuzzydate = 'airdate'
|
||||
##
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
|
||||
\$.tablesorter.addParser({
|
||||
id: 'loadingNames',
|
||||
is: function(s) {
|
||||
return false;
|
||||
},
|
||||
format: function(s) {
|
||||
if (s.indexOf('Loading...') == 0)
|
||||
return s.replace('Loading...', '000');
|
||||
else
|
||||
#if not $sickbeard.SORT_ARTICLE
|
||||
return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
|
||||
#else
|
||||
return (s || '');
|
||||
#end if
|
||||
},
|
||||
type: 'text'
|
||||
});
|
||||
|
||||
\$.tablesorter.addParser({
|
||||
id: 'quality',
|
||||
is: function(s) {
|
||||
return false;
|
||||
},
|
||||
format: function(s) {
|
||||
return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7);
|
||||
},
|
||||
type: 'numeric'
|
||||
});
|
||||
|
||||
\$(document).ready(function(){
|
||||
##
|
||||
#for $curShowlist in $showlists
|
||||
#set $curListID = $curShowlist[0]
|
||||
#if 'poster' != $layout
|
||||
|
||||
\$('#$curListID:has(tbody tr)').tablesorter({
|
||||
sortList: [[5,1],[1,0]],
|
||||
textExtraction: {
|
||||
0: function(node) { return \$(node).find('span').text().toLowerCase(); },
|
||||
2: function(node) { return \$(node).find('span').text().toLowerCase(); },
|
||||
3: function(node) { return \$(node).find('span').text().toLowerCase(); },
|
||||
4: function(node) { return \$(node).find('span').attr('data-progress'); },
|
||||
5: function(node) { return \$(node).find('i').attr('alt'); }
|
||||
},
|
||||
widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'],
|
||||
headers: {
|
||||
0: { sorter: 'isoDate' },
|
||||
1: { sorter: 'loadingNames' },
|
||||
3: { sorter: 'quality' },
|
||||
4: { sorter: 'eps' }
|
||||
},
|
||||
widgetOptions: {
|
||||
filter_columnFilters: false,
|
||||
filter_reset: '.resetshows'
|
||||
},
|
||||
sortStable: true
|
||||
});
|
||||
|
||||
\$.tablesorter.filter.bindSearch( '#$curListID', \$('.search') );
|
||||
#else
|
||||
|
||||
var \$container = [\$('#$curListID')]
|
||||
|
||||
jQuery.each(\$container, function (j) {
|
||||
this.isotope({
|
||||
itemSelector: '.show',
|
||||
sortBy: '$sickbeard.POSTER_SORTBY',
|
||||
sortAscending: $sickbeard.POSTER_SORTDIR,
|
||||
layoutMode: 'masonry',
|
||||
masonry: {
|
||||
columnWidth: 12,
|
||||
isFitWidth: true
|
||||
},
|
||||
getSortData: {
|
||||
name: function( itemElem ) {
|
||||
var name = \$( itemElem ).attr('data-name');
|
||||
#if not $sickbeard.SORT_ARTICLE
|
||||
return (name || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
|
||||
#else
|
||||
return (name || '');
|
||||
#end if
|
||||
},
|
||||
date: function( itemElem ) {
|
||||
var date = \$( itemElem ).attr('data-date');
|
||||
return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY;
|
||||
},
|
||||
network: '[data-network]',
|
||||
progress: function( itemElem ) {
|
||||
var progress = \$( itemElem ).children('.sort-data').attr('data-progress');
|
||||
return progress.length && parseInt( progress, 10 ) || Number.NEGATIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
\$('#postersort').on( 'change', function() {
|
||||
var sortValue = this.value;
|
||||
\$('#$curListID').isotope({ sortBy: sortValue });
|
||||
\$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
|
||||
});
|
||||
|
||||
\$('#postersortdirection').on( 'change', function() {
|
||||
var sortDirection = this.value;
|
||||
sortDirection = sortDirection == 'true';
|
||||
\$('#$curListID').isotope({ sortAscending: sortDirection });
|
||||
\$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
|
||||
});
|
||||
#end if
|
||||
#end for
|
||||
|
||||
#raw
|
||||
$('img#network').on('error', function(){
|
||||
$(this).parent().text($(this).attr('alt'));
|
||||
$(this).remove();
|
||||
});
|
||||
#end raw
|
||||
##
|
||||
#set $fuzzydate = 'airdate'
|
||||
#if $sickbeard.FUZZY_DATING
|
||||
fuzzyMoment({
|
||||
dtInline: #echo ('!1', '!0')['poster' == $layout]#,
|
||||
containerClass: '.${fuzzydate}',
|
||||
dateHasTime: !1,
|
||||
dateFormat: '${sickbeard.DATE_PRESET}',
|
||||
timeFormat: '${sickbeard.TIME_PRESET}',
|
||||
trimZero: #echo ('!1', '!0')[$sickbeard.TRIM_ZERO]#
|
||||
});
|
||||
#end if
|
||||
#if $sickbeard.HOME_SEARCH_FOCUS
|
||||
|
||||
\$('#search_show_name').focus();
|
||||
|
||||
#end if
|
||||
});
|
||||
|
||||
//-->
|
||||
</script>
|
||||
##
|
||||
#if $varExists('header')
|
||||
|
||||
<script>
|
||||
var config = {
|
||||
isPoster: #echo ['!1','!0']['poster' == $sickbeard.HOME_LAYOUT]#,
|
||||
sortArticle: #echo ['!1','!0'][$sickbeard.SORT_ARTICLE]#,
|
||||
homeSearchFocus: #echo ['!1','!0'][$sickbeard.HOME_SEARCH_FOCUS]#,
|
||||
fuzzyDating: #echo ['!1','!0'][$sickbeard.FUZZY_DATING]#,
|
||||
timeZero: #echo ['!1','!0'][$sickbeard.TRIM_ZERO]#,
|
||||
datePreset: "$sickbeard.DATE_PRESET",
|
||||
timePreset: "$sickbeard.TIME_PRESET",
|
||||
posterSortby: "$sickbeard.POSTER_SORTBY",
|
||||
posterSortdir: #echo ['!1','!0'][$sickbeard.POSTER_SORTDIR]#,
|
||||
fuzzydate: ".$fuzzydate",
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/home.js?v=$sbPID"></script>
|
||||
<h1 class="header" style="margin-bottom:0">$showlists[0][1]</h1>
|
||||
#else
|
||||
|
||||
<h1 class="title" style="margin-bottom:0">$title</h1>
|
||||
#end if
|
||||
#set $tab = 1
|
||||
#if 'poster' != $layout
|
||||
|
||||
|
@ -324,7 +192,7 @@
|
|||
<div class="show-date">
|
||||
#if $cur_airs_next
|
||||
#set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
|
||||
<span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</span>
|
||||
<span class="$fuzzydate">$sbdatetime.sbdatetime.sbfdate($ldatetime)</span>
|
||||
#else
|
||||
#set $output_html = '?'
|
||||
#if None is not $display_status
|
||||
|
@ -471,7 +339,7 @@
|
|||
<tr>
|
||||
#if $cur_airs_next
|
||||
#set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
|
||||
<td class="text-nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort-data">$ldatetime.strftime('%Y%m%d%H%M')</span></td>
|
||||
<td class="text-nowrap"><div class="$fuzzydate">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort-data">$ldatetime.strftime('%Y%m%d%H%M')</span></td>
|
||||
#else
|
||||
<td></td>
|
||||
#end if
|
||||
|
@ -550,21 +418,6 @@
|
|||
#end if
|
||||
#end for
|
||||
##
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
#raw
|
||||
$(document).ready(function(){
|
||||
$('div[id^="progressbar"]').each(function(k, v){
|
||||
var progress = parseInt($(this).siblings('span[class="sort-data"]').attr('data-progress'), 10) , elId = '#' + $(this).attr('id'), v = 80;
|
||||
$(elId).progressbar({value:progress});
|
||||
if (progress < 80) {
|
||||
v = progress >= 40 ? 60 : (progress >= 20 ? 40 : 20);
|
||||
}
|
||||
$(elId + ' > .ui-progressbar-value').addClass('progress-' + v);
|
||||
});
|
||||
});
|
||||
#end raw
|
||||
//-->
|
||||
</script>
|
||||
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
|
@ -10,14 +10,11 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addExistingShow.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?$sbPID"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
\$(document).ready(function(){
|
||||
\$.sgSid = '$kwargs.get('sid', '')';
|
||||
\$.sgHashDir = '$kwargs.get('hash_dir', '')';
|
||||
\$(document).ready(function(){
|
||||
\$( '#tabs' ).tabs({
|
||||
collapsible: true,
|
||||
selected: #if $sickbeard.ROOT_DIRS then '-1' else '0'#
|
||||
|
@ -25,6 +22,10 @@
|
|||
});
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addExistingShow.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
@ -36,48 +37,56 @@
|
|||
|
||||
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
|
||||
|
||||
<p>Tip: shows are added quicker when usable show nfo and xml metadata is found</p>
|
||||
<span#if $kwargs.get('hash_dir', None)# style="display:none"#end if>
|
||||
<p>Tip: shows are added quicker when usable show nfo and xml metadata is found</p>
|
||||
|
||||
<p style="margin-top:15px">
|
||||
<input type="checkbox" id="promptForSettings" name="promptForSettings" style="vertical-align: top;" />
|
||||
<label for="promptForSettings">Enable to change the following options per show, otherwise use these options with all shows added below</label>
|
||||
</p>
|
||||
<p style="margin-top:15px">
|
||||
<input type="checkbox" id="promptForSettings" name="promptForSettings" style="vertical-align: top;" />
|
||||
<label for="promptForSettings">Enable to change the following options per show, otherwise use these options with all shows added below</label>
|
||||
</p>
|
||||
|
||||
<div id="tabs">
|
||||
<ul>
|
||||
<li><a href="#tabs-1">Manage parent folders</a></li>
|
||||
<li><a href="#tabs-2">Custom options</a></li>
|
||||
</ul>
|
||||
<div id="tabs-1" class="existingtabs">
|
||||
<div style="width: 430px; margin: 0px auto">
|
||||
<div id="tabs">
|
||||
<ul>
|
||||
<li><a href="#tabs-1">Manage parent folders</a></li>
|
||||
<li><a href="#tabs-2">Custom options</a></li>
|
||||
</ul>
|
||||
<div id="tabs-1" class="existingtabs">
|
||||
<div style="width: 430px; margin: 0px auto">
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tabs-2">
|
||||
<div class="stepDiv">
|
||||
<div id="tabs-2">
|
||||
<div class="stepDiv">
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<hr />
|
||||
<br />
|
||||
<hr />
|
||||
</span>
|
||||
|
||||
<p>The following parent folder(s) are scanned for existing shows. Toggle a folder to display shows</p>
|
||||
<p>The following parent folder$multi_parents scanned for
|
||||
#if not $kwargs.get('hash_dir', None)#existing shows. Toggle a folder to display shows#else#the existing show...#end if#
|
||||
</p>
|
||||
|
||||
<ul id="rootDirStaticList">
|
||||
<li></li>
|
||||
</ul>
|
||||
|
||||
#if not $kwargs.get('hash_dir', None)
|
||||
<p>shows <span class="boldest">not known</span> to SickGear are listed below...</p>
|
||||
#end if
|
||||
|
||||
<div id="tableDiv"></div>
|
||||
|
||||
<br />
|
||||
#if not $kwargs.get('hash_dir', None)
|
||||
<p>If you tried to add a show, arrived here and can't see the folder, then that show may already be in your show list.</p>
|
||||
#end if
|
||||
|
||||
<input class="btn btn-primary" type="button" value="Submit" id="submitShowDirs" />
|
||||
<input class="btn btn-primary" type="button" value="#if $kwargs.get('hash_dir', None)#Redo Search#else#Submit#end if#" id="submitShowDirs" />
|
||||
|
||||
</form>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -10,55 +10,65 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
<div id="addShowPortal">
|
||||
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/newShow/">
|
||||
<div class="button"><div class="icon-addnewshow"></div></div>
|
||||
<div class="buttontext">
|
||||
<h3>Add New Show</h3>
|
||||
<p>Search a TV database for a show to add. A new folder will be created for episodes</p>
|
||||
<p>Search a TV database for a show to add.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/trendingShows/">
|
||||
<div class="button"><div class="icon-addtrendingshow"></div></div>
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/trakt_default/">
|
||||
<div class="button"><div class="icon-addrecommendedshow"></div></div>
|
||||
<div class="buttontext">
|
||||
<h3>Add From Trending</h3>
|
||||
<p>Browse a current trending show list to add from. A folder for episodes will be created</p>
|
||||
<h3>Add From Trakt</h3>
|
||||
<p>Browse trends, recommended and more.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div style="clear:both;font-size:2px"> </div>
|
||||
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/existingShows/">
|
||||
<div class="button"><div class="icon-addexistingshow"></div></div>
|
||||
<div class="buttontext">
|
||||
<h3>Add Existing Shows</h3>
|
||||
<p>Scan parent folders for shows and episode metadata to import into SickGear</p>
|
||||
<p>Scan parent folders to import into SickGear.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
#if True == $sickbeard.USE_TRAKT:
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/recommendedShows/">
|
||||
<div class="button"><div class="icon-addrecommendedshow"></div></div>
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/popular_imdb/">
|
||||
<div class="button"><div class="icon-addtrendingshow"></div></div>
|
||||
<div class="buttontext">
|
||||
<h3>Add Recommended</h3>
|
||||
<p>Browse recommendations based on your Trakt.tv show library to add to SickGear</p>
|
||||
<h3>Add From IMDb</h3>
|
||||
<p>Browse popular for a show to add.</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div style="clear:both;font-size:2px"> </div>
|
||||
|
||||
#if $sickbeard.USE_ANIDB
|
||||
<a class="btn btn-large" href="$sbRoot/home/addShows/randomhot_anidb/" style="float:right">
|
||||
<div class="button"><div class="icon-addtrendingshow"></div></div>
|
||||
<div class="buttontext">
|
||||
<h3>Add from AniDB</h3>
|
||||
<p>Browse what's hot and recommnended.</p>
|
||||
</div>
|
||||
</a>
|
||||
#else
|
||||
<div class="buttontext" style="padding:10px 5px">
|
||||
<p>There's more... unlock another button to browse<br />
|
||||
recommended shows based on your Trakt.tv show<br />
|
||||
library by enabling Trakt in Config/Notifications/Social</p>
|
||||
<div class="buttontext" style="padding:10px 5px 10px 30px">
|
||||
<h3>Add Random/Hot AniDB</h3>
|
||||
<p>To use, enable AniDB in Config/Anime.</p>
|
||||
</div>
|
||||
#end if
|
||||
|
||||
</div>
|
||||
|
||||
<div style="clear:both"> </div>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
265
gui/slick/interfaces/default/home_browseShows.tmpl
Normal file
|
@ -0,0 +1,265 @@
|
|||
#import sickbeard
|
||||
#import datetime
|
||||
#import re
|
||||
#import urllib
|
||||
#from sickbeard.common import *
|
||||
#from sickbeard import sbdatetime
|
||||
#from sickbeard.helpers import anon_url
|
||||
##
|
||||
#set global $title='Browse Shows'
|
||||
#set global $header='Browse Shows'
|
||||
#set global $sbPath='..'
|
||||
#set global $topmenu='home'
|
||||
##
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
#raw
|
||||
var addQTip = (function(){
|
||||
$(this).css('cursor', 'help');
|
||||
$(this).qtip({
|
||||
show: {solo:true},
|
||||
position: {viewport:$(window), my:'left center', adjust:{y: -10,x: 2 }},
|
||||
style: {tip: {corner:true, method:'polygon'}, classes:'qtip-rounded qtip-bootstrap qtip-shadow ui-tooltip-sb'}
|
||||
});
|
||||
});
|
||||
#end raw
|
||||
|
||||
\$(document).ready(function(){
|
||||
// initialise combos for dirty page refreshes
|
||||
\$('#showsort').val('original');
|
||||
\$('#showsortdirection').val('asc');
|
||||
\$('#showfilter').val('*');
|
||||
|
||||
var \$container = [\$('#container')];
|
||||
jQuery.each(\$container, function (j) {
|
||||
this.isotope({
|
||||
itemSelector: '.browse-show',
|
||||
sortBy: 'original-order',
|
||||
layoutMode: 'masonry',
|
||||
masonry: {
|
||||
columnWidth: 12,
|
||||
isFitWidth: true
|
||||
},
|
||||
getSortData: {
|
||||
premiered: '[data-premiered] parseInt',
|
||||
name: function( itemElem ) {
|
||||
var name = \$( itemElem ).attr('data-name') || '';
|
||||
#if not $sickbeard.SORT_ARTICLE:
|
||||
name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
|
||||
#end if
|
||||
return name.toLowerCase();
|
||||
},
|
||||
rating: '[data-rating] parseInt',
|
||||
votes: '[data-votes] parseInt',
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
\$('#showsort').on( 'change', function() {
|
||||
var sortCriteria;
|
||||
switch (this.value) {
|
||||
case 'original':
|
||||
sortCriteria = 'original-order'
|
||||
break;
|
||||
case 'rating':
|
||||
/* randomise, else the rating_votes can already
|
||||
* have sorted leaving this with nothing to do.
|
||||
*/
|
||||
\$('#container').isotope({sortBy: 'random'});
|
||||
sortCriteria = 'rating';
|
||||
break;
|
||||
case 'rating_votes':
|
||||
sortCriteria = ['rating', 'votes'];
|
||||
break;
|
||||
case 'votes':
|
||||
sortCriteria = 'votes';
|
||||
break;
|
||||
case 'premiered':
|
||||
sortCriteria = 'premiered';
|
||||
break;
|
||||
default:
|
||||
sortCriteria = 'name'
|
||||
break;
|
||||
}
|
||||
\$('#container').isotope({sortBy: sortCriteria});
|
||||
});
|
||||
|
||||
\$('#showsortdirection').on( 'change', function() {
|
||||
\$('#container').isotope({sortAscending: ('asc' == this.value)});
|
||||
});
|
||||
|
||||
\$('#showfilter').on( 'change', function() {
|
||||
var filterValue = this.value;
|
||||
if (-1 == filterValue.indexOf('trakt')) {
|
||||
\$('#container').isotope({ filter: filterValue });
|
||||
} else {
|
||||
location = '$sbRoot/home/addShows/' + filterValue;
|
||||
}
|
||||
});
|
||||
|
||||
#raw
|
||||
$('.service, .browse-image').each(addQTip);
|
||||
#end raw
|
||||
});
|
||||
|
||||
//-->
|
||||
</script>
|
||||
#if $varExists('header')
|
||||
#set $heading = ('header', $header)
|
||||
#else
|
||||
#set $heading = ('title', $title)
|
||||
#end if
|
||||
<h1 style="margin-bottom:0" class="grey-text #echo '%s">%s' % $heading#</h1>
|
||||
|
||||
#if $all_shows or ($kwargs and $kwargs.get('show_header', None))
|
||||
<div class="pull-right" style="margin-top:-35px">
|
||||
<span>Show:</span>
|
||||
<select id="showfilter" class="form-control form-control-inline input-sm">
|
||||
#set $count_all_shows = len($all_shows)
|
||||
#set $count_inlibrary = $all_shows_inlibrary
|
||||
<option value="*" selected="selected">All<%= ' (%d)' % count_all_shows %></option>
|
||||
<option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_all_shows - count_inlibrary) %></option>
|
||||
<option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option>
|
||||
#if 'Trakt' == $browse_type
|
||||
#set $mode = $kwargs and $kwargs.get('mode', None)
|
||||
#set $selected = ' class="selected"'
|
||||
<optgroup label="Trakt">
|
||||
<option value="trakt_anticipated"#echo ('', selected)['anticipated' == $mode]#>Anticipating</option>
|
||||
<option value="trakt_newseasons"#echo ('', selected)['newseasons' == $mode]#>New Seasons</option>
|
||||
<option value="trakt_newshows"#echo ('', selected)['newshows' == $mode]#>New Shows</option>
|
||||
<option value="trakt_popular"#echo ('', selected)['popular' == $mode]#>Popular</option>
|
||||
<option value="trakt_trending"#echo ('', selected)['trending' == $mode]#>Trending</option>
|
||||
</optgroup>
|
||||
<optgroup label="Trakt last month">
|
||||
<option value="trakt_watched"#echo ('', selected)['watched' == $mode]#>Most Watched</option>
|
||||
<option value="trakt_played"#echo ('', selected)['played' == $mode]#>Most Played</option>
|
||||
<option value="trakt_collected"#echo ('', selected)['collected' == $mode]#>Most Collected</option>
|
||||
</optgroup>
|
||||
<optgroup label="Trakt last 12 months">
|
||||
<option value="trakt_watched?period=year"#echo ('', selected)['watched-year' == $mode]#>Most Watched</option>
|
||||
<option value="trakt_played?period=year"#echo ('', selected)['played-year' == $mode]#>Most Played</option>
|
||||
<option value="trakt_collected?period=year"#echo ('', selected)['collected-year' == $mode]#>Most Collected</option>
|
||||
</optgroup>
|
||||
#if any($sickbeard.TRAKT_ACCOUNTS)
|
||||
<optgroup label="Trakt recommended">
|
||||
#for $account in $sickbeard.TRAKT_ACCOUNTS
|
||||
#if $sickbeard.TRAKT_ACCOUNTS[$account].active and $sickbeard.TRAKT_ACCOUNTS[$account].name
|
||||
<option value="trakt_recommended?account=$account"#echo ('', selected)['recommended-%s' % $account == $mode]#>for $sickbeard.TRAKT_ACCOUNTS[$account].name</option>
|
||||
#end if
|
||||
#end for
|
||||
#else
|
||||
<optgroup label="To get recommended">
|
||||
<option value="trakt_recommended?action=add">Enable Trakt here</option>
|
||||
#end if
|
||||
</optgroup>
|
||||
#end if
|
||||
</select>
|
||||
|
||||
<span style="margin-left:12px">Sort By:</span>
|
||||
<select id="showsort" class="form-control form-control-inline input-sm">
|
||||
<option value="name">Name</option>
|
||||
<option value="original" selected="selected">Original</option>
|
||||
<option value="premiered">First aired</option>
|
||||
<option value="votes">Votes</option>
|
||||
<option value="rating">% Rating</option>
|
||||
<option value="rating_votes">% Rating > Votes</option>
|
||||
</select>
|
||||
|
||||
<span style="margin-left:12px">Sort Order:</span>
|
||||
<select id="showsortdirection" class="form-control form-control-inline input-sm">
|
||||
<option value="asc" selected="selected">Asc</option>
|
||||
<option value="desc">Desc</option>
|
||||
</select>
|
||||
</div>
|
||||
<h4 style="float:left;margin:0 0 0 2px">$browse_title</h4>
|
||||
#if $kwargs and $kwargs.get('oldest', None):
|
||||
<div class="grey-text" style="clear:both;margin-left:2px;font-size:0.85em">
|
||||
First aired from $kwargs['oldest'] until $kwargs['newest']
|
||||
</div>
|
||||
#end if
|
||||
#end if
|
||||
|
||||
<div id="container">
|
||||
#if $all_shows
|
||||
#for $this_show in $all_shows:
|
||||
#set $title_html = $this_show['title'].replace('"', '"').replace("'", ''')
|
||||
#if 'newseasons' == $kwargs.get('mode', '')
|
||||
#set $overview = '%s: %s' % (
|
||||
('Season %s' % $this_show['episode_season'], 'Brand-new')[1 == $this_show['episode_season']],
|
||||
($this_show['overview'], $this_show['episode_overview'])[any($this_show['episode_overview']) and 1 != $this_show['episode_season']])
|
||||
#else
|
||||
#set $overview = $this_show['overview']
|
||||
#end if
|
||||
|
||||
<div class="browse-show <%= ('notinlibrary', 'inlibrary')[':' in this_show['show_id']] %>" data-name="#echo re.sub(r'([\'\"])', r'', $this_show['title'])#" data-rating="$this_show['rating']" data-votes="$this_show['votes']" data-premiered="$this_show['premiered']">
|
||||
<div class="browse-container">
|
||||
<div class="browse-image">
|
||||
<a class="browse-image" href="<%= anon_url(this_show['url_src_db']) %>" target="_blank"
|
||||
title="<span style='color: rgb(66, 139, 202)'>$re.sub(r'(?m)\s+\((?:19|20)\d\d\)\s*$', '', $title_html)</span>#if $this_show['genres']#<br /><div style='font-weight:bold'>(<em>$this_show['genres']</em>)</div>#end if#
|
||||
<p style='margin:0 0 2px'>#echo re.sub(r'([,\.!][^,\.!]*?)$', '...', re.sub(r'([!\?\.])(?=\w)', r'\1 ', $overview))#</p>
|
||||
<p><span style='font-weight:bold;font-size:0.9em;color:#888'><em>#if $kwargs and 'newseasons' == $kwargs.get('mode', None)#Air#else#First air#end if##echo ('s', 'ed')[$this_show['when_past']]#: $this_show['premiered_str']</em></span></p>
|
||||
<span style='float:right'>Click for more at <span class='boldest'>$browse_type</span></span>">
|
||||
#if 'poster' in $this_show['images']:
|
||||
#set $image = $this_show['images']['poster']['thumb']
|
||||
<img alt="" class="browse-image" src="#if $image and 'http' != $image[:4]#$sbRoot/#end if#$image" />
|
||||
#else:
|
||||
<span> </span>
|
||||
#end if
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="show-title">
|
||||
<%= (this_show['title'], '<span> </span>')['' == this_show['title']] %>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
<p>$this_show['rating']% <img src="$sbRoot/images/heart.png"><i>$this_show['votes'] votes</i></p>
|
||||
#if 'url_tvdb' in $this_show and $this_show['url_tvdb']:
|
||||
<a class="service" href="<%= anon_url(this_show['url_tvdb']) %>" onclick="window.open(this.href, '_blank'); return false;"
|
||||
title="View <span class='boldest'>tvdb</span> detail for <span style='color: rgb(66, 139, 202)'>$title_html</span>">
|
||||
<i><img style="margin-top:5px" alt="tvdb" height="16" width="16" src="$sbRoot/images/$sickbeard.indexerApi($sickbeard.indexers.indexer_config.INDEXER_TVDB).config['icon']" /></i></a>
|
||||
#end if
|
||||
|
||||
<div class="browse-add-show-holder">
|
||||
#if ':' in $this_show['show_id']:
|
||||
<p style="line-height: 1.5; padding: 2px 5px 3px" title="<%= '%s added' % ('TVRage', 'theTVDB')['1' == this_show['show_id'][:1]] %>">In library</p>
|
||||
#else
|
||||
<a href="$sbRoot/home/addShows/add${browse_type}Show?indexer_id=${this_show['show_id']}&showName=${urllib.quote($this_show['title'].encode("utf-8"))}" class="btn btn-xs">Add Show</a>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#end for
|
||||
</div>
|
||||
#if $kwargs and $kwargs.get('footnote', None):
|
||||
<div>
|
||||
$kwargs['footnote']
|
||||
</div>
|
||||
#end if
|
||||
#else
|
||||
<div class="browse-show" style="width:100%; margin-top:20px">
|
||||
<p class="red-text">
|
||||
#if $kwargs and $kwargs.get('error_msg', None):
|
||||
$kwargs['error_msg']
|
||||
#else
|
||||
$browse_type API did not return results, this can happen from time to time.
|
||||
<br /><br />This view should auto refresh every 10 mins.
|
||||
#end if
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
#end if
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
|
||||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
|
@ -2,48 +2,72 @@
|
|||
#from sickbeard.helpers import anon_url
|
||||
|
||||
<table id="addRootDirTable" class="sickbeardTable tablesorter">
|
||||
<thead><tr><th class="col-checkbox"><input type="checkbox" id="checkAll" checked=checked></th><th>Parent\show folder</th><th width="20%">Show name<br />(tvshow.nfo)<th width="15%">TV database</td></tr></thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th rowspan="1" colspan="4" align="left"><a href="#" class="showManage">Manage Directories</a></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
#for $curDir in $dirList:
|
||||
#if $curDir['added_already']:
|
||||
#continue
|
||||
#end if
|
||||
|
||||
#set $show_id = $curDir['dir']
|
||||
#if $curDir['existing_info'][0]:
|
||||
#set $show_id = $show_id + '|' + $str($curDir['existing_info'][0]) + '|' + $str($curDir['existing_info'][1])
|
||||
#set $indexer = $curDir['existing_info'][2]
|
||||
#end if
|
||||
|
||||
#set $indexer = 0
|
||||
#if $curDir['existing_info'][0]:
|
||||
#set $indexer = $curDir['existing_info'][2]
|
||||
#elif $sickbeard.INDEXER_DEFAULT > 0:
|
||||
#set $indexer = $sickbeard.INDEXER_DEFAULT
|
||||
#end if
|
||||
|
||||
<tr>
|
||||
<td class="col-checkbox"><input type="checkbox" id="$show_id" class="dirCheck" checked=checked></td>
|
||||
<td><label for="$show_id">$curDir['display_dir']</label></td>
|
||||
#if $curDir['existing_info'][1] and $indexer > 0:
|
||||
<td><a href="<%= anon_url(sickbeard.indexerApi(indexer).config['show_url'], curDir['existing_info'][0]) %>" target="_new">$curDir['existing_info'][1]</a></td>
|
||||
#else:
|
||||
<td>?</td>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-checkbox">
|
||||
<input type="checkbox" id="checkAll" checked=checked>
|
||||
</th>
|
||||
<th>Parent\show folder</th>
|
||||
<th width="20%">Show name<br />(tvshow.nfo)</th>
|
||||
<th width="15%">TV info source</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th rowspan="1" colspan="4" align="left">
|
||||
<a href="#" class="showManage">Manage Directories</a>
|
||||
</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
#for $curDir in $dirList
|
||||
#if $curDir['added_already'] and (None is $curDir.get('highlight') or not $kwargs.get('hash_dir'))
|
||||
#continue
|
||||
#end if
|
||||
<td align="center">
|
||||
<select name="indexer">
|
||||
#for $curIndexer in $sickbeard.indexerApi().indexers.items():
|
||||
<option value="$curIndexer[0]" #if $curIndexer[0] == $indexer then "selected=\"selected\"" else "UNKNOWN"#>$curIndexer[1]</option>
|
||||
#end for
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
#set $show_id = $curDir['dir']
|
||||
#if $curDir['existing_info'][0]
|
||||
#set $show_id = $show_id + '|' + $str($curDir['existing_info'][0]) + '|' + $str($curDir['existing_info'][1])
|
||||
#set $indexer = $curDir['existing_info'][2]
|
||||
#end if
|
||||
|
||||
#set $indexer = $sickbeard.INDEXER_DEFAULT
|
||||
|
||||
#*
|
||||
#set $indexer = 0
|
||||
#if $curDir['existing_info'][0]
|
||||
#set $indexer = $curDir['existing_info'][2]
|
||||
#elif 0 < $sickbeard.INDEXER_DEFAULT
|
||||
#set $indexer = $sickbeard.INDEXER_DEFAULT
|
||||
#end if
|
||||
*#
|
||||
<tr>
|
||||
<td class="col-checkbox">
|
||||
<input type="checkbox" id="$show_id" class="dirCheck" checked=checked>
|
||||
</td>
|
||||
<td>
|
||||
<label for="$show_id">
|
||||
<span class="filepath#echo ('', ' red-text')[$curDir['highlight']]#">$curDir['path']</span>$curDir['name']
|
||||
#echo ('', '<br />^ <span class="red-text">... (cannot add, this location is in use)</span>')[$curDir['highlight']]#
|
||||
</label>
|
||||
</td>
|
||||
#if $curDir['existing_info'][1] and $indexer > 0
|
||||
<td>
|
||||
<a href="<%= anon_url(sickbeard.indexerApi(indexer).config['show_url'], curDir['existing_info'][0]) %>" target="_new">$curDir['existing_info'][1]</a>
|
||||
</td>
|
||||
#else
|
||||
<td>?</td>
|
||||
#end if
|
||||
<td align="center">
|
||||
<select name="indexer">
|
||||
#for $curIndexer in $sickbeard.indexerApi().indexers.items()
|
||||
#if $curIndexer[0] == $sickbeard.INDEXER_DEFAULT
|
||||
<option value="$curIndexer[0]" #if $curIndexer[0] == $indexer then 'selected="selected"' else ''#>$curIndexer[1]</option>
|
||||
#end if
|
||||
#end for
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
#end for
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -10,10 +10,14 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/formwizard.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/newShow.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?$sbPID"></script>
|
||||
<script>
|
||||
var show_scene_maps = ${show_scene_maps}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/formwizard.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/newShow.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
@ -31,7 +35,7 @@
|
|||
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
|
||||
|
||||
<fieldset class="sectionwrap step-one">
|
||||
<legend class="legendStep"><p>Find show at a TV database</p></legend>
|
||||
<legend class="legendStep"><p>#if $use_provided_info#Using known show information#else#Find show at TV info source#end if#</p></legend>
|
||||
|
||||
<div class="stepDiv">
|
||||
<input type="hidden" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" />
|
||||
|
@ -39,29 +43,38 @@
|
|||
#if $use_provided_info
|
||||
#set $provided_indexer_local = $provided_indexer
|
||||
#set $provided_indexer_id_local = $provided_indexer_id
|
||||
Show retrieved from existing metadata: <a href="<%= anon_url(sickbeard.indexerApi(provided_indexer_local).config['show_url'], provided_indexer_id_local) %>">$provided_indexer_name</a>
|
||||
Show: <a href="<%= anon_url(sickbeard.indexerApi(provided_indexer_local).config['show_url'], provided_indexer_id_local) %>">$provided_indexer_name</a>
|
||||
<input type="hidden" name="indexerLang" value="en" />
|
||||
<input type="hidden" name="whichSeries" value="$provided_indexer_id" />
|
||||
<input type="hidden" name="whichSeries" value="#echo '|'.join([str($provided_indexer), '', str($provided_indexer_id), $provided_indexer_name])#" />
|
||||
<input type="hidden" id="providedName" value="$provided_indexer_name" />
|
||||
<input type="hidden" id="providedIndexer" value="$provided_indexer" />
|
||||
#else
|
||||
#if 2 > $len($indexers)
|
||||
<style>
|
||||
#addShowForm input#nameToSearch{width:611px}
|
||||
</style>
|
||||
<input type="hidden" id="providedIndexer" value="$provided_indexer" />
|
||||
#end if
|
||||
<input type="text" id="nameToSearch" value="$default_show_name" class="form-control form-control-inline input-sm input350" />
|
||||
|
||||
<span style="float:right">
|
||||
<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm">
|
||||
<option value="en" selected="selected">en</option>
|
||||
</select><b> *</b>
|
||||
<select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
|
||||
<option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option>
|
||||
#for $indexer in $indexers
|
||||
<option value="$indexer" #if $provided_indexer == $indexer then "selected=\"selected\"" else ""#>$indexers[$indexer]</option>
|
||||
#end for
|
||||
</select>
|
||||
|
||||
<input class="btn btn-inline" type="button" id="searchName" value="Search" />
|
||||
<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm">
|
||||
<option value="en" selected="selected">en</option>
|
||||
</select><b> *</b>
|
||||
|
||||
#if 1 < $len($indexers)
|
||||
<select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
|
||||
<option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option>
|
||||
#for $indexer in $indexers
|
||||
<option value="$indexer" #if $provided_indexer == $indexer then "selected=\"selected\"" else ""#>$indexers[$indexer]</option>
|
||||
#end for
|
||||
</select>
|
||||
#end if
|
||||
|
||||
<input class="btn btn-inline" type="button" id="searchName" value="Search" />
|
||||
</span>
|
||||
<br />
|
||||
<p style="margin:5px 0 15px"><b>*</b> SickGear supports english episodes. The language choice is used for fetching show data and episode filenames</p>
|
||||
<p style="margin:5px 0 15px">Enter show name, TVDB ID, IMDb Url, or IMDb ID. <b>*</b>SickGear supports english, language is used for show/episode data</p>
|
||||
|
||||
<div id="searchResults" style="height: 100%"></div>
|
||||
#end if
|
||||
|
@ -107,9 +120,9 @@
|
|||
#end if
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?v=$sbPID"></script>
|
||||
|
||||
</div></div>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/formwizard.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/recommendedShows.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/formwizard.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/recommendedShows.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/addShowOptions.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
@ -65,7 +65,7 @@
|
|||
<input class="btn" type="button" id="addShowButton" value="Add Show" disabled="disabled" />
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?v=$sbPID"></script>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
#import sickbeard
|
||||
#import datetime
|
||||
#import re
|
||||
#import urllib
|
||||
#from sickbeard.common import *
|
||||
#from sickbeard import sbdatetime
|
||||
#from sickbeard.helpers import anon_url
|
||||
##
|
||||
#set global $title='Trending Shows'
|
||||
#set global $header='Trending Shows'
|
||||
#set global $sbPath='..'
|
||||
#set global $topmenu='episodeView'
|
||||
##
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
|
||||
\$(document).ready(function(){
|
||||
// initialise combos for dirty page refreshes
|
||||
\$('#showsort').val('original');
|
||||
\$('#showsortdirection').val('asc');
|
||||
\$('#showfilter').val('*');
|
||||
|
||||
var \$container = [\$('#container')];
|
||||
jQuery.each(\$container, function (j) {
|
||||
this.isotope({
|
||||
itemSelector: '.trakt_show',
|
||||
sortBy: 'original-order',
|
||||
layoutMode: 'fitRows',
|
||||
getSortData: {
|
||||
name: function( itemElem ) {
|
||||
var name = \$( itemElem ).attr('data-name') || '';
|
||||
#if not $sickbeard.SORT_ARTICLE:
|
||||
name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
|
||||
#end if
|
||||
return name.toLowerCase();
|
||||
},
|
||||
rating: '[data-rating] parseInt',
|
||||
votes: '[data-votes] parseInt',
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
\$('#showsort').on( 'change', function() {
|
||||
var sortCriteria;
|
||||
switch (this.value) {
|
||||
case 'original':
|
||||
sortCriteria = 'original-order'
|
||||
break;
|
||||
case 'rating':
|
||||
/* randomise, else the rating_votes can already
|
||||
* have sorted leaving this with nothing to do.
|
||||
*/
|
||||
\$('#container').isotope({sortBy: 'random'});
|
||||
sortCriteria = 'rating';
|
||||
break;
|
||||
case 'rating_votes':
|
||||
sortCriteria = ['rating', 'votes'];
|
||||
break;
|
||||
case 'votes':
|
||||
sortCriteria = 'votes';
|
||||
break;
|
||||
default:
|
||||
sortCriteria = 'name'
|
||||
break;
|
||||
}
|
||||
\$('#container').isotope({sortBy: sortCriteria});
|
||||
});
|
||||
|
||||
\$('#showsortdirection').on( 'change', function() {
|
||||
\$('#container').isotope({sortAscending: ('asc' == this.value)});
|
||||
});
|
||||
|
||||
\$('#showfilter').on( 'change', function() {
|
||||
var filterValue = this.value;
|
||||
\$('#container').isotope({ filter: filterValue });
|
||||
});
|
||||
});
|
||||
|
||||
//-->
|
||||
</script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
#if $trending_shows
|
||||
<div class="pull-right" style="margin-top: -40px;">
|
||||
<span>Show:</span>
|
||||
<select id="showfilter" class="form-control form-control-inline input-sm">
|
||||
#set $count_trending = len($trending_shows)
|
||||
#set $count_inlibrary = $trending_inlibrary
|
||||
<option value="*" selected="selected">All<%= ' (%d)' % count_trending %></option>
|
||||
<option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_trending - count_inlibrary) %></option>
|
||||
<option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option>
|
||||
</select>
|
||||
|
||||
<span style="margin-left:12px">Sort By:</span>
|
||||
<select id="showsort" class="form-control form-control-inline input-sm">
|
||||
<option value="name">Name</option>
|
||||
<option value="original" selected="selected">Original</option>
|
||||
<option value="votes">Votes</option>
|
||||
<option value="rating">% Rating</option>
|
||||
<option value="rating_votes">% Rating > Votes</option>
|
||||
</select>
|
||||
|
||||
<span style="margin-left:12px">Sort Order:</span>
|
||||
<select id="showsortdirection" class="form-control form-control-inline input-sm">
|
||||
<option value="asc" selected="selected">Asc</option>
|
||||
<option value="desc">Desc</option>
|
||||
</select>
|
||||
</div>
|
||||
#end if
|
||||
|
||||
<div id="container">
|
||||
|
||||
#if None is $trending_shows
|
||||
<div class="trakt_show" style="width:100%; margin-top:20px">
|
||||
<p class="red-text">Trakt API did not return results, this can happen from time to time.
|
||||
<br /><br />This view should auto refresh every 10 mins.</p>
|
||||
</div>
|
||||
#else
|
||||
#for $cur_show in $trending_shows:
|
||||
|
||||
#set $image = re.sub(r'(.*)/original/(.+)$', r'\1/thumb/\2', $cur_show['images']['poster'], 0, re.IGNORECASE | re.MULTILINE)
|
||||
|
||||
<div class="trakt_show <%= ('notinlibrary', 'inlibrary')[':' in cur_show['show_id']] %>" data-name="$cur_show['title']" data-rating="$cur_show['ratings']['percentage']" data-votes="$cur_show['ratings']['votes']">
|
||||
<div class="traktContainer">
|
||||
<div class="trakt-image">
|
||||
<a class="trakt-image" href="<%= anon_url('https://trakt.tv', cur_show['url']) %>" target="_blank"><img alt="" class="trakt-image" src="${image}" /></a>
|
||||
</div>
|
||||
|
||||
<div class="show-title">
|
||||
<%= (cur_show['title'], '<span> </span>')[ '' == cur_show['title']] %>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
<p>$cur_show['ratings']['percentage']% <img src="$sbRoot/images/heart.png"></p>
|
||||
<i>$cur_show['ratings']['votes'] votes</i>
|
||||
|
||||
<div class="traktShowTitleIcons">
|
||||
#if ':' in $cur_show['show_id']:
|
||||
<p style="line-height: 1.5; padding: 2px 5px 3px" title="<%= '%s added' % ('TVRage', 'theTVDB')['1' == cur_show['show_id'][:1]] %>">In library</p>
|
||||
#else
|
||||
#set $encoded_show_title = urllib.quote($cur_show['title'].encode("utf-8"))
|
||||
<a href="$sbRoot/home/addShows/addTraktShow?indexer_id=${cur_show['show_id']}&showName=${encoded_show_title}" class="btn btn-xs">Add Show</a>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
#end for
|
||||
#end if
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
|
||||
//-->
|
||||
</script>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
|
@ -79,7 +79,7 @@
|
|||
<span class="component-title">Scene numbering</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="scene" id="scene" #if $sickbeard.SCENE_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<p>search for episodes that are numbered by scene groups instead of by the TV network</p>
|
||||
<p>search for episodes numbered by scene groups instead of by the TV network<span id="scene-maps-found" style="display:none" class="grey-text"> (scene numbers found)</span></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -101,7 +101,7 @@
|
|||
<label for="anime">
|
||||
<span class="component-title">Show is anime</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="anime" id="anime" #if $sickbeard.ANIME_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<input type="checkbox" name="anime" id="anime" #if $sickbeard.ANIME_DEFAULT or $kwargs.get('is_anime') then "checked=\"checked\"" else ""# />
|
||||
<p>enable if this show is anime and episode releases are named ... <em class="grey-text">Show.265</em> instead of <em class="grey-text">Show.S02E03</em></p>
|
||||
</span>
|
||||
</label>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<span class="component-title input">Quality</span>
|
||||
<span class="component-desc">
|
||||
#set $selected = None
|
||||
<select id="qualityPreset" class="form-control form-control-inline input-sm">
|
||||
<select id="qualityPreset" name="quality_preset" class="form-control form-control-inline input-sm">
|
||||
<option value="0">Custom</option>
|
||||
#for $curPreset in sorted($qualityPresets):
|
||||
<option value="$curPreset" #if $curPreset == $overall_quality then "selected=\"selected\"" else ""# #if $qualityPresetStrings[$curPreset].endswith("0p") then "style=\"padding-left: 15px;\"" else ""#>$qualityPresetStrings[$curPreset]</option>
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="robots" content="noindex, nofollow, noarchive, nocache, noodp, noydir, noimageindex, nosnippet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<title>SickGear - $title</title>
|
||||
|
||||
<title>SickGear - BRANCH:[$sickbeard.BRANCH] - $title</title>
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
|
@ -24,7 +25,7 @@
|
|||
<link rel="apple-touch-icon" sizes="76x76" href="$sbRoot/images/ico/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="$sbRoot/images/ico/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="$sbRoot/images/ico/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="$sbRoot/images/ico/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="$sbRoot/images/ico/apple-touch-icon-57x57.png">
|
||||
<link rel="icon" type="image/png" href="$sbRoot/images/ico/favicon-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="$sbRoot/images/ico/favicon-160x160.png" sizes="160x160">
|
||||
<link rel="icon" type="image/png" href="$sbRoot/images/ico/favicon-96x96.png" sizes="96x96">
|
||||
|
@ -34,92 +35,47 @@
|
|||
<meta name="msapplication-TileImage" content="$sbRoot/images/ico/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="$sbRoot/css/browserconfig.xml">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/browser.css?$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery-ui-1.10.4.custom.css?$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery.qtip-2.2.1.min.css?$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/pnotify.custom.min.css?$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/${sickbeard.THEME_NAME}.css?$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?v=$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/browser.css?v=$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery-ui-1.10.4.custom.css?v=$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery.qtip-2.2.1.min.css?v=$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/pnotify.custom.min.css?v=$sbPID" />
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?v=$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/${sickbeard.THEME_NAME}.css?v=$sbPID" />
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/bootstrap.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/bootstrap-hover-dropdown.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-ui-1.10.4.custom.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.cookie.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.cookiejar.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.json-2.2.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.selectboxes.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter-2.17.7.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter.widgets-2.17.7.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.qtip-2.2.1.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/pnotify.custom.min.js"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.form-3.35.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.ui.touch-punch-0.2.2.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/isotope.pkgd.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/imagesloaded.pkgd.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.confirm.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/script.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/bootstrap.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/bootstrap-hover-dropdown.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-ui-1.10.4.custom.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.cookie.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.cookiejar.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.json-2.2.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.selectboxes.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter-2.17.7.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter.widgets-2.17.7.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.qtip-2.2.1.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/pnotify.custom.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.form-3.35.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.ui.touch-punch-0.2.2.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/isotope.pkgd.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/imagesloaded.pkgd.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.confirm.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/script.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/inc_top.js?v=$sbPID"></script>
|
||||
#if $sickbeard.FUZZY_DATING
|
||||
<script type="text/javascript" src="$sbRoot/js/moment/moment.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/fuzzyMoment.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/moment/moment.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/fuzzyMoment.js?v=$sbPID"></script>
|
||||
#end if
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
var sbRoot = '$sbRoot', anonURL = '$sickbeard.ANON_REDIRECT', themeSpinner = '#echo ('', '-dark')['dark' == $sickbeard.THEME_NAME]#',
|
||||
top_image_html = '<img src="$sbRoot/images/top.gif" width="31" height="11" alt="Jump to top" />';
|
||||
top_image_html = '<img src="$sbRoot/images/top.gif" width="31" height="11" alt="Jump to top" />', topmenu = '$topmenu';
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.scrolltopcontrol-1.1.js"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/browser.js"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/ajaxNotifications.js"></script>
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
#raw
|
||||
function initActions(){
|
||||
$('#SubMenu a[href*="/home/restart/"]').addClass('btn restart').html('<i class="sgicon-restart"></i>Restart');
|
||||
$('#SubMenu a[href*="/home/shutdown/"]').addClass('btn shutdown').html('<i class="sgicon-shutdown"></i>Shutdown');
|
||||
$('#SubMenu a[href*="/home/logout/"]').addClass('btn').html('<i class="sgicon-logout"></i>Logout');
|
||||
$('#SubMenu a:contains("Edit")').addClass('btn').html('<i class="sgicon-edit"></i>Edit');
|
||||
$('#SubMenu a:contains("Remove")').addClass('btn remove').html('<i class="sgicon-delete"></i>Remove');
|
||||
$('#SubMenu a:contains("Clear History")').addClass('btn clearhistory').html('<i class="sgicon-delete"></i>Clear History');
|
||||
$('#SubMenu a:contains("Trim History")').addClass('btn trimhistory').html('<i class="sgicon-trim"></i>Trim History');
|
||||
$('#SubMenu a[href$="/errorlogs/clearerrors/"]').addClass('btn').html('<i class="sgicon-delete"></i>Clear Errors');
|
||||
$('#SubMenu a:contains("Re-scan")').addClass('btn').html('<i class="sgicon-refresh"></i>Re-scan');
|
||||
$('#SubMenu a:contains("Backlog Overview")').addClass('btn').html('<i class="sgicon-backlog"></i>Backlog Overview');
|
||||
$('#SubMenu a[href$="/home/updatePLEX/"]').addClass('btn').html('<i class="sgicon-plex"></i>Update PLEX');
|
||||
$('#SubMenu a:contains("Force")').addClass('btn').html('<i class="sgicon-fullupdate"></i>Force Full Update');
|
||||
$('#SubMenu a:contains("Rename")').addClass('btn').html('<i class="sgicon-rename"></i>Preview Rename');
|
||||
$('#SubMenu a[href$="/config/subtitles/"]').addClass('btn').html('<i class="sgicon-subtitles"></i>Search Subtitles');
|
||||
$('#SubMenu a[href*="/home/subtitleShow"]').addClass('btn').html('<i class="sgicon-subtitles"></i>Download Subtitles');
|
||||
$('#SubMenu a:contains("Anime")').addClass('btn').html('<i class="sgicon-anime"></i>Anime');
|
||||
$('#SubMenu a:contains("Settings")').addClass('btn').html('<i class="sgicon-search"></i>Search Settings');
|
||||
$('#SubMenu a:contains("Provider")').addClass('btn').html('<i class="sgicon-search"></i>Search Providers');
|
||||
$('#SubMenu a:contains("General")').addClass('btn').html('<i class="sgicon-config"></i>General');
|
||||
$('#SubMenu a:contains("Episode Status")').addClass('btn').html('<i class="sgicon-episodestatus"></i>Episode Status Management');
|
||||
$('#SubMenu a:contains("Missed Subtitle")').addClass('btn').html('<i class="sgicon-subtitles"></i>Missed Subtitles');
|
||||
$('#SubMenu a[href$="/home/addShows/"]').addClass('btn').html('<i class="sgicon-addshow"></i>Add Show');
|
||||
$('#SubMenu a:contains("Processing")').addClass('btn').html('<i class="sgicon-postprocess"></i>Post-Processing');
|
||||
$('#SubMenu a:contains("Manage Searches")').addClass('btn').html('<i class="sgicon-search"></i>Manage Searches');
|
||||
$('#SubMenu a:contains("Manage Torrents")').addClass('btn').html('<i class="sgicon-bittorrent"></i>Manage Torrents');
|
||||
$('#SubMenu a:contains("Show Queue Overview")').addClass('btn').html('<i class="sgicon-showqueue"></i>Show Queue Overview');
|
||||
$('#SubMenu a[href$="/manage/failedDownloads/"]').addClass('btn').html('<i class="sgicon-failed"></i>Failed Downloads');
|
||||
$('#SubMenu a:contains("Notification")').addClass('btn').html('<i class="sgicon-notification"></i>Notifications');
|
||||
$('#SubMenu a:contains("Update show in XBMC")').addClass('btn').html('<i class="sgicon-xbmc"></i>Update show in XBMC');
|
||||
$('#SubMenu a[href$="/home/updateXBMC/"]').addClass('btn').html('<i class="sgicon-xbmc"></i>Update XBMC');
|
||||
$('#SubMenu a:contains("Update show in Kodi")').addClass('btn').html('<i class="sgicon-kodi"></i>Update show in Kodi');
|
||||
$('#SubMenu a[href$="/home/updateKODI/"]').addClass('btn').html('<i class="sgicon-kodi"></i>Update Kodi');
|
||||
}
|
||||
#end raw
|
||||
|
||||
\$(document).ready(function(){
|
||||
initActions();
|
||||
\$('#NAV$topmenu').addClass('active');
|
||||
\$('.dropdown-toggle').dropdownHover();
|
||||
});
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/confirmations.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/confirmations.js?v=$sbPID"></script>
|
||||
</head>
|
||||
#set $tab = 4
|
||||
#set $body_attr = ''
|
||||
|
@ -178,11 +134,6 @@
|
|||
#if $sickbeard.USE_KODI and $sickbeard.KODI_HOST != ''
|
||||
<li><a href="$sbRoot/home/updateKODI/" tabindex="$tab#set $tab += 1#"><i class="sgicon-kodi"></i>Update Kodi</a></li>
|
||||
#end if
|
||||
#if $sickbeard.USE_TORRENTS and $sickbeard.TORRENT_METHOD != 'blackhole' \
|
||||
and ($sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'https' \
|
||||
or not $sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'http:')
|
||||
<li><a href="$sbRoot/manage/manageTorrents/" tabindex="$tab#set $tab += 1#"><i class="sgicon-bittorrent"></i>Manage Torrents</a></li>
|
||||
#end if
|
||||
#if $sickbeard.USE_FAILED_DOWNLOADS
|
||||
<li><a href="$sbRoot/manage/failedDownloads/" tabindex="$tab#set $tab += 1#"><i class="sgicon-failed"></i>Failed Downloads</a></li>
|
||||
#end if
|
||||
|
@ -217,9 +168,11 @@
|
|||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="sgicon-updatecheck"></i>Force Version Check</a></li>
|
||||
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="sgicon-updatecheck"></i>Check for Updates</a></li>
|
||||
<li><a href="$sbRoot/home/viewchanges" tabindex="$tab#set $tab += 1#"><i class="sgicon-log"></i>View Changes</a></li>
|
||||
<li class="divider"></li>
|
||||
#if $sickbeard.WEB_USERNAME or $sickbeard.WEB_PASSWORD
|
||||
<li><a href="$sbRoot/logout" class="confirm logout" tabindex="$tab#set $tab += 1#"><i class="sgicon-logout"></i>Logout</a></li>
|
||||
<li><a href="$sbRoot/logout" class="confirm logout" tabindex="$tab#set $tab += 1#"><i class="sgicon-logout"></i>Logout</a></li>
|
||||
#end if
|
||||
<li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart" tabindex="$tab#set $tab += 1#"><i class="sgicon-restart"></i>Restart</a></li>
|
||||
<li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm shutdown" tabindex="$tab#set $tab += 1#"><i class="sgicon-shutdown"></i>Shutdown</a></li>
|
||||
|
@ -259,6 +212,14 @@
|
|||
|
||||
#end if
|
||||
##
|
||||
#if sys.version_info < (2, 7, 9):
|
||||
<div class="alert alert-danger upgrade-notification" role="alert">
|
||||
<span>
|
||||
SickGear will be dropping support for Python 2.7.8 and below. We recommend updating to latest version:
|
||||
<a href="https://www.python.org/downloads/" onclick="window.open(this.href); return false;">Download here</a>
|
||||
</span>
|
||||
</div>
|
||||
#end if
|
||||
#if $sickbeard.NEWEST_VERSION_STRING
|
||||
<div class="alert alert-success upgrade-notification" role="alert">
|
||||
<span>$sickbeard.NEWEST_VERSION_STRING</span>
|
||||
|
|
|
@ -84,7 +84,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
|
|||
});
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/massUpdate.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/massUpdate.js?v=$sbPID"></script>
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
##
|
||||
|
@ -28,7 +28,7 @@
|
|||
<select name="whichStatus" class="form-control form-control-inline input-sm" style="margin:0 10px">
|
||||
|
||||
#for $curStatus in [$common.SKIPPED, $common.UNKNOWN, $common.SNATCHED, $common.WANTED, $common.ARCHIVED, $common.IGNORED]:
|
||||
<option value="$curStatus">$common.statusStrings[$curStatus]</option>
|
||||
<option value="$curStatus"#echo ('', ' selected="selected"')[$curStatus == $default_manage]#>$common.statusStrings[$curStatus]</option>
|
||||
#end for
|
||||
|
||||
</select>
|
||||
|
@ -36,8 +36,10 @@
|
|||
</form>
|
||||
##
|
||||
#else
|
||||
#if $whichStatus in ($common.ARCHIVED, $common.IGNORED, $common.SNATCHED):
|
||||
#if $whichStatus in ($common.ARCHIVED, $common.IGNORED):
|
||||
#set $row_class = 'good'
|
||||
#elif $whichStatus == $common.SNATCHED:
|
||||
#set $row_class = 'snatched'
|
||||
#else
|
||||
#set $row_class = $common.Overview.overviewStrings[$whichStatus]
|
||||
#end if
|
||||
|
@ -51,7 +53,7 @@
|
|||
$statusList.append($common.FAILED)
|
||||
#end if
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/manageEpisodeStatuses.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/manageEpisodeStatuses.js?v=$sbPID"></script>
|
||||
|
||||
<form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
|
||||
<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus">
|
||||
|
@ -108,4 +110,4 @@
|
|||
</form>
|
||||
|
||||
#end if
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
});
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript" src="$sbRoot/js/failedDownloads.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/failedDownloads.js?v=$sbPID"></script>
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/manageSearches.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/manageSearches.js?v=$sbPID"></script>
|
||||
<div id="content800">
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
#set $initial_quality = $common.SD
|
||||
#end if
|
||||
#set $anyQualities, $bestQualities = $common.Quality.splitQuality($initial_quality)
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/massEdit.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/massEdit.js?v=$sbPID"></script>
|
||||
|
||||
<form action="massEditSubmit" method="post">
|
||||
<input type="hidden" name="toEdit" value="$showList">
|
||||
|
@ -60,11 +60,11 @@
|
|||
</select>
|
||||
</div><br />
|
||||
|
||||
<div id="customQuality">
|
||||
<div id="customQuality" class="show-if-quality-custom">
|
||||
<div class="manageCustom pull-left">
|
||||
<h4 style="font-size:14px">Initial</h4>
|
||||
#set $anyQualityList = filter(lambda x: x > $common.Quality.NONE, $common.Quality.qualityStrings)
|
||||
<select id="anyQualities" name="anyQualities" multiple="multiple" size="len($anyQualityList)">
|
||||
<select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)">
|
||||
#for $curQuality in sorted($anyQualityList):
|
||||
<option value="$curQuality" #if $curQuality in $anyQualities then $selected else ''#>$common.Quality.qualityStrings[$curQuality]</option>
|
||||
#end for
|
||||
|
@ -73,7 +73,7 @@
|
|||
<div class="manageCustom pull-left">
|
||||
<h4 style="font-size:14px">Upgrade to</h4>
|
||||
#set $bestQualityList = filter(lambda x: x > $common.Quality.SDTV, $common.Quality.qualityStrings)
|
||||
<select id="bestQualities" name="bestQualities" multiple="multiple" size="len($bestQualityList)">
|
||||
<select id="bestQualities" name="bestQualities" multiple="multiple" size="$len($bestQualityList)">
|
||||
#for $curQuality in sorted($bestQualityList):
|
||||
<option value="$curQuality" #if $curQuality in $bestQualities then $selected else ''#>$common.Quality.qualityStrings[$curQuality]</option>
|
||||
#end for
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/manageShowQueueOverview.js?$sbPID" xmlns="http://www.w3.org/1999/html"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/manageShowQueueOverview.js?v=$sbPID" xmlns="http://www.w3.org/1999/html"></script>
|
||||
<div id="content800">
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
#from lib import subliminal
|
||||
#from sickbeard import common
|
||||
##
|
||||
#set global $title = 'Episode Overview'
|
||||
#set global $header = 'Episode Overview'
|
||||
#set global $title = 'Missing Subtitles'
|
||||
#set global $header = 'Missing Subtitles'
|
||||
#set global $sbPath = '..'
|
||||
#set global $topmenu = 'manage'
|
||||
##
|
||||
|
@ -12,9 +12,9 @@
|
|||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<div id="content960">
|
||||
#if $varExists('header')
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
##
|
||||
|
@ -41,7 +41,7 @@
|
|||
</form>
|
||||
#else
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/manageSubtitleMissed.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/manageSubtitleMissed.js?v=$sbPID"></script>
|
||||
<input type="hidden" id="selectSubLang" name="selectSubLang" value="$whichSubs">
|
||||
|
||||
<form action="$sbRoot/manage/downloadSubtitleMissed" method="post">
|
||||
|
@ -64,4 +64,4 @@
|
|||
</form>
|
||||
#end if
|
||||
</div>
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
#import sickbeard
|
||||
#import datetime
|
||||
#from sickbeard.common import *
|
||||
##
|
||||
#set global $title = 'Manage Torrents'
|
||||
#set global $header = 'Manage Torrents'
|
||||
#set global $sbPath = '..'
|
||||
#set global $topmenu = 'manage'
|
||||
##
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?$sbPID"></script>
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
$info_download_station
|
||||
<iframe id="extFrame" src="$webui_url" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
|
@ -21,8 +21,8 @@ sbHost = "$curSBHost";
|
|||
//-->
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/restart.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery-1.8.3.min.js?v=$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/restart.js?v=$sbPID"></script>
|
||||
|
||||
#set themeSpinner = '-dark' if 'dark' == themeSpinner else ''
|
||||
<h2>Performing Restart</h2>
|
||||
|
|
20
gui/slick/interfaces/default/viewchanges.tmpl
Normal file
|
@ -0,0 +1,20 @@
|
|||
#import sickbeard
|
||||
##
|
||||
#set global $header = 'Changes'
|
||||
#set global $title = 'Changes'
|
||||
#set global $topmenu = ''
|
||||
##
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<h1 class="header">$header</h1>
|
||||
|
||||
<div class="align-left" style="margin:30px 0">
|
||||
<div id="changes">
|
||||
#for $i, $change in $enumerate($changelist)##if 'rel' == $change['type']#
|
||||
<div class="release#echo ('', ' old')[bool($i)]#"><span class="ver">$change['ver']</span> <span class="change-date grey-text">$change['date']</span></div>
|
||||
#else# <div><span class="btn-text change-$change['type'].lower()">$change['type']</span> <span class="change-text">$change['text']</span></div>
|
||||
#end if##end for#
|
||||
</div>
|
||||
</div>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
|
@ -28,6 +28,7 @@ $(document).ready(function(){
|
|||
|
||||
window.location.href = sbRoot + '/home/addShows/addExistingShows'
|
||||
+ '?promptForSettings=' + ($('#promptForSettings').prop('checked') ? 'on' : 'off')
|
||||
+ (undefined !== $.sgSid && 0 < $.sgSid.length ? '&sid=' + $.sgSid : '')
|
||||
+ '&shows_to_add=' + dirArr.join('&shows_to_add=');
|
||||
});
|
||||
|
||||
|
@ -47,7 +48,7 @@ $(document).ready(function(){
|
|||
+ ' height="32" width="32" />'
|
||||
+ ' scanning parent folders...');
|
||||
|
||||
$.get(sbRoot + '/home/addShows/massAddTable',
|
||||
$.get(sbRoot + '/home/addShows/massAddTable' + (undefined !== $.sgHashDir && 0 < $.sgHashDir.length ? '?hash_dir=' + $.sgHashDir : ''),
|
||||
url,
|
||||
function(data){
|
||||
$('#tableDiv').html(data);
|
||||
|
@ -90,4 +91,4 @@ $(document).ready(function(){
|
|||
$('html,body').animate({scrollTop: 0}, 1000);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,8 @@ $(document).ready(function(){
|
|||
default_flatten_folders: $('#flatten_folders').prop('checked'),
|
||||
default_scene: $('#scene').prop('checked'),
|
||||
default_subtitles: $('#subtitles').prop('checked'),
|
||||
default_anime: $('#anime').prop('checked')
|
||||
default_anime: $('#anime').prop('checked'),
|
||||
default_tag: $('#tag').val()
|
||||
});
|
||||
|
||||
new PNotify({
|
||||
|
@ -32,7 +33,7 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
$('#statusSelect, #qualityPreset, #anyQualities, #bestQualities, #wanted_begin, #wanted_latest,'
|
||||
+ ' #flatten_folders, #scene, #subtitles, #anime').change(function() {
|
||||
+ ' #flatten_folders, #scene, #subtitles, #anime, #tag').change(function() {
|
||||
$('#saveDefaultsButton').attr('disabled', false);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,181 +1,208 @@
|
|||
;(function($) {
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
$.Browser = {
|
||||
defaults: {
|
||||
title: 'Choose Directory',
|
||||
url: sbRoot + '/browser/',
|
||||
autocompleteURL: sbRoot + '/browser/complete',
|
||||
includeFiles: 0
|
||||
}
|
||||
};
|
||||
$.Browser = {
|
||||
defaults: {
|
||||
title: 'Choose Directory (or enter manually)',
|
||||
url: sbRoot + '/browser/',
|
||||
autocompleteURL: sbRoot + '/browser/complete',
|
||||
includeFiles: 0,
|
||||
showBrowseButton: !0
|
||||
}
|
||||
};
|
||||
|
||||
var fileBrowserDialog, currentBrowserPath, currentRequest = null;
|
||||
var fileBrowserDialog, currentBrowserPath, currentRequest = null;
|
||||
|
||||
function browse(path, endpoint, includeFiles) {
|
||||
function browse(path, endpoint, includeFiles) {
|
||||
|
||||
if (currentBrowserPath == path) {
|
||||
return;
|
||||
}
|
||||
if (path === currentBrowserPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentBrowserPath = path;
|
||||
currentBrowserPath = path;
|
||||
|
||||
if (currentRequest) {
|
||||
currentRequest.abort();
|
||||
}
|
||||
if (currentRequest) {
|
||||
currentRequest.abort();
|
||||
}
|
||||
|
||||
fileBrowserDialog.dialog('option', 'dialogClass', 'browserDialog busy');
|
||||
fileBrowserDialog.dialog('option', 'dialogClass', 'browserDialog busy');
|
||||
|
||||
currentRequest = $.getJSON(endpoint, { path: path, includeFiles: includeFiles }, function (data) {
|
||||
fileBrowserDialog.empty();
|
||||
var first_val = data[0];
|
||||
var i = 0;
|
||||
var list, link = null;
|
||||
data = $.grep(data, function (value) {
|
||||
return i++ != 0;
|
||||
});
|
||||
$('<h2>').text(first_val.current_path).appendTo(fileBrowserDialog);
|
||||
list = $('<ul>').appendTo(fileBrowserDialog);
|
||||
$.each(data, function (i, entry) {
|
||||
link = $("<a href='javascript:void(0)' />").click(function () { browse(entry.path, endpoint, includeFiles); }).text(entry.name);
|
||||
$('<span class="ui-icon ui-icon-folder-collapsed"></span>').prependTo(link);
|
||||
link.hover(
|
||||
function () {$("span", this).addClass("ui-icon-folder-open"); },
|
||||
function () {$("span", this).removeClass("ui-icon-folder-open"); }
|
||||
);
|
||||
link.appendTo(list);
|
||||
});
|
||||
$("a", list).wrap('<li class="ui-state-default ui-corner-all">');
|
||||
fileBrowserDialog.dialog('option', 'dialogClass', 'browserDialog');
|
||||
});
|
||||
}
|
||||
currentRequest = $.getJSON(endpoint, {path: path, includeFiles: includeFiles}, function(data){
|
||||
fileBrowserDialog.empty();
|
||||
var firstVal = data[0], i = 0, list, link = null;
|
||||
data = $.grep(data, function(){
|
||||
return i++ != 0;
|
||||
});
|
||||
$('<input type="text" class="form-control input-sm">')
|
||||
.val(firstVal.currentPath)
|
||||
.on('keypress', function(e){
|
||||
if (13 === e.which) {
|
||||
browse(e.target.value, endpoint, includeFiles);
|
||||
}
|
||||
})
|
||||
.appendTo(fileBrowserDialog)
|
||||
.fileBrowser({showBrowseButton: !1})
|
||||
.on('autocompleteselect',
|
||||
function(e, ui){browse(ui.item.value, endpoint, includeFiles);
|
||||
});
|
||||
|
||||
$.fn.nFileBrowser = function (callback, options) {
|
||||
options = $.extend({}, $.Browser.defaults, options);
|
||||
list = $('<ul>').appendTo(fileBrowserDialog);
|
||||
$.each(data, function(i, entry){
|
||||
link = $('<a href="javascript:void(0)">').on('click',
|
||||
function(){
|
||||
if (entry.isFile) {
|
||||
currentBrowserPath = entry.path;
|
||||
$('.browserDialog .ui-button:contains("Ok")').click();
|
||||
} else {
|
||||
browse(entry.path, endpoint, includeFiles);
|
||||
}
|
||||
}).text(entry.name);
|
||||
|
||||
// make a fileBrowserDialog object if one doesn't exist already
|
||||
if (!fileBrowserDialog) {
|
||||
if (entry.isFile) {
|
||||
link.prepend('<span class="ui-icon ui-icon-blank"></span>');
|
||||
} else {
|
||||
link.prepend('<span class="ui-icon ui-icon-folder-collapsed"></span>')
|
||||
.on('mouseenter', function(){$('span', this).addClass('ui-icon-folder-open');})
|
||||
.on('mouseleave', function(){$('span', this).removeClass('ui-icon-folder-open');});
|
||||
}
|
||||
link.appendTo(list);
|
||||
});
|
||||
$('a', list).wrap('<li class="ui-state-default ui-corner-all">');
|
||||
fileBrowserDialog.dialog('option', 'dialogClass', 'browserDialog');
|
||||
});
|
||||
}
|
||||
|
||||
// set up the jquery dialog
|
||||
fileBrowserDialog = $('<div id="fileBrowserDialog" style="display:hidden"></div>').appendTo('body').dialog({
|
||||
dialogClass: 'browserDialog',
|
||||
title: options.title,
|
||||
position: ['center', 40],
|
||||
minWidth: Math.min($(document).width() - 80, 650),
|
||||
height: Math.min($(document).height() - 80, $(window).height() - 80),
|
||||
maxHeight: Math.min($(document).height() - 80, $(window).height() - 80),
|
||||
maxWidth: $(document).width() - 80,
|
||||
modal: true,
|
||||
autoOpen: false
|
||||
});
|
||||
}
|
||||
$.fn.nFileBrowser = function(callback, options){
|
||||
options = $.extend({}, $.Browser.defaults, options);
|
||||
|
||||
fileBrowserDialog.dialog('option', 'buttons', [
|
||||
{
|
||||
text: "Ok",
|
||||
"class": "btn",
|
||||
click: function() {
|
||||
// store the browsed path to the associated text field
|
||||
callback(currentBrowserPath, options);
|
||||
$(this).dialog("close");
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "Cancel",
|
||||
"class": "btn",
|
||||
click: function() {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
}
|
||||
]);
|
||||
// make a fileBrowserDialog object if one doesn't exist already
|
||||
if (fileBrowserDialog) {
|
||||
fileBrowserDialog.dialog('option', 'title', options.title);
|
||||
} else {
|
||||
// set up the jquery dialog
|
||||
var docWidth = $(document).width(), dlgWidth = Math.min(docWidth - 80, 650),
|
||||
docHeight = $(document).height() - 80, winHeight = $(window).height() - 80;
|
||||
fileBrowserDialog = $('<div id="fileBrowserDialog" style="display:none"></div>').appendTo('body').dialog({
|
||||
dialogClass: 'browserDialog',
|
||||
title: options.title,
|
||||
position: [(docWidth - dlgWidth)/2, 60],
|
||||
minWidth: dlgWidth,
|
||||
height: Math.min(docHeight, winHeight),
|
||||
maxHeight: Math.min(docHeight, winHeight),
|
||||
maxWidth: docWidth - 80,
|
||||
modal: true,
|
||||
autoOpen: false
|
||||
});
|
||||
}
|
||||
|
||||
// set up the browser and launch the dialog
|
||||
var initialDir = '';
|
||||
if (options.initialDir) {
|
||||
initialDir = options.initialDir;
|
||||
}
|
||||
fileBrowserDialog.dialog('option', 'buttons',
|
||||
[{
|
||||
text: 'Ok',
|
||||
'class': 'btn',
|
||||
click: function(){
|
||||
// store the browsed path to the associated text field
|
||||
callback(currentBrowserPath, options);
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Cancel',
|
||||
'class': 'btn',
|
||||
click: function(){
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}]);
|
||||
|
||||
browse(initialDir, options.url, options.includeFiles);
|
||||
fileBrowserDialog.dialog('open');
|
||||
// set up the browser and launch the dialog
|
||||
var initialDir = '';
|
||||
if (options.initialDir) {
|
||||
initialDir = options.initialDir;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
browse(initialDir, options.url, options.includeFiles);
|
||||
fileBrowserDialog.dialog('open');
|
||||
|
||||
$.fn.fileBrowser = function (options) {
|
||||
options = $.extend({}, $.Browser.defaults, options);
|
||||
// text field used for the result
|
||||
options.field = $(this);
|
||||
return false;
|
||||
};
|
||||
|
||||
if (options.field.autocomplete && options.autocompleteURL) {
|
||||
var query = '';
|
||||
options.field.autocomplete({
|
||||
position: { my : "top", at: "bottom", collision: "flipfit" },
|
||||
source: function (request, response) {
|
||||
//keep track of user submitted search term
|
||||
query = $.ui.autocomplete.escapeRegex(request.term, options.includeFiles);
|
||||
$.ajax({
|
||||
url: options.autocompleteURL,
|
||||
data: request,
|
||||
dataType: "json",
|
||||
success: function (data, item) {
|
||||
//implement a startsWith filter for the results
|
||||
var matcher = new RegExp("^" + query, "i");
|
||||
var a = $.grep(data, function (item, index) {
|
||||
return matcher.test(item);
|
||||
});
|
||||
response(a);
|
||||
}
|
||||
});
|
||||
},
|
||||
open: function (event, ui) {
|
||||
$(".ui-autocomplete li.ui-menu-item a").removeClass("ui-corner-all");
|
||||
$(".ui-autocomplete li.ui-menu-item:odd a").addClass("ui-menu-item-alternate");
|
||||
}
|
||||
})
|
||||
.data("ui-autocomplete")._renderItem = function (ul, item) {
|
||||
//highlight the matched search term from the item -- note that this is global and will match anywhere
|
||||
var result_item = item.label;
|
||||
var x = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi");
|
||||
result_item = result_item.replace(x, function (FullMatch, n) {
|
||||
return '<b>' + FullMatch + '</b>';
|
||||
});
|
||||
return $("<li></li>")
|
||||
.data("ui-autocomplete-item", item)
|
||||
.append("<a class='nowrap'>" + result_item + "</a>")
|
||||
.appendTo(ul);
|
||||
};
|
||||
}
|
||||
$.fn.fileBrowser = function(options){
|
||||
options = $.extend({}, $.Browser.defaults, options);
|
||||
// text field used for the result
|
||||
options.field = $(this);
|
||||
|
||||
var initialDir, path, callback, ls = false;
|
||||
// if the text field is empty and we're given a key then populate it with the last browsed value from localStorage
|
||||
try { ls = !!(localStorage.getItem); } catch (e) {}
|
||||
if (ls && options.key) {
|
||||
path = localStorage['fileBrowser-' + options.key];
|
||||
}
|
||||
if (options.key && options.field.val().length == 0 && (path)) {
|
||||
options.field.val(path);
|
||||
}
|
||||
if (options.field.autocomplete && options.autocompleteURL) {
|
||||
var query = '';
|
||||
options.field.autocomplete({
|
||||
position: {my: 'top', at: 'bottom', collision: 'flipfit'},
|
||||
source: function(request, response){
|
||||
//keep track of user submitted search term
|
||||
query = $.ui.autocomplete.escapeRegex(request.term, options.includeFiles);
|
||||
$.ajax({
|
||||
url: options.autocompleteURL,
|
||||
data: request,
|
||||
dataType: 'json',
|
||||
success: function(data){
|
||||
//implement a startsWith filter for the results
|
||||
var matcher = new RegExp('^' + query, 'i');
|
||||
var a = $.grep(data, function(item){
|
||||
return matcher.test(item);
|
||||
});
|
||||
response(a);
|
||||
}
|
||||
});
|
||||
},
|
||||
open: function(){
|
||||
$('.ui-autocomplete li.ui-menu-item a').removeClass('ui-corner-all');
|
||||
$('.ui-autocomplete li.ui-menu-item:odd a').addClass('ui-menu-item-alternate');
|
||||
}
|
||||
}).data('ui-autocomplete')._renderItem = function(ul, item){
|
||||
//highlight the matched search term from the item -- note that this is global and will match anywhere
|
||||
var resultItem = item.label;
|
||||
var x = new RegExp('(?![^&;]+;)(?!<[^<>]*)(' + query + ')(?![^<>]*>)(?![^&;]+;)', 'gi');
|
||||
resultItem = resultItem.replace(x, function(fullMatch){
|
||||
return '<b>' + fullMatch + '</b>';
|
||||
});
|
||||
return $('<li></li>')
|
||||
.data('ui-autocomplete-item', item)
|
||||
.append('<a class="nowrap">' + resultItem + '</a>')
|
||||
.appendTo(ul);
|
||||
};
|
||||
}
|
||||
|
||||
callback = function (path, options) {
|
||||
// store the browsed path to the associated text field
|
||||
options.field.val(path);
|
||||
var path, callback, ls = false;
|
||||
// if empty text field and given a key then populate it with the last browsed value from localStorage
|
||||
try { ls = !!(localStorage.getItem); } catch (e) {}
|
||||
if (ls && options.key) {
|
||||
path = localStorage['fileBrowser-' + options.key];
|
||||
}
|
||||
if (options.key && options.field.val().length == 0 && (path)) {
|
||||
options.field.val(path);
|
||||
}
|
||||
|
||||
// use a localStorage to remember for next time -- no ie6/7
|
||||
if (ls && options.key) {
|
||||
localStorage['fileBrowser-' + options.key] = path;
|
||||
}
|
||||
callback = function(path, options){
|
||||
// store the browsed path to the associated text field
|
||||
options.field.val(path);
|
||||
|
||||
};
|
||||
// use a localStorage to remember for next time -- no ie6/7
|
||||
if (ls && options.key) {
|
||||
localStorage['fileBrowser-' + options.key] = path;
|
||||
}
|
||||
};
|
||||
|
||||
initialDir = options.field.val() || (options.key && path) || '';
|
||||
|
||||
options = $.extend(options, {initialDir: initialDir});
|
||||
|
||||
// append the browse button and give it a click behaviour
|
||||
return options.field.addClass('fileBrowserField').after($('<input type="button" value="Browse…" class="btn btn-inline fileBrowser" />').click(function () {
|
||||
$(this).nFileBrowser(callback, options);
|
||||
return false;
|
||||
}));
|
||||
};
|
||||
options.field.addClass('fileBrowserField');
|
||||
if (options.showBrowseButton) {
|
||||
// append the browse button and give it a click behaviour
|
||||
options.field.after(
|
||||
$('<input type="button" value="Browse…" class="btn btn-inline fileBrowser">').on('click',
|
||||
function(){
|
||||
$(this).nFileBrowser(callback, $.extend(
|
||||
{}, options, {initialDir: options.field.val() || (options.key && path) || ''}
|
||||
));
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
return options.field;
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
$(document).ready(function(){
|
||||
$(document).ready(function(){
|
||||
var loading = '<img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif" height="16" width="16" />';
|
||||
|
||||
$('#testGrowl').click(function () {
|
||||
|
@ -352,37 +352,162 @@ $(document).ready(function(){
|
|||
});
|
||||
});
|
||||
|
||||
$('#testTrakt').click(function () {
|
||||
var trakt_api = $.trim($('#trakt_api').val());
|
||||
var trakt_username = $.trim($('#trakt_username').val());
|
||||
var trakt_password = $.trim($('#trakt_password').val());
|
||||
if (!trakt_api || !trakt_username || !trakt_password) {
|
||||
$('#testTrakt-result').html('Please fill out the necessary fields above.');
|
||||
if (!trakt_api) {
|
||||
$('#trakt_api').addClass('warning');
|
||||
} else {
|
||||
$('#trakt_api').removeClass('warning');
|
||||
}
|
||||
if (!trakt_username) {
|
||||
$('#trakt_username').addClass('warning');
|
||||
} else {
|
||||
$('#trakt_username').removeClass('warning');
|
||||
}
|
||||
if (!trakt_password) {
|
||||
$('#trakt_password').addClass('warning');
|
||||
} else {
|
||||
$('#trakt_password').removeClass('warning');
|
||||
}
|
||||
return;
|
||||
}
|
||||
$('#trakt_api,#trakt_username,#trakt_password').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testTrakt-result').html(loading);
|
||||
$.get(sbRoot + '/home/testTrakt', {'api': trakt_api, 'username': trakt_username, 'password': trakt_password})
|
||||
.done(function (data) {
|
||||
$('#testTrakt-result').html(data);
|
||||
$('#testTrakt').prop('disabled', false);
|
||||
var elTraktAuth = $('#trakt-authenticate'), elTraktAuthResult = $('#trakt-authentication-result');
|
||||
|
||||
function trakt_send_auth(){
|
||||
var elAccountSelect = $('#trakt_accounts'), strCurAccountId = elAccountSelect.find('option:selected').val(),
|
||||
elTraktPin = $('#trakt_pin'), strPin = $.trim(elTraktPin.val());
|
||||
|
||||
elTraktAuthResult.html(loading);
|
||||
|
||||
$.get(sbRoot + '/home/trakt_authenticate', {'pin': strPin, 'account': strCurAccountId})
|
||||
.done(function(data) {
|
||||
elTraktAuth.prop('disabled', !1);
|
||||
elTraktPin.val('');
|
||||
|
||||
var JSONData = $.parseJSON(data);
|
||||
|
||||
elTraktAuthResult.html('Success' == JSONData.result
|
||||
? JSONData.result + ' account: ' + JSONData.account_name
|
||||
: JSONData.result + ' ' + JSONData.error_message);
|
||||
|
||||
if ('Success' == JSONData.result) {
|
||||
var elUpdateRows = $('#trakt-collection').find('tr');
|
||||
if ('new' == strCurAccountId) {
|
||||
elAccountSelect.append($('<option>', {value: JSONData.account_id, text: JSONData.account_id + ' - ' + JSONData.account_name + ' (ok)'}));
|
||||
|
||||
if ('Connect New Pin' == elUpdateRows.eq(0).find('th').last().text()) {
|
||||
elUpdateRows.eq(0).find('th').last().html('Account');
|
||||
elUpdateRows.eq(1).find('th').last().html(JSONData.account_name);
|
||||
elUpdateRows.eq(1).find('th').last().addClass('tid-' + JSONData.account_id);
|
||||
elUpdateRows.has('td').each(function(nRow) {
|
||||
var elCells = $(this).find('td');
|
||||
if (!(nRow % 2)) {
|
||||
var IdLoc = 'update_trakt_' + JSONData.account_id + '_' + elCells.eq(0).find('span').attr('data-loc');
|
||||
elCells.last().html('<input type="checkbox" id="' + IdLoc + '" name="' + IdLoc + '">');
|
||||
} else {
|
||||
elCells.attr('colspan', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
elUpdateRows.eq(0).find('th').last().html('Trakt accounts');
|
||||
elUpdateRows.eq(0).find('th').last().attr('colspan', 1 + parseInt(elUpdateRows.eq(0).find('th').last().attr('colspan'), 10));
|
||||
elUpdateRows.eq(1).find('th').last().after('<th>' + JSONData.account_name + '</th>');
|
||||
elUpdateRows.eq(1).find('th').last().addClass('tid-' + JSONData.account_id);
|
||||
elUpdateRows.has('td').each(function(nRow) {
|
||||
var elCells = $(this).find('td');
|
||||
if (!(nRow % 2)) {
|
||||
var IdLoc = 'update_trakt_' + JSONData.account_id + '_' + elCells.eq(0).find('span').attr('data-loc');
|
||||
elCells.last().after('<td class="opt"><input type="checkbox" id="' + IdLoc + '" name="' + IdLoc + '"></td>');
|
||||
} else {
|
||||
elCells.attr('colspan', 1 + parseInt(elCells.attr('colspan'), 10));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
elAccountSelect.find('option[value=' + strCurAccountId + ']').html(JSONData.account_id + ' - ' + JSONData.account_name + ' (ok)');
|
||||
elUpdateRows.eq(1).find('th[class*="tid-' + JSONData.account_id + '"]').text(JSONData.account_name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
elTraktAuth.click(function(e) {
|
||||
var elTraktPin = $('#trakt_pin');
|
||||
|
||||
elTraktPin.removeClass('warning');
|
||||
if (!$.trim(elTraktPin.val())) {
|
||||
elTraktPin.addClass('warning');
|
||||
elTraktAuthResult.html('Please enter a required PIN above.');
|
||||
} else {
|
||||
var elAccountSelect = $('#trakt_accounts'), elSelected = elAccountSelect.find('option:selected');
|
||||
$(this).prop('disabled', !0);
|
||||
if ('new' != elSelected.val()) {
|
||||
$.confirm({
|
||||
'title' : 'Replace Trakt Account',
|
||||
'message' : 'Are you sure you want to replace <span class="footerhighlight">' + elSelected.text() + '</span> ?<br /><br />',
|
||||
'buttons' : {
|
||||
'Yes' : {
|
||||
'class' : 'green',
|
||||
'action': function() {
|
||||
trakt_send_auth();
|
||||
}
|
||||
},
|
||||
'No' : {
|
||||
'class' : 'red',
|
||||
'action': function() {
|
||||
e.preventDefault();
|
||||
elTraktAuth.prop('disabled', !1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
trakt_send_auth();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#trakt_accounts').change(function() {
|
||||
$('#trakt-delete').prop('disabled', 'new' == $('#trakt_accounts').val());
|
||||
});
|
||||
|
||||
$('#trakt-delete').click(function(e) {
|
||||
var elAccountSelect = $('#trakt_accounts'), elSelected = elAccountSelect.find('option:selected'), that = $(this);
|
||||
|
||||
that.prop('disabled', !0);
|
||||
$.confirm({
|
||||
'title' : 'Remove Trakt Account',
|
||||
'message' : 'Are you sure you want to remove <span class="footerhighlight">' + elSelected.text() + '</span> ?<br /><br />',
|
||||
'buttons' : {
|
||||
'Yes' : {
|
||||
'class' : 'green',
|
||||
'action': function() {
|
||||
$.get(sbRoot + '/home/trakt_delete', {'accountid': elSelected.val()})
|
||||
.done(function(data) {
|
||||
that.prop('disabled', !1);
|
||||
var JSONData = $.parseJSON(data);
|
||||
if ('Success' == JSONData.result) {
|
||||
var elCollection = $('#trakt-collection'), elUpdateRows = elCollection.find('tr'),
|
||||
header = elCollection.find('th[class*="tid-' + JSONData.account_id + '"]'),
|
||||
num_acc = parseInt(JSONData.num_accounts, 10);
|
||||
|
||||
elUpdateRows.eq(0).find('th').last().html(!num_acc && '<i>Connect New Pin</i>' ||
|
||||
(1 < num_acc ? 'Trakt accounts' : 'Account'));
|
||||
elUpdateRows.find('th[colspan]').attr('colspan', 1 < num_acc ? num_acc : 1);
|
||||
|
||||
!num_acc && header.html('..') || header.remove();
|
||||
|
||||
var elInputs = elUpdateRows.find('input[id*=update_trakt_' + JSONData.account_id + ']');
|
||||
!num_acc && elInputs.parent().html('..') || elInputs.parent().remove();
|
||||
|
||||
elUpdateRows.find('td[colspan]').each(function() {
|
||||
$(this).attr('colspan', (num_acc ? 1 + num_acc : 2))
|
||||
});
|
||||
|
||||
elSelected.remove();
|
||||
$('#trakt_accounts').change();
|
||||
|
||||
elTraktAuthResult.html('Deleted account: ' + JSONData.account_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
'No' : {
|
||||
'class' : 'red',
|
||||
'action': function() {
|
||||
e.preventDefault();
|
||||
$('#trakt_accounts').change();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#testEmail').click(function () {
|
||||
|
|
|
@ -11,7 +11,7 @@ $(document).ready(function () {
|
|||
|
||||
function israr_supported() {
|
||||
var pattern = $('#naming_pattern').val();
|
||||
$.get(sbRoot + '/config/postProcessing/isRarSupported',
|
||||
$.get(sbRoot + '/config/postProcessing/isRarSupported',
|
||||
function (data) {
|
||||
if (data == "supported") {
|
||||
} else {
|
||||
|
@ -21,11 +21,11 @@ $(document).ready(function () {
|
|||
});
|
||||
$('#unpack').qtip('toggle', true);
|
||||
$('#unpack').css('background-color', '#FFFFDD');
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function fill_examples() {
|
||||
var pattern = $('#naming_pattern').val();
|
||||
var multi = $('#naming_multi_ep :selected').val();
|
||||
|
@ -207,7 +207,7 @@ $(document).ready(function () {
|
|||
var multi = $('#naming_anime_multi_ep :selected').val();
|
||||
var anime_type = $('input[name="naming_anime"]:checked').val();
|
||||
|
||||
$.get(sbRoot + '/config/postProcessing/testNaming', {pattern: pattern, anime_type: anime_type},
|
||||
$.get(sbRoot + '/config/postProcessing/testNaming', {pattern: pattern, anime: 'True', anime_type: anime_type},
|
||||
function (data) {
|
||||
if (data) {
|
||||
$('#naming_example_anime').text(data + '.ext');
|
||||
|
@ -217,7 +217,7 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
$.get(sbRoot + '/config/postProcessing/testNaming', {pattern: pattern, multi: multi, anime_type: anime_type},
|
||||
$.get(sbRoot + '/config/postProcessing/testNaming', {pattern: pattern, multi: multi, anime: 'True', anime_type: anime_type},
|
||||
function (data) {
|
||||
if (data) {
|
||||
$('#naming_example_multi_anime').text(data + '.ext');
|
||||
|
@ -227,7 +227,7 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
$.get(sbRoot + '/config/postProcessing/isNamingValid', {pattern: pattern, multi: multi, anime_type: anime_type},
|
||||
$.get(sbRoot + '/config/postProcessing/isNamingValid', {pattern: pattern, multi: multi, anime: 'True', anime_type: anime_type},
|
||||
function (data) {
|
||||
if (data == "invalid") {
|
||||
$('#naming_anime_pattern').qtip('option', {
|
||||
|
|
|
@ -80,7 +80,6 @@ $(document).ready(function(){
|
|||
$(torrent_seed_time_option).show();
|
||||
} else if ('transmission' == selectedProvider){
|
||||
client = 'Transmission';
|
||||
$(torrent_seed_time_option).show();
|
||||
$(torrent_high_bandwidth_option).show();
|
||||
$(torrent_label_option).hide();
|
||||
//$('#directory_title').text(client + directory);
|
||||
|
|
106
gui/slick/js/editShow.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*globals $, config, sbRoot, generate_bwlist*/
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$.getJSON(sbRoot + '/home/addShows/getIndexerLanguages', {}, function (data) {
|
||||
var resultStr, flag, selected, current_lang_added = '';
|
||||
|
||||
if (data.results.length === 0) {
|
||||
flag = ' class="flag" style="background-image:url(' + sbRoot + '/images/flags/' + config.show_lang + '.png)"';
|
||||
resultStr = '<option value="' + config.show_lang + '" selected="selected"' + flag + '>' + config.show_lang + '</option>';
|
||||
} else {
|
||||
current_lang_added = false;
|
||||
$.each(data.results, function (index, obj) {
|
||||
|
||||
if (obj === config.show_lang) {
|
||||
selected = ' selected="selected"';
|
||||
current_lang_added = true;
|
||||
}
|
||||
else {
|
||||
selected = '';
|
||||
}
|
||||
|
||||
flag = ' class="flag" style="background-image:url(' + sbRoot + '/images/flags/' + obj + '.png);"';
|
||||
resultStr += '<option value="' + obj + '"' + selected + flag + '>' + obj + '</option>';
|
||||
});
|
||||
|
||||
if (!current_lang_added) {
|
||||
resultStr += '<option value=" ' + config.show_lang + '" selected="selected"> ' + config.show_lang + '</option>';
|
||||
}
|
||||
|
||||
}
|
||||
$('#indexerLangSelectEdit').html(resultStr);
|
||||
|
||||
});
|
||||
|
||||
|
||||
var all_exceptions = [];
|
||||
|
||||
$('#location').fileBrowser({title: 'Select Show Location'});
|
||||
|
||||
$('#submit').click(function () {
|
||||
all_exceptions = [];
|
||||
|
||||
$('#exceptions_list').find('option').each (function () {
|
||||
all_exceptions.push($(this).val());
|
||||
});
|
||||
|
||||
$('#exceptions_list').val(all_exceptions);
|
||||
if (config.show_isanime) {
|
||||
generate_bwlist();
|
||||
}
|
||||
});
|
||||
|
||||
$('#addSceneName').click(function () {
|
||||
var scene_ex = $('#SceneName').val();
|
||||
var scene_ex_season = $('#SceneNameSeason').val();
|
||||
var option = $('<option>');
|
||||
all_exceptions = [];
|
||||
|
||||
$('#exceptions_list').find('option').each (function () {
|
||||
all_exceptions.push($(this).val());
|
||||
});
|
||||
|
||||
$('#SceneName').val('');
|
||||
$('#SceneNameSeason').val('');
|
||||
|
||||
if ($.inArray(scene_ex_season + '|' + scene_ex, all_exceptions) > -1 || (scene_ex === '')) {
|
||||
return;
|
||||
}
|
||||
$('#SceneException').show();
|
||||
|
||||
option.attr('value', scene_ex_season + '|' + scene_ex);
|
||||
if (scene_ex_season === "-1") {
|
||||
option.html('S*: ' + scene_ex);
|
||||
}
|
||||
else {
|
||||
option.html('S' + scene_ex_season + ': ' + scene_ex);
|
||||
}
|
||||
return option.appendTo('#exceptions_list');
|
||||
});
|
||||
|
||||
$('#removeSceneName').click(function () {
|
||||
$('#exceptions_list').find('option:selected').remove();
|
||||
|
||||
$(this).toggle_SceneException();
|
||||
});
|
||||
|
||||
$.fn.toggle_SceneException = function () {
|
||||
all_exceptions = [];
|
||||
|
||||
$('#exceptions_list').find('option').each (function () {
|
||||
all_exceptions.push($(this).val());
|
||||
});
|
||||
|
||||
if ('' === all_exceptions) {
|
||||
$('#SceneException').hide();
|
||||
}
|
||||
else {
|
||||
$('#SceneException').show();
|
||||
}
|
||||
};
|
||||
|
||||
$(this).toggle_SceneException();
|
||||
|
||||
|
||||
});
|
|
@ -191,8 +191,9 @@ FormToWizard.prototype = {
|
|||
$section.data('elements', []);
|
||||
|
||||
//create each 'step' DIV and add it to main Steps Container:
|
||||
var $stepwords = ['first', 'second', 'third'], $thestep = $('<div class="step disabledstep" />').data('section', i).html(($stepwords[i]
|
||||
+ ' step') + '<div class="smalltext">' + $section.find('legend:eq(0)').text() + '<p></p></div>').appendTo($stepsguide);
|
||||
var $stepwords = ['first', 'then', 'finally'],
|
||||
$thestep = $('<div class="step disabledstep" />').data('section', i).html(($stepwords[i]) +
|
||||
'<div class="smalltext">' + $section.find('legend:eq(0)').text() + '<p></p></div>').appendTo($stepsguide);
|
||||
|
||||
//assign behavior to each step div
|
||||
$thestep.click(function(){
|
||||
|
|
141
gui/slick/js/home.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
$.tablesorter.addParser({
|
||||
id: 'loadingNames',
|
||||
is: function (s) {
|
||||
return false;
|
||||
},
|
||||
format: function (s) {
|
||||
if (s.indexOf('Loading...') === 0) {
|
||||
return s.replace('Loading...', '000');
|
||||
} else if (config.sortArticle) {
|
||||
return (s || '');
|
||||
} else {
|
||||
return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
|
||||
}
|
||||
},
|
||||
type: 'text'
|
||||
});
|
||||
|
||||
$.tablesorter.addParser({
|
||||
id: 'quality',
|
||||
is: function (s) {
|
||||
return false;
|
||||
},
|
||||
format: function (s) {
|
||||
return s.replace('hd1080p', 5).replace('hd720p', 4).replace('hd', 3).replace('sd', 2).replace('any', 1).replace('custom', 7);
|
||||
},
|
||||
type: 'numeric'
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
if (config.homeSearchFocus) {
|
||||
$('#search_show_name').focus();
|
||||
}
|
||||
|
||||
if (config.fuzzyDating) {
|
||||
fuzzyMoment({
|
||||
dtInline: config.isPoster,
|
||||
containerClass: config.fuzzydate,
|
||||
dateHasTime: !1,
|
||||
dateFormat: config.datePreset,
|
||||
timeFormat: config.timePreset,
|
||||
trimZero: config.trimZero
|
||||
});
|
||||
}
|
||||
|
||||
$('div[id^="progressbar"]').each(function (k, v) {
|
||||
var progress = parseInt($(this).siblings('span[class="sort-data"]').attr('data-progress'), 10), elId = '#' + $(this).attr('id'), v = 80;
|
||||
$(elId).progressbar({value: progress});
|
||||
if (progress < 80) {
|
||||
v = progress >= 40 ? 60 : (progress >= 20 ? 40 : 20);
|
||||
}
|
||||
$(elId + ' > .ui-progressbar-value').addClass('progress-' + v);
|
||||
});
|
||||
|
||||
$('img#network').on('error', function () {
|
||||
$(this).parent().text($(this).attr('alt'));
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
if (config.isPoster) {
|
||||
$('.container').each(function (i, obj) {
|
||||
$(obj).isotope({
|
||||
itemSelector: '.show',
|
||||
sortBy: config.posterSortby,
|
||||
sortAscending: config.posterSortdir,
|
||||
layoutMode: 'masonry',
|
||||
masonry: {
|
||||
columnWidth: 12,
|
||||
isFitWidth: true
|
||||
},
|
||||
getSortData: {
|
||||
name: function (itemElem) {
|
||||
var name = $(itemElem).attr('data-name');
|
||||
if (config.sortArticle) {
|
||||
return (name || '');
|
||||
} else {
|
||||
return (name || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
|
||||
}
|
||||
},
|
||||
date: function (itemElem) {
|
||||
var date = $(itemElem).attr('data-date');
|
||||
return date.length && parseInt(date, 10) || Number.POSITIVE_INFINITY;
|
||||
},
|
||||
network: '[data-network]',
|
||||
progress: function (itemElem) {
|
||||
var progress = $(itemElem).children('.sort-data').attr('data-progress');
|
||||
return progress.length && parseInt(progress, 10) || Number.NEGATIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#postersort').on('change', function () {
|
||||
var sortValue = this.value;
|
||||
$(obj).isotope({sortBy: sortValue});
|
||||
$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
|
||||
});
|
||||
|
||||
$('#postersortdirection').on('change', function () {
|
||||
var sortDirection = this.value;
|
||||
sortDirection = sortDirection == 'true';
|
||||
$(obj).isotope({sortAscending: sortDirection});
|
||||
$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$('.tablesorter').each(function (i, obj) {
|
||||
$(obj).has('tbody tr').tablesorter({
|
||||
sortList: [[5, 1], [1, 0]],
|
||||
textExtraction: {
|
||||
0: function (node) {
|
||||
return $(node).find('span').text().toLowerCase();
|
||||
},
|
||||
2: function (node) {
|
||||
return $(node).find('span').text().toLowerCase();
|
||||
},
|
||||
3: function (node) {
|
||||
return $(node).find('span').text().toLowerCase();
|
||||
},
|
||||
4: function (node) {
|
||||
return $(node).find('span').attr('data-progress');
|
||||
},
|
||||
5: function (node) {
|
||||
return $(node).find('i').attr('alt');
|
||||
}
|
||||
},
|
||||
widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'],
|
||||
headers: {
|
||||
0: {sorter: 'isoDate'},
|
||||
1: {sorter: 'loadingNames'},
|
||||
3: {sorter: 'quality'},
|
||||
4: {sorter: 'eps'}
|
||||
},
|
||||
widgetOptions: {
|
||||
filter_columnFilters: false,
|
||||
filter_reset: '.resetshows'
|
||||
},
|
||||
sortStable: true
|
||||
});
|
||||
$.tablesorter.filter.bindSearch($(obj), $('.search'));
|
||||
});
|
||||
}
|
||||
});
|
40
gui/slick/js/inc_top.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
function initActions() {
|
||||
$('#SubMenu a[href*="/home/restart/"]').addClass('btn restart').html('<i class="sgicon-restart"></i>Restart');
|
||||
$('#SubMenu a[href*="/home/shutdown/"]').addClass('btn shutdown').html('<i class="sgicon-shutdown"></i>Shutdown');
|
||||
$('#SubMenu a[href*="/home/logout/"]').addClass('btn').html('<i class="sgicon-logout"></i>Logout');
|
||||
$('#SubMenu a:contains("Edit")').addClass('btn').html('<i class="sgicon-edit"></i>Edit');
|
||||
$('#SubMenu a:contains("Remove")').addClass('btn remove').html('<i class="sgicon-delete"></i>Remove');
|
||||
$('#SubMenu a:contains("Clear History")').addClass('btn clearhistory').html('<i class="sgicon-delete"></i>Clear History');
|
||||
$('#SubMenu a:contains("Trim History")').addClass('btn trimhistory').html('<i class="sgicon-trim"></i>Trim History');
|
||||
$('#SubMenu a[href$="/errorlogs/clearerrors/"]').addClass('btn').html('<i class="sgicon-delete"></i>Clear Errors');
|
||||
$('#SubMenu a:contains("Re-scan")').addClass('btn').html('<i class="sgicon-refresh"></i>Re-scan');
|
||||
$('#SubMenu a:contains("Backlog Overview")').addClass('btn').html('<i class="sgicon-backlog"></i>Backlog Overview');
|
||||
$('#SubMenu a[href$="/home/updatePLEX/"]').addClass('btn').html('<i class="sgicon-plex"></i>Update PLEX');
|
||||
$('#SubMenu a:contains("Force")').addClass('btn').html('<i class="sgicon-fullupdate"></i>Force Full Update');
|
||||
$('#SubMenu a:contains("Rename")').addClass('btn').html('<i class="sgicon-rename"></i>Media Renamer');
|
||||
$('#SubMenu a[href$="/config/subtitles/"]').addClass('btn').html('<i class="sgicon-subtitles"></i>Search Subtitles');
|
||||
$('#SubMenu a[href*="/home/subtitleShow"]').addClass('btn').html('<i class="sgicon-subtitles"></i>Download Subtitles');
|
||||
$('#SubMenu a:contains("Anime")').addClass('btn').html('<i class="sgicon-anime"></i>Anime');
|
||||
$('#SubMenu a:contains("Settings")').addClass('btn').html('<i class="sgicon-search"></i>Search Settings');
|
||||
$('#SubMenu a:contains("Provider")').addClass('btn').html('<i class="sgicon-search"></i>Search Providers');
|
||||
$('#SubMenu a:contains("General")').addClass('btn').html('<i class="sgicon-config"></i>General');
|
||||
$('#SubMenu a:contains("Episode Status")').addClass('btn').html('<i class="sgicon-episodestatus"></i>Episode Status Management');
|
||||
$('#SubMenu a:contains("Missed Subtitle")').addClass('btn').html('<i class="sgicon-subtitles"></i>Missed Subtitles');
|
||||
$('#SubMenu a[href$="/home/addShows/"]').addClass('btn').html('<i class="sgicon-addshow"></i>Add Show');
|
||||
$('#SubMenu a:contains("Processing")').addClass('btn').html('<i class="sgicon-postprocess"></i>Post-Processing');
|
||||
$('#SubMenu a:contains("Manage Searches")').addClass('btn').html('<i class="sgicon-search"></i>Manage Searches');
|
||||
$('#SubMenu a:contains("Manage Torrents")').addClass('btn').html('<i class="sgicon-bittorrent"></i>Manage Torrents');
|
||||
$('#SubMenu a:contains("Show Queue Overview")').addClass('btn').html('<i class="sgicon-showqueue"></i>Show Queue Overview');
|
||||
$('#SubMenu a[href$="/manage/failedDownloads/"]').addClass('btn').html('<i class="sgicon-failed"></i>Failed Downloads');
|
||||
$('#SubMenu a:contains("Notification")').addClass('btn').html('<i class="sgicon-notification"></i>Notifications');
|
||||
$('#SubMenu a:contains("Update show in XBMC")').addClass('btn').html('<i class="sgicon-xbmc"></i>Update show in XBMC');
|
||||
$('#SubMenu a[href$="/home/updateXBMC/"]').addClass('btn').html('<i class="sgicon-xbmc"></i>Update XBMC');
|
||||
$('#SubMenu a:contains("Update show in Kodi")').addClass('btn').html('<i class="sgicon-kodi"></i>Update show in Kodi');
|
||||
$('#SubMenu a[href$="/home/updateKODI/"]').addClass('btn').html('<i class="sgicon-kodi"></i>Update Kodi');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
initActions();
|
||||
$('#NAV' + topmenu).addClass('active');
|
||||
$('.dropdown-toggle').dropdownHover();
|
||||
});
|
12
gui/slick/js/lib/isotope.pkgd.min.js
vendored
|
@ -77,40 +77,38 @@ $(document).ready(function () {
|
|||
if (0 === data.results.length) {
|
||||
resultStr += '<span class="boldest">Sorry, no results found. Try a different search.</span>';
|
||||
} else {
|
||||
var idxSrcDB = 0, idxSrcDBId = 1, idxSrcUrl = 2, idxShowID = 3, idxTitle = 4, idxDate = 5;
|
||||
$.each(data.results, function (index, obj) {
|
||||
checked = (0 == row ? ' checked' : '');
|
||||
rowType = (0 == row % 2 ? '' : ' class="alt"');
|
||||
row++;
|
||||
|
||||
var whichSeries = cleanseText(obj.join('|'), !0),
|
||||
display_show_name = cleanseText(obj[4], !0),
|
||||
showstartdate = '';
|
||||
var display_show_name = cleanseText(obj[idxTitle], !0), showstartdate = '';
|
||||
|
||||
if (null !== obj[5]) {
|
||||
var startDate = new Date(obj[5]);
|
||||
if (null !== obj[idxDate]) {
|
||||
var startDate = new Date(obj[idxDate]);
|
||||
var today = new Date();
|
||||
showstartdate = ' <span class="stepone-result-date">('
|
||||
+ (startDate > today ? 'will debut' : 'started')
|
||||
+ ' on ' + obj[5] + ')</span>';
|
||||
+ ': ' + obj[idxDate] + ')</span>';
|
||||
}
|
||||
|
||||
resultStr += '<div' + rowType + '>'
|
||||
+ '<input id="whichSeries" type="radio"'
|
||||
+ ' class="stepone-result-radio"'
|
||||
+ ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + display_show_name + '</span>"'
|
||||
+ ' name="whichSeries"'
|
||||
+ ' value="' + whichSeries + '"'
|
||||
+ ' value="' + cleanseText([obj[idxSrcDBId], obj[idxSrcDB], obj[idxShowID], obj[idxTitle]].join('|'), !0) + '"'
|
||||
+ checked
|
||||
+ ' />'
|
||||
+ '<a'
|
||||
+ ' class="stepone-result-title"'
|
||||
+ ' title="View detail for <span style=\'color: rgb(66, 139, 202)\'>' + display_show_name + '</span>"'
|
||||
+ ' href="' + anonURL + obj[2] + obj[3] + ((data.langid && '' != data.langid) ? '&lid=' + data.langid : '') + '"'
|
||||
+ ' href="' + anonURL + obj[idxSrcUrl] + obj[idxShowID] + ((data.langid && '' != data.langid) ? '&lid=' + data.langid : '') + '"'
|
||||
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
|
||||
+ '>' + display_show_name + '</a>'
|
||||
+ showstartdate
|
||||
+ (null == obj[0] ? ''
|
||||
: ' <span class="stepone-result-db grey-text">' + '[' + obj[0] + ']' + '</span>')
|
||||
+ (null == obj[idxSrcDB] ? ''
|
||||
: ' <span class="stepone-result-db grey-text">' + '[' + obj[idxSrcDB] + ']' + '</span>')
|
||||
+ '</div>' + "\n";
|
||||
});
|
||||
}
|
||||
|
@ -189,22 +187,24 @@ $(document).ready(function () {
|
|||
function updateSampleText() {
|
||||
// if something's selected then we have some behavior to figure out
|
||||
|
||||
var show_name,
|
||||
var show_name = '',
|
||||
sep_char,
|
||||
elRadio = $('input:radio[name="whichSeries"]:checked'),
|
||||
elInput = $('input:hidden[name="whichSeries"]'),
|
||||
elScene = $('#scene'),
|
||||
elRootDirs = $('#rootDirs'),
|
||||
elFullShowPath = $('#fullShowPath');
|
||||
elFullShowPath = $('#fullShowPath'),
|
||||
idxWhichShowID = 2, idxWhichTitle = 3;
|
||||
|
||||
// if they've picked a radio button then use that
|
||||
if (elRadio.length) {
|
||||
show_name = elRadio.val().split('|')[4];
|
||||
show_name = elRadio.val().split('|')[idxWhichTitle];
|
||||
elScene[0].checked = 0 <= show_scene_maps.indexOf(parseInt(elRadio.val().split('|')[idxWhichShowID], 10));
|
||||
$('#scene-maps-found').css('display', elScene.is(':checked') ? 'inline' : 'None');
|
||||
}
|
||||
// if we provided a show in the hidden field, use that
|
||||
else if (elInput.length && elInput.val().length) {
|
||||
show_name = $('#providedName').val();
|
||||
} else {
|
||||
show_name = '';
|
||||
}
|
||||
update_bwlist(show_name);
|
||||
var sample_text = '<p>Adding show <span class="show-name">' + cleanseText(show_name, !0) + '</span>'
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
function setFromPresets (preset) {
|
||||
var elCustomQuality = $('.show-if-quality-custom'),
|
||||
selected = 'selected';
|
||||
if (0 == preset) {
|
||||
if (preset = parseInt(preset)) {
|
||||
elCustomQuality.hide();
|
||||
|
||||
var upgrade = !0;
|
||||
$('#anyQualities, #bestQualities').find('option').each(function() {
|
||||
if (upgrade && 'bestQualities' === $(this).parent().attr('id')) {
|
||||
upgrade = !1;
|
||||
switch (preset) {
|
||||
case 3: preset = 128 + 32 + 4; break;
|
||||
case 164: preset = 256 + 64 + 16 + 4; break;
|
||||
case 336: preset = 256; break;
|
||||
default: preset = 0;
|
||||
}
|
||||
}
|
||||
$(this).attr(selected, ((preset & parseInt($(this).val())) ? selected : false));
|
||||
});
|
||||
} else
|
||||
elCustomQuality.show();
|
||||
return;
|
||||
}
|
||||
|
||||
elCustomQuality.hide();
|
||||
|
||||
$('#anyQualities').find('option').each(function() {
|
||||
var result = preset & $(this).val();
|
||||
$(this).attr(selected, (0 < result ? selected : false));
|
||||
});
|
||||
|
||||
$('#bestQualities').find('option').each(function() {
|
||||
var result = preset & ($(this).val() << 16);
|
||||
$(this).attr(selected, (result > 0 ? selected: false));
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
@ -24,7 +26,7 @@ $(document).ready(function() {
|
|||
selected = ':selected';
|
||||
|
||||
elQualityPreset.change(function() {
|
||||
setFromPresets($('#qualityPreset').find(selected).val());
|
||||
setFromPresets($(this).find(selected).val());
|
||||
});
|
||||
|
||||
setFromPresets(elQualityPreset.find(selected).val());
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
$(document).ready(function (){
|
||||
|
||||
function getRecommendedShows(){
|
||||
|
||||
$('#searchResults').empty().html('<img id="searchingAnim"'
|
||||
+ ' src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif"'
|
||||
+ ' height="32" width="32" />'
|
||||
+ ' fetching recommendations...');
|
||||
|
||||
$.getJSON(sbRoot + '/home/addShows/getRecommendedShows',
|
||||
{},
|
||||
function (data){
|
||||
var resultStr = '', checked = '', rowType, row = 0;
|
||||
|
||||
if (null === data || 0 === data.results.length){
|
||||
resultStr += '<p>Sorry, no recommended shows found, this can happen from time to time.</p>'
|
||||
+ '<p>However, if the issue persists, then try updating your watched shows list on trakt.tv</p>';
|
||||
} else {
|
||||
|
||||
$.each(data.results, function (index, obj){
|
||||
checked = (0 == row ? ' checked' : '');
|
||||
rowType = (0 == row % 2 ? '' : ' class="alt"');
|
||||
row++;
|
||||
|
||||
var whichSeries = obj[6] + '|' + obj[0] + '|' + obj[1] + '|' + obj[2] + '|' + obj[3],
|
||||
showstartdate = '';
|
||||
|
||||
if (null !== obj[3]){
|
||||
var startDate = new Date(obj[3]);
|
||||
var today = new Date();
|
||||
showstartdate = ' <span class="stepone-result-date">('
|
||||
+ (startDate > today ? 'will debut' : 'started')
|
||||
+ ' on ' + obj[3] + ')</span>';
|
||||
}
|
||||
|
||||
resultStr += '<div' + rowType + '>'
|
||||
+ '<input id="whichSeries" type="radio"'
|
||||
+ ' class="stepone-result-radio"'
|
||||
+ ' style="float:left;margin-top:4px"'
|
||||
+ ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
|
||||
+ ' name="whichSeries"'
|
||||
+ ' value="' + whichSeries + '"'
|
||||
+ checked
|
||||
+ ' />'
|
||||
+ '<div style="margin-left:20px">'
|
||||
+ '<a'
|
||||
+ ' class="stepone-result-title"'
|
||||
+ ' style="margin-left:5px"'
|
||||
+ ' title="View <span class=\'boldest\'>Trakt</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
|
||||
+ ' href="' + anonURL + obj[0] + '"'
|
||||
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
|
||||
+ '>' + obj[1] + '</a>'
|
||||
+ showstartdate
|
||||
+ (null == obj[6] ? ''
|
||||
: ' '
|
||||
+ '<span class="stepone-result-db grey-text">'
|
||||
+ '<a class="service" href="' + anonURL + obj[7] + '"'
|
||||
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
|
||||
+ ' title="View <span class=\'boldest\'>' + obj[4] + '</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
|
||||
+ '>'
|
||||
+ '<img alt="' + obj[4] + '" height="16" width="16" src="' + sbRoot + '/images/' + obj[5] + '" />'
|
||||
+ ''
|
||||
+ '</a>'
|
||||
+ '</span>'
|
||||
)
|
||||
+ (null == obj[10] ? ''
|
||||
: ' '
|
||||
+ '<span class="stepone-result-db grey-text">'
|
||||
+ '<a class="service" href="' + anonURL + obj[11] + '"'
|
||||
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
|
||||
+ ' title="View <span class=\'boldest\'>' + obj[8] + '</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
|
||||
+ '>'
|
||||
+ '<img alt="' + obj[8] + '" height="16" width="16" src="' + sbRoot + '/images/' + obj[9] + '" />'
|
||||
+ ''
|
||||
+ '</a>'
|
||||
+ '</span>'
|
||||
)
|
||||
+ (null == obj[2] ? ''
|
||||
: ' <div class="stepone-result-overview grey-text">' + obj[2] + '</div>')
|
||||
+ '</div></div>';
|
||||
});
|
||||
}
|
||||
|
||||
$('#searchResults').html(
|
||||
'<fieldset>' + "\n" + '<legend class="legendStep" style="margin-bottom: 15px">'
|
||||
+ (0 < row ? row : 'No')
|
||||
+ ' recommended result' + (1 == row ? '' : 's') + '...</legend>' + "\n"
|
||||
+ resultStr
|
||||
+ '</fieldset>'
|
||||
);
|
||||
updateSampleText();
|
||||
myform.loadsection(0);
|
||||
$('.stepone-result-radio, .stepone-result-title, .service').each(addQTip);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$('#addShowButton').click(function () {
|
||||
// if they haven't picked a show don't let them submit
|
||||
if (!$('input:radio[name="whichSeries"]:checked').val()
|
||||
&& !$('input:hidden[name="whichSeries"]').val().length) {
|
||||
alert('You must choose a show to continue');
|
||||
return false;
|
||||
}
|
||||
$('#addShowForm').submit();
|
||||
});
|
||||
|
||||
$('#qualityPreset').change(function (){
|
||||
myform.loadsection(2);
|
||||
});
|
||||
|
||||
var myform = new FormToWizard({
|
||||
fieldsetborderwidth: 0,
|
||||
formid: 'addShowForm',
|
||||
revealfx: ['slide', 500],
|
||||
oninit: function (){
|
||||
getRecommendedShows();
|
||||
updateSampleText();
|
||||
}
|
||||
});
|
||||
|
||||
function goToStep(num){
|
||||
$('.step').each(function (){
|
||||
if ($.data(this, 'section') + 1 == num){
|
||||
$(this).click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateSampleText(){
|
||||
// if something's selected then we have some behavior to figure out
|
||||
|
||||
var elRadio = $('input:radio[name="whichSeries"]:checked'),
|
||||
elFullShowPath = $('#fullShowPath'),
|
||||
sep_char = '',
|
||||
root_dirs = $('#rootDirs'),
|
||||
// if they've picked a radio button then use that
|
||||
show_name = (elRadio.length ? elRadio.val().split('|')[2] : ''),
|
||||
sample_text = '<p>Adding show <span class="show-name">' + show_name + '</span>'
|
||||
+ ('' == show_name ? 'into<br />' : '<br />into')
|
||||
+ ' <span class="show-dest">';
|
||||
|
||||
// if we have a root dir selected, figure out the path
|
||||
if (root_dirs.find('option:selected').length){
|
||||
var root_dir_text = root_dirs.find('option:selected').val();
|
||||
if (0 <= root_dir_text.indexOf('/')){
|
||||
sep_char = '/';
|
||||
} else if (0 <= root_dir_text.indexOf('\\')){
|
||||
sep_char = '\\';
|
||||
}
|
||||
|
||||
root_dir_text += (sep_char != root_dir_text.substr(sample_text.length - 1)
|
||||
? sep_char : '')
|
||||
+ '<i>||</i>' + sep_char;
|
||||
|
||||
sample_text += root_dir_text;
|
||||
} else if (elFullShowPath.length && elFullShowPath.val().length){
|
||||
sample_text += elFullShowPath.val();
|
||||
} else {
|
||||
sample_text += 'unknown dir.';
|
||||
}
|
||||
|
||||
sample_text += '</span></p>';
|
||||
|
||||
// if we have a show name then sanitize and use it for the dir name
|
||||
if (show_name.length){
|
||||
$.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data){
|
||||
$('#displayText').html(sample_text.replace('||', data));
|
||||
});
|
||||
// if not then it's unknown
|
||||
} else {
|
||||
$('#displayText').html(sample_text.replace('||', '??'));
|
||||
}
|
||||
|
||||
// also toggle the add show button
|
||||
$('#addShowButton').attr('disabled',
|
||||
((root_dirs.find('option:selected').length
|
||||
|| (elFullShowPath.length && elFullShowPath.val().length))
|
||||
&& elRadio.length
|
||||
? false : true));
|
||||
}
|
||||
|
||||
var addQTip = (function(){
|
||||
$(this).css('cursor', 'help');
|
||||
$(this).qtip({
|
||||
show: {
|
||||
solo: true
|
||||
},
|
||||
position: {
|
||||
viewport: $(window),
|
||||
my: 'left center',
|
||||
adjust: {
|
||||
y: -10,
|
||||
x: 2
|
||||
}
|
||||
},
|
||||
style: {
|
||||
tip: {
|
||||
corner: true,
|
||||
method: 'polygon'
|
||||
},
|
||||
classes: 'qtip-rounded qtip-bootstrap qtip-shadow ui-tooltip-sb'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#rootDirText').change(updateSampleText);
|
||||
|
||||
$('#searchResults').on('click', '.stepone-result-radio', updateSampleText);
|
||||
|
||||
});
|
|
@ -1,15 +1,15 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: sickbeard
|
||||
# Provides: sickgear
|
||||
# Required-Start: $local_fs $network $remote_fs
|
||||
# Required-Stop: $local_fs $network $remote_fs
|
||||
# Should-Start: $NetworkManager
|
||||
# Should-Stop: $NetworkManager
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts instance of SickBeard
|
||||
# Description: starts instance of SickBeard using start-stop-daemon
|
||||
# Short-Description: starts instance of SickGear
|
||||
# Description: starts instance of SickGear using start-stop-daemon
|
||||
### END INIT INFO
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables
|
||||
|
@ -19,57 +19,57 @@
|
|||
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
# Source SickBeard configuration
|
||||
if [ -f /etc/default/sickbeard ]; then
|
||||
. /etc/default/sickbeard
|
||||
# Source SickGear configuration
|
||||
if [ -f /etc/default/sickgear ]; then
|
||||
. /etc/default/sickgear
|
||||
else
|
||||
[ "${VERBOSE}" != no ] && echo "/etc/default/sickbeard not found. Using default settings.";
|
||||
[ "${VERBOSE}" != no ] && echo "/etc/default/sickgear not found. Using default settings.";
|
||||
fi
|
||||
|
||||
## Don't set -e
|
||||
## Don't edit this file!
|
||||
## Edit user configuation in /etc/default/sickbeard to change
|
||||
## Edit user configuation in /etc/default/sickgear to change
|
||||
##
|
||||
## SB_USER= #$RUN_AS, username to run sickbeard under, the default is sickbeard
|
||||
## SB_GROUP= #$RUN_GROUP, group to run sickbeard under, the default is sickbeard
|
||||
## SB_HOME= #$APP_PATH, the location of SickBeard.py, the default is /opt/sickbeard
|
||||
## SB_DATA= #$DATA_DIR, the location of sickbeard.db, cache, logs, the default is /opt/sickbeard
|
||||
## SB_PIDFILE= #$PID_FILE, the location of sickbeard.pid, the default is /var/run/sickbeard/sickbeard.pid
|
||||
## SG_USER= #$RUN_AS, username to run sickgear under, the default is sickgear
|
||||
## SG_GROUP= #$RUN_GROUP, group to run sickgear under, the default is sickgear
|
||||
## SG_HOME= #$APP_PATH, the location of SickBeard.py, the default is /opt/sickgear
|
||||
## SG_DATA= #$DATA_DIR, the location of sickbeard.db, cache, logs, the default is /opt/sickgear
|
||||
## SG_PIDFILE= #$PID_FILE, the location of sickgear.pid, the default is /var/run/sickgear/sickgear.pid
|
||||
## PYTHON_BIN= #$DAEMON, the location of the python binary, the default is /usr/bin/python
|
||||
## SB_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for sickbeard, i.e. " --config=/home/sickbeard/config.ini"
|
||||
## SG_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for sickgear, i.e. " --config=/home/sickgear/config.ini"
|
||||
## SSD_OPTS= #$EXTRA_SSD_OPTS, extra start-stop-daemon option like " --group=users"
|
||||
##
|
||||
## EXAMPLE if want to run as different user
|
||||
## add SB_USER=username to /etc/default/sickbeard
|
||||
## otherwise default sickbeard is used
|
||||
## add SG_USER=username to /etc/default/sickgear
|
||||
## otherwise default sickgear is used
|
||||
|
||||
# Script name
|
||||
NAME=$(basename "$0")
|
||||
|
||||
# App name
|
||||
DESC=SickBeard
|
||||
DESC=SickGear
|
||||
|
||||
## The defaults
|
||||
# Run as username
|
||||
RUN_AS=${SB_USER-sickbeard}
|
||||
# Run as username
|
||||
RUN_AS=${SG_USER-sickgear}
|
||||
|
||||
# Run as group
|
||||
RUN_GROUP=${SB_GROUP-sickbeard}
|
||||
RUN_GROUP=${SG_GROUP-sickgear}
|
||||
|
||||
# Path to app SB_HOME=path_to_app_SickBeard.py
|
||||
APP_PATH=${SB_HOME-/opt/sickbeard}
|
||||
# Path to app SG_HOME=path_to_app_SickBeard.py
|
||||
APP_PATH=${SG_HOME-/opt/sickgear}
|
||||
|
||||
# Data directory where sickbeard.db, cache and logs are stored
|
||||
DATA_DIR=${SB_DATA-/opt/sickbeard}
|
||||
DATA_DIR=${SG_DATA-/opt/sickgear}
|
||||
|
||||
# Path to store PID file
|
||||
PID_FILE=${SB_PIDFILE-/var/run/sickbeard/sickbeard.pid}
|
||||
PID_FILE=${SG_PIDFILE-/var/run/sickgear/sickgear.pid}
|
||||
|
||||
# path to python bin
|
||||
DAEMON=${PYTHON_BIN-/usr/bin/python}
|
||||
|
||||
# Extra daemon option like: SB_OPTS=" --config=/home/sickbeard/config.ini"
|
||||
EXTRA_DAEMON_OPTS=${SB_OPTS-}
|
||||
# Extra daemon option like: SG_OPTS=" --config=/home/sickgear/config.ini"
|
||||
EXTRA_DAEMON_OPTS=${SG_OPTS-}
|
||||
|
||||
# Extra start-stop-daemon option like START_OPTS=" --group=users"
|
||||
EXTRA_SSD_OPTS=${SSD_OPTS-}
|
||||
|
@ -82,7 +82,7 @@ DAEMON_OPTS=" SickBeard.py -q --daemon --nolaunch --pidfile=${PID_FILE} --datadi
|
|||
|
||||
test -x $DAEMON || exit 0
|
||||
|
||||
# Create PID directory if not exist and ensure the SickBeard user can write to it
|
||||
# Create PID directory if not exist and ensure the SickGear user can write to it
|
||||
if [ ! -d $PID_PATH ]; then
|
||||
mkdir -p $PID_PATH
|
||||
chown $RUN_AS $PID_PATH
|
||||
|
@ -101,9 +101,9 @@ if [ -e $PID_FILE ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
start_sickbeard() {
|
||||
start_sickgear() {
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
start-stop-daemon -d $APP_PATH -c $RUN_AS --group=${RUN_GROUP} $EXTRA_SSD_OPTS --start --quiet --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS
|
||||
start-stop-daemon -d $APP_PATH -c $RUN_AS --group=${RUN_GROUP} $EXTRA_SSD_OPTS --start --quiet --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS
|
||||
RETVAL="$?"
|
||||
case "${RETVAL}" in
|
||||
# Service was started or was running already
|
||||
|
@ -115,7 +115,7 @@ start_sickbeard() {
|
|||
return 0
|
||||
}
|
||||
|
||||
stop_sickbeard() {
|
||||
stop_sickgear() {
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
start-stop-daemon --stop --pidfile $PID_FILE --quiet --retry TERM/30/KILL/5
|
||||
RETVAL="$?"
|
||||
|
@ -127,23 +127,23 @@ stop_sickbeard() {
|
|||
esac
|
||||
[ "${RETVAL}" = 2 ] && return 2
|
||||
[ -f "${PID_FILE}" ] && rm -f ${PID_FILE}
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start_sickbeard
|
||||
start_sickgear
|
||||
exit $?
|
||||
;;
|
||||
stop)
|
||||
stop_sickbeard
|
||||
stop_sickgear
|
||||
exit $?
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
stop_sickbeard
|
||||
stop_sickgear
|
||||
sleep 2
|
||||
start_sickbeard
|
||||
start_sickgear
|
||||
return $?
|
||||
;;
|
||||
status)
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: sickbeard
|
||||
# Provides: sickgear
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
|
@ -13,27 +13,27 @@
|
|||
# Source function library.
|
||||
. /etc/init.d/functions
|
||||
|
||||
# Source SickBeard configuration
|
||||
if [ -f /etc/sysconfig/sickbeard ]; then
|
||||
. /etc/sysconfig/sickbeard
|
||||
# Source SickGear configuration
|
||||
if [ -f /etc/sysconfig/sickgear ]; then
|
||||
. /etc/sysconfig/sickgear
|
||||
fi
|
||||
|
||||
prog=sickbeard
|
||||
prog=sickgear
|
||||
lockfile=/var/lock/subsys/$prog
|
||||
|
||||
## Edit user configuation in /etc/sysconfig/sickbeard to change
|
||||
## Edit user configuation in /etc/sysconfig/sickgear to change
|
||||
## the defaults
|
||||
username=${SB_USER-sickbeard}
|
||||
homedir=${SB_HOME-/opt/sickbeard}
|
||||
datadir=${SB_DATA-/opt/sickbeard}
|
||||
pidfile=${SB_PIDFILE-/var/run/sickbeard/sickbeard.pid}
|
||||
nice=${SB_NICE-}
|
||||
username=${SG_USER-sickgear}
|
||||
homedir=${SG_HOME-/opt/sickgear}
|
||||
datadir=${SG_DATA-/opt/sickgear}
|
||||
pidfile=${SG_PIDFILE-/var/run/sickgear/sickgear.pid}
|
||||
nice=${SG_NICE-}
|
||||
##
|
||||
|
||||
pidpath=`dirname ${pidfile}`
|
||||
options=" --daemon --nolaunch --pidfile=${pidfile} --datadir=${datadir}"
|
||||
|
||||
# create PID directory if not exist and ensure the SickBeard user can write to it
|
||||
# create PID directory if not exist and ensure the SickGear user can write to it
|
||||
if [ ! -d $pidpath ]; then
|
||||
mkdir -p $pidpath
|
||||
chown $username $pidpath
|
98
init-scripts/init.freebsd
Executable file
|
@ -0,0 +1,98 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# PROVIDE: sickgear
|
||||
# REQUIRE: LOGIN
|
||||
# KEYWORD: shutdown
|
||||
#
|
||||
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
|
||||
# to enable this service:
|
||||
#
|
||||
# sickgear_enable (bool): Set to NO by default.
|
||||
# Set it to YES to enable it.
|
||||
# sickgear_user: The user account SickGear daemon runs as what
|
||||
# you want it to be. It uses '_sabnzbd' user by
|
||||
# default. Do not sets it as empty or it will run
|
||||
# as root.
|
||||
# sickgear_dir: Directory where SickGear lives.
|
||||
# Default: /usr/local/sickgear
|
||||
# sickgear_chdir: Change to this directory before running SickGear.
|
||||
# Default is same as sickgear_dir.
|
||||
# sickgear_datadir: Data directory for Sick Beard (DB, Logs, config)
|
||||
# Default is same as sickgear_chdir
|
||||
# sickgear_pid: The name of the pidfile to create.
|
||||
# Default is sickgear.pid in sickgear_dir.
|
||||
# sickgear_host: The hostname or IP SickGear is listening on
|
||||
# Default is 127.0.0.1
|
||||
# sickgear_port: The port SickGear is listening on
|
||||
# Default is 8081
|
||||
# sickgear_web_user: Username to authenticate to the SickGear web interface
|
||||
# Default is an empty string (no username)
|
||||
# sickgear_web_password: Password to authenticate to the SickGear web interface
|
||||
# Default is an empty string (no password)
|
||||
# sickgear_webroot: Set to value of web_root in config (for proxies etc)
|
||||
# Default is an empty string (if set must start with a "/")
|
||||
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="sickgear"
|
||||
rcvar=${name}_enable
|
||||
|
||||
load_rc_config ${name}
|
||||
|
||||
: ${sickgear_enable:="NO"}
|
||||
: ${sickgear_user:="_sabnzbd"}
|
||||
: ${sickgear_dir:="/usr/local/sickgear"}
|
||||
: ${sickgear_chdir:="${sickgear_dir}"}
|
||||
: ${sickgear_datadir:="${sickgear_chdir}"}
|
||||
: ${sickgear_pid:="${sickgear_dir}/sickgear.pid"}
|
||||
: ${sickgear_host:="127.0.0.1"}
|
||||
: ${sickgear_port:="8081"}
|
||||
: ${sickgear_web_user:=""}
|
||||
: ${sickgear_web_password:=""}
|
||||
: ${sickgear_webroot:=""}
|
||||
|
||||
status_cmd="${name}_status"
|
||||
stop_cmd="${name}_stop"
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="-f -p ${sickgear_pid} python ${sickgear_dir}/SickBeard.py --quiet --nolaunch"
|
||||
|
||||
# Add datadir to the command if set
|
||||
[ ! -z "${sickgear_datadir}" ] && \
|
||||
command_args="${command_args} --datadir ${sickgear_datadir}"
|
||||
|
||||
# Ensure user is root when running this script.
|
||||
if [ `id -u` != "0" ]; then
|
||||
echo "Oops, you should be root before running this!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_sickgear_pid() {
|
||||
# Make sure the pid corresponds to the SickGear process.
|
||||
pid=`cat ${sickgear_pid} 2>/dev/null`
|
||||
ps -p ${pid} 2>/dev/null | grep -q "python ${sickgear_dir}/SickBeard.py"
|
||||
return $?
|
||||
}
|
||||
|
||||
# Try to stop SickGear cleanly by calling shutdown over http.
|
||||
sickgear_stop() {
|
||||
echo "Stopping $name"
|
||||
verify_sickgear_pid
|
||||
sickgear_url="${sickgear_host}:${sickgear_port}"
|
||||
[ ! -z "${sickgear_web_user}" ] && \
|
||||
sickgear_url="${sickgear_web_user}:${sickgear_web_password}@${sickgear_url}"
|
||||
[ ! -z "${sickgear_webroot}" ] && \
|
||||
sickgear_url="${sickgear_url}${sickgear_webroot}"
|
||||
fetch -o - -q "http://${sickgear_url}/home/shutdown/?pid=${pid}" >/dev/null
|
||||
if [ -n "${pid}" ]; then
|
||||
wait_for_pids ${pid}
|
||||
echo "Stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
sickgear_status() {
|
||||
verify_sickgear_pid && echo "$name is running as ${pid}" || echo "$name is not running"
|
||||
}
|
||||
|
||||
run_rc_command "$1"
|
|
@ -9,15 +9,15 @@
|
|||
# You will need to create a configuration file in order for this script
|
||||
# to work properly. Please create /etc/conf.d/sickbeard with the following:
|
||||
#
|
||||
# SICKBEARD_USER=<user you want sickbeard to run under>
|
||||
# SICKBEARD_GROUP=<group you want sickbeard to run under>
|
||||
# SICKBEARD_DIR=<path to Sickbeard.py>
|
||||
# SICKGEAR_USER=<user you want sickgear to run under>
|
||||
# SICKGEAR_GROUP=<group you want sickgear to run under>
|
||||
# SICKGEAR_DIR=<path to Sickbeard.py>
|
||||
# PATH_TO_PYTHON_2=/usr/bin/python2
|
||||
# SICKBEARD_DATADIR=<directory that contains sickbeard.db file>
|
||||
# SICKBEARD_CONFDIR=<directory that contains Sickbeard's config.ini file>
|
||||
# SICKGEAR_DATADIR=<directory that contains sickbeard.db file>
|
||||
# SICKGEAR_CONFDIR=<directory that contains SickGear's config.ini file>
|
||||
#
|
||||
|
||||
RUNDIR=/var/run/sickbeard
|
||||
RUNDIR=/var/run/sickgear
|
||||
|
||||
depend() {
|
||||
need net
|
||||
|
@ -30,32 +30,32 @@ get_pidfile() {
|
|||
-e 's/[[:space:]]*$//' \
|
||||
-e 's/^[[:space:]]*//' \
|
||||
-e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
|
||||
< ${SICKBEARD_CONFDIR}/config.ini \
|
||||
< ${SICKGEAR_CONFDIR}/config.ini \
|
||||
| sed -n -e "/^\[General\]/,/^\s*\[/{/^[^;].*\=.*/p;}"`
|
||||
|
||||
echo "${RUNDIR}/sickbeard-${web_port}.pid"
|
||||
echo "${RUNDIR}/sickgear-${web_port}.pid"
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Starting Sickbeard"
|
||||
ebegin "Starting SickGear"
|
||||
|
||||
checkpath -q -d -o ${SICKBEARD_USER}:${SICKBEARD_GROUP} -m 0770 "${RUNDIR}"
|
||||
checkpath -q -d -o ${SICKGEAR_USER}:${SICKGEAR_GROUP} -m 0770 "${RUNDIR}"
|
||||
|
||||
start-stop-daemon \
|
||||
--quiet \
|
||||
--start \
|
||||
--user ${SICKBEARD_USER} \
|
||||
--group ${SICKBEARD_GROUP} \
|
||||
--name sickbeard \
|
||||
--user ${SICKGEAR_USER} \
|
||||
--group ${SICKGEAR_GROUP} \
|
||||
--name sickgear \
|
||||
--background \
|
||||
--pidfile $(get_pidfile) \
|
||||
--exec ${PATH_TO_PYTHON_2} \
|
||||
-- \
|
||||
${SICKBEARD_DIR}/SickBeard.py \
|
||||
${SICKGEAR_DIR}/SickBeard.py \
|
||||
-d \
|
||||
--pidfile $(get_pidfile) \
|
||||
--config ${SICKBEARD_CONFDIR}/config.ini \
|
||||
--datadir ${SICKBEARD_DATADIR}
|
||||
--config ${SICKGEAR_CONFDIR}/config.ini \
|
||||
--datadir ${SICKGEAR_DATADIR}
|
||||
eend $?
|
||||
}
|
||||
|
||||
|
@ -74,5 +74,5 @@ stop() {
|
|||
local pidfile=$(get_pidfile)
|
||||
local rc
|
||||
|
||||
ebegin "Stopping Sickbeard"
|
||||
ebegin "Stopping SickGear"
|
||||
}
|
|
@ -1,35 +1,35 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
|
||||
<!--
|
||||
Assumes user=sickbeard group=other
|
||||
Assumes /opt/sickbeard is installation directory
|
||||
|
||||
Assumes user=sickgear group=other
|
||||
Assumes /opt/sickgear is installation directory
|
||||
|
||||
See http://www.sun.com/bigadmin/content/selfheal/sdev_intro.jsp for more information
|
||||
|
||||
|
||||
To install (see http://docs.sun.com/app/docs/doc/819-2379/fgour?l=en&a=view for more information)
|
||||
svccfg import sickbeard.smf
|
||||
|
||||
svccfg import sickgear.smf
|
||||
|
||||
To enable/disable
|
||||
svcadm enable sickbeard
|
||||
svcadm disable sickbeard
|
||||
|
||||
svcadm enable sickgear
|
||||
svcadm disable sickgear
|
||||
|
||||
To check if failures
|
||||
svcs -xv
|
||||
|
||||
|
||||
To check logs
|
||||
tail /var/svc/log/network-sickbeard\:default.log
|
||||
tail /var/svc/log/network-sickgear\:default.log
|
||||
-->
|
||||
|
||||
<service_bundle type='manifest' name='sickbeard'>
|
||||
|
||||
|
||||
<service_bundle type='manifest' name='sickgear'>
|
||||
|
||||
<service
|
||||
name='network/sickbeard'
|
||||
name='network/sickgear'
|
||||
type='service'
|
||||
version='1'>
|
||||
|
||||
|
||||
<create_default_instance enabled='false' />
|
||||
<single_instance />
|
||||
|
||||
|
||||
<!--
|
||||
Only start in muti-user mode
|
||||
-->
|
||||
|
@ -39,7 +39,7 @@
|
|||
type='service'>
|
||||
<service_fmri value='svc:/milestone/multi-user' />
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--
|
||||
Wait for network interfaces to be initialized.
|
||||
-->
|
||||
|
@ -49,7 +49,7 @@
|
|||
type='service'>
|
||||
<service_fmri value='svc:/milestone/network:default'/>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--
|
||||
Wait for all local filesystems to be mounted.
|
||||
-->
|
||||
|
@ -59,37 +59,37 @@
|
|||
type='service'>
|
||||
<service_fmri value='svc:/system/filesystem/local:default'/>
|
||||
</dependency>
|
||||
|
||||
<!-- execute as user sickbeard -->
|
||||
|
||||
<!-- execute as user sickgear -->
|
||||
<method_context>
|
||||
<method_credential user='sickbeard' group='other' />
|
||||
<method_credential user='sickgear' group='other' />
|
||||
</method_context>
|
||||
|
||||
|
||||
<exec_method
|
||||
type='method'
|
||||
name='start'
|
||||
exec='/opt/sickbeard/SickBeard.py --daemon'
|
||||
exec='/opt/sickgear/SickBeard.py --daemon'
|
||||
timeout_seconds='60'>
|
||||
</exec_method>
|
||||
|
||||
|
||||
<exec_method
|
||||
type='method'
|
||||
name='stop'
|
||||
exec=':kill'
|
||||
timeout_seconds='2'>
|
||||
</exec_method>
|
||||
|
||||
|
||||
<template>
|
||||
<common_name>
|
||||
<loctext xml:lang='C'>Sickbeard</loctext>
|
||||
<loctext xml:lang='C'>SickGear</loctext>
|
||||
</common_name>
|
||||
<documentation>
|
||||
<doc_link name='sickbeard'
|
||||
uri='http://www.sickbeard.com/' />
|
||||
<doc_link name='sickgear'
|
||||
uri='https://github.com/SickGear/SickGear/' />
|
||||
</documentation>
|
||||
</template>
|
||||
|
||||
|
||||
</service>
|
||||
|
||||
|
||||
</service_bundle>
|
||||
|
|
@ -4,54 +4,54 @@
|
|||
#
|
||||
# - Option names (e.g. ExecStart=, Type=) are case-sensitive)
|
||||
#
|
||||
# - Adjust User= and Group= to the user/group you want Sickbeard to run as.
|
||||
# - Adjust User= and Group= to the user/group you want SickGear to run as.
|
||||
#
|
||||
# - Optional adjust EnvironmentFile= path to configuration file
|
||||
# Can ONLY be used for configuring extra options used in ExecStart.
|
||||
# Putting a minus (-) in front of file means no error warning if the file doesn't exist
|
||||
#
|
||||
# - Adjust ExecStart= to point to your python and SickBeard executables.
|
||||
# - Adjust ExecStart= to point to your python and SickGear executables.
|
||||
# The FIRST token of the command line must be an ABSOLUTE FILE NAME,
|
||||
# then followed by arguments for the process.
|
||||
# If no --datadir is given, data is stored in same dir as SickBeard.py
|
||||
# Arguments can also be set in EnvironmentFile (except python)
|
||||
#
|
||||
# - WantedBy= specifies which target (i.e. runlevel) to start Sickbeard for.
|
||||
# - WantedBy= specifies which target (i.e. runlevel) to start SickGear for.
|
||||
# multi-user.target equates to runlevel 3 (multi-user text mode)
|
||||
# graphical.target equates to runlevel 5 (multi-user X11 graphical mode)
|
||||
#
|
||||
|
||||
### Example Using SickBeard as daemon with pid file
|
||||
### Example Using SickGear as daemon with pid file
|
||||
# Type=forking
|
||||
# PIDFile=/var/run/sickbeard/sickbeard.pid
|
||||
# ExecStart=/usr/bin/python /opt/sickbeard/SickBeard.py -q --daemon --nolaunch --pidfile=/var/run/sickbeard/sickbeard.pid --datadir=/opt/sickbeard
|
||||
# PIDFile=/var/run/sickgear/sickgear.pid
|
||||
# ExecStart=/usr/bin/python /opt/sickgear/SickBeard.py -q --daemon --nolaunch --pidfile=/var/run/sickgear/sickgear.pid --datadir=/opt/sickgear
|
||||
|
||||
## Example Using SickBeard as daemon without pid file
|
||||
## Example Using SickGear as daemon without pid file
|
||||
# Type=forking
|
||||
# GuessMainPID=no
|
||||
# ExecStart=/usr/bin/python /opt/sickbeard/SickBeard.py -q --daemon --nolaunch --datadir=/opt/sickbeard
|
||||
# ExecStart=/usr/bin/python /opt/sickgear/SickBeard.py -q --daemon --nolaunch --datadir=/opt/sickgear
|
||||
|
||||
### Example Using simple
|
||||
# Type=simple
|
||||
# ExecStart=/usr/bin/python /opt/sickbeard/SickBeard.py -q --nolaunch
|
||||
# ExecStart=/usr/bin/python /opt/sickgear/SickBeard.py -q --nolaunch
|
||||
|
||||
### Example Using simple with EnvironmentFile where SB_DATA=/home/sickbeard/.sickbeard in /etc/sickbeard.conf
|
||||
### Example Using simple with EnvironmentFile where SB_DATA=/home/sickgear/.sickgear in /etc/sickgear.conf
|
||||
# Type=simple
|
||||
# EnvironmentFile=/etc/sickbeard.conf
|
||||
# ExecStart=/usr/bin/python /opt/sickbeard/SickBeard.py -q --nolaunch --datadir=${SB_DATA}
|
||||
# EnvironmentFile=/etc/sickgear.conf
|
||||
# ExecStart=/usr/bin/python /opt/sickgear/SickBeard.py -q --nolaunch --datadir=${SB_DATA}
|
||||
|
||||
### Configuration
|
||||
|
||||
[Unit]
|
||||
Description=SickBeard Daemon
|
||||
Description=SickGear Daemon
|
||||
|
||||
[Service]
|
||||
User=sickbeard
|
||||
Group=sickbeard
|
||||
User=sickgear
|
||||
Group=sickgear
|
||||
|
||||
Type=forking
|
||||
GuessMainPID=no
|
||||
ExecStart=/usr/bin/python /opt/sickbeard/SickBeard.py -q --daemon --nolaunch --datadir=/opt/sickbeard
|
||||
ExecStart=/usr/bin/python /opt/sickgear/SickBeard.py -q --daemon --nolaunch --datadir=/opt/sickgear
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
### BEGIN INIT INFO
|
||||
# Provides: sickbeard
|
||||
# Provides: sickgear
|
||||
# Required-Start: $local_fs $network $remote_fs
|
||||
# Required-Stop: $local_fs $network $remote_fs
|
||||
# Should-Start: $NetworkManager
|
||||
|
@ -12,55 +12,55 @@
|
|||
# Description: starts instance of SickGear using start-stop-daemon
|
||||
### END INIT INFO
|
||||
|
||||
# Source SickBeard configuration
|
||||
if [ -f /etc/default/sickbeard ]; then
|
||||
. /etc/default/sickbeard
|
||||
# Source SickGear configuration
|
||||
if [ -f /etc/default/sickgear ]; then
|
||||
. /etc/default/sickgear
|
||||
else
|
||||
echo "/etc/default/sickbeard not found using default settings.";
|
||||
echo "/etc/default/sickgear not found using default settings.";
|
||||
fi
|
||||
|
||||
# Source init functions
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
# Script name
|
||||
NAME=sickbeard
|
||||
NAME=sickgear
|
||||
|
||||
# App name
|
||||
DESC=SickBeard
|
||||
DESC=SickGear
|
||||
|
||||
## Don't edit this file
|
||||
## Edit user configuation in /etc/default/sickbeard to change
|
||||
## Edit user configuation in /etc/default/sickgear to change
|
||||
##
|
||||
## SB_USER= #$RUN_AS, username to run sickbeard under, the default is sickbeard
|
||||
## SB_HOME= #$APP_PATH, the location of SickBeard.py, the default is /opt/sickbeard
|
||||
## SB_DATA= #$DATA_DIR, the location of sickbeard.db, cache, logs, the default is /opt/sickbeard
|
||||
## SB_PIDFILE= #$PID_FILE, the location of sickbeard.pid, the default is /var/run/sickbeard/sickbeard.pid
|
||||
## SG_USER= #$RUN_AS, username to run sickgear under, the default is sickgear
|
||||
## SG_HOME= #$APP_PATH, the location of SickBeard.py, the default is /opt/sickgear
|
||||
## SG_DATA= #$DATA_DIR, the location of sickbeard.db, cache, logs, the default is /opt/sickgear
|
||||
## SG_PIDFILE= #$PID_FILE, the location of sickgear.pid, the default is /var/run/sickgear/sickgear.pid
|
||||
## PYTHON_BIN= #$DAEMON, the location of the python binary, the default is /usr/bin/python
|
||||
## SB_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for sickbeard, i.e. " --config=/home/sickbeard/config.ini"
|
||||
## SG_OPTS= #$EXTRA_DAEMON_OPTS, extra cli option for sickgear, i.e. " --config=/home/sickgear/config.ini"
|
||||
## SSD_OPTS= #$EXTRA_SSD_OPTS, extra start-stop-daemon option like " --group=users"
|
||||
##
|
||||
## EXAMPLE if want to run as different user
|
||||
## add SB_USER=username to /etc/default/sickbeard
|
||||
## otherwise default sickbeard is used
|
||||
## add SG_USER=username to /etc/default/sickgear
|
||||
## otherwise default sickgear is used
|
||||
|
||||
## The defaults
|
||||
# Run as username
|
||||
RUN_AS=${SB_USER-sickbeard}
|
||||
# Run as username
|
||||
RUN_AS=${SG_USER-sickgear}
|
||||
|
||||
# Path to app SB_HOME=path_to_app_SickBeard.py
|
||||
APP_PATH=${SB_HOME-/opt/sickbeard}
|
||||
# Path to app SG_HOME=path_to_app_SickBeard.py
|
||||
APP_PATH=${SG_HOME-/opt/sickgear}
|
||||
|
||||
# Data directory where sickbeard.db, cache and logs are stored
|
||||
DATA_DIR=${SB_DATA-/opt/sickbeard}
|
||||
DATA_DIR=${SG_DATA-/opt/sickgear}
|
||||
|
||||
# Path to store PID file
|
||||
PID_FILE=${SB_PIDFILE-/var/run/sickbeard/sickbeard.pid}
|
||||
PID_FILE=${SG_PIDFILE-/var/run/sickgear/sickgear.pid}
|
||||
|
||||
# path to python bin
|
||||
DAEMON=${PYTHON_BIN-/usr/bin/python}
|
||||
|
||||
# Extra daemon option like: SB_OPTS=" --config=/home/sickbeard/config.ini"
|
||||
EXTRA_DAEMON_OPTS=${SB_OPTS-}
|
||||
# Extra daemon option like: SG_OPTS=" --config=/home/sickgear/config.ini"
|
||||
EXTRA_DAEMON_OPTS=${SG_OPTS-}
|
||||
|
||||
# Extra start-stop-daemon option like START_OPTS=" --group=users"
|
||||
EXTRA_SSD_OPTS=${SSD_OPTS-}
|
||||
|
@ -75,7 +75,7 @@ test -x $DAEMON || exit 0
|
|||
|
||||
set -e
|
||||
|
||||
# Create PID directory if not exist and ensure the SickBeard user can write to it
|
||||
# Create PID directory if not exist and ensure the SickGear user can write to it
|
||||
if [ ! -d $PID_PATH ]; then
|
||||
mkdir -p $PID_PATH
|
||||
chown $RUN_AS $PID_PATH
|
||||
|
@ -94,28 +94,28 @@ if [ -e $PID_FILE ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
start_sickbeard() {
|
||||
start_sickgear() {
|
||||
echo "Starting $DESC"
|
||||
start-stop-daemon -d $APP_PATH -c $RUN_AS $EXTRA_SSD_OPTS --start --pidfile $PID_FILE --exec $DAEMON -- $DAEMON_OPTS
|
||||
}
|
||||
|
||||
stop_sickbeard() {
|
||||
stop_sickgear() {
|
||||
echo "Stopping $DESC"
|
||||
start-stop-daemon --stop --pidfile $PID_FILE --retry 15
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start_sickbeard
|
||||
start_sickgear
|
||||
;;
|
||||
stop)
|
||||
stop_sickbeard
|
||||
stop_sickgear
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
stop_sickbeard
|
||||
stop_sickgear
|
||||
sleep 2
|
||||
start_sickbeard
|
||||
start_sickgear
|
||||
;;
|
||||
status)
|
||||
status_of_proc -p "$PID_FILE" "$DAEMON" "$DESC"
|
98
init.freebsd
|
@ -1,98 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# PROVIDE: sickbeard
|
||||
# REQUIRE: LOGIN
|
||||
# KEYWORD: shutdown
|
||||
#
|
||||
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
|
||||
# to enable this service:
|
||||
#
|
||||
# sickbeard_enable (bool): Set to NO by default.
|
||||
# Set it to YES to enable it.
|
||||
# sickbeard_user: The user account SickGear daemon runs as what
|
||||
# you want it to be. It uses '_sabnzbd' user by
|
||||
# default. Do not sets it as empty or it will run
|
||||
# as root.
|
||||
# sickbeard_dir: Directory where SickGear lives.
|
||||
# Default: /usr/local/sickbeard
|
||||
# sickbeard_chdir: Change to this directory before running SickGear.
|
||||
# Default is same as sickbeard_dir.
|
||||
# sickbeard_datadir: Data directory for Sick Beard (DB, Logs, config)
|
||||
# Default is same as sickbeard_chdir
|
||||
# sickbeard_pid: The name of the pidfile to create.
|
||||
# Default is sickbeard.pid in sickbeard_dir.
|
||||
# sickbeard_host: The hostname or IP SickGear is listening on
|
||||
# Default is 127.0.0.1
|
||||
# sickbeard_port: The port SickGear is listening on
|
||||
# Default is 8081
|
||||
# sickbeard_web_user: Username to authenticate to the SickGear web interface
|
||||
# Default is an empty string (no username)
|
||||
# sickbeard_web_password: Password to authenticate to the SickGear web interface
|
||||
# Default is an empty string (no password)
|
||||
# sickbeard_webroot: Set to value of web_root in config (for proxies etc)
|
||||
# Default is an empty string (if set must start with a "/")
|
||||
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="sickbeard"
|
||||
rcvar=${name}_enable
|
||||
|
||||
load_rc_config ${name}
|
||||
|
||||
: ${sickbeard_enable:="NO"}
|
||||
: ${sickbeard_user:="_sabnzbd"}
|
||||
: ${sickbeard_dir:="/usr/local/sickbeard"}
|
||||
: ${sickbeard_chdir:="${sickbeard_dir}"}
|
||||
: ${sickbeard_datadir:="${sickbeard_chdir}"}
|
||||
: ${sickbeard_pid:="${sickbeard_dir}/sickbeard.pid"}
|
||||
: ${sickbeard_host:="127.0.0.1"}
|
||||
: ${sickbeard_port:="8081"}
|
||||
: ${sickbeard_web_user:=""}
|
||||
: ${sickbeard_web_password:=""}
|
||||
: ${sickbeard_webroot:=""}
|
||||
|
||||
status_cmd="${name}_status"
|
||||
stop_cmd="${name}_stop"
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="-f -p ${sickbeard_pid} python ${sickbeard_dir}/SickBeard.py --quiet --nolaunch"
|
||||
|
||||
# Add datadir to the command if set
|
||||
[ ! -z "${sickbeard_datadir}" ] && \
|
||||
command_args="${command_args} --datadir ${sickbeard_datadir}"
|
||||
|
||||
# Ensure user is root when running this script.
|
||||
if [ `id -u` != "0" ]; then
|
||||
echo "Oops, you should be root before running this!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
verify_sickbeard_pid() {
|
||||
# Make sure the pid corresponds to the SickGear process.
|
||||
pid=`cat ${sickbeard_pid} 2>/dev/null`
|
||||
ps -p ${pid} 2>/dev/null | grep -q "python ${sickbeard_dir}/SickBeard.py"
|
||||
return $?
|
||||
}
|
||||
|
||||
# Try to stop SickGear cleanly by calling shutdown over http.
|
||||
sickbeard_stop() {
|
||||
echo "Stopping $name"
|
||||
verify_sickbeard_pid
|
||||
sickbeard_url="${sickbeard_host}:${sickbeard_port}"
|
||||
[ ! -z "${sickbeard_web_user}" ] && \
|
||||
sickbeard_url="${sickbeard_web_user}:${sickbeard_web_password}@${sickbeard_url}"
|
||||
[ ! -z "${sickbeard_webroot}" ] && \
|
||||
sickbeard_url="${sickbeard_url}${sickbeard_webroot}"
|
||||
fetch -o - -q "http://${sickbeard_url}/home/shutdown/?pid=${pid}" >/dev/null
|
||||
if [ -n "${pid}" ]; then
|
||||
wait_for_pids ${pid}
|
||||
echo "Stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
sickbeard_status() {
|
||||
verify_sickbeard_pid && echo "$name is running as ${pid}" || echo "$name is not running"
|
||||
}
|
||||
|
||||
run_rc_command "$1"
|
|
@ -61,8 +61,9 @@ class Connection(threading.Thread):
|
|||
|
||||
self._iamALIVE = False
|
||||
|
||||
self.counter = 0
|
||||
self.counterAge = 0
|
||||
# start with a throttled connection
|
||||
self.counter = 6
|
||||
self.counterAge = time()
|
||||
|
||||
def print_log(self, data):
|
||||
print(strftime("%Y-%m-%d %H:%M:%S", localtime(time())) + ": " + str(data))
|
||||
|
|
|
@ -17,8 +17,8 @@ http://www.crummy.com/software/BeautifulSoup/bs4/doc/
|
|||
"""
|
||||
|
||||
__author__ = "Leonard Richardson (leonardr@segfault.org)"
|
||||
__version__ = "4.3.2"
|
||||
__copyright__ = "Copyright (c) 2004-2013 Leonard Richardson"
|
||||
__version__ = "4.4.0"
|
||||
__copyright__ = "Copyright (c) 2004-2015 Leonard Richardson"
|
||||
__license__ = "MIT"
|
||||
|
||||
__all__ = ['BeautifulSoup']
|
||||
|
@ -77,10 +77,11 @@ class BeautifulSoup(Tag):
|
|||
|
||||
ASCII_SPACES = '\x20\x0a\x09\x0c\x0d'
|
||||
|
||||
NO_PARSER_SPECIFIED_WARNING = "No parser was explicitly specified, so I'm using the best available parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n\nTo get rid of this warning, change this:\n\n BeautifulSoup([your markup])\n\nto this:\n\n BeautifulSoup([your markup], \"%(parser)s\")\n"
|
||||
NO_PARSER_SPECIFIED_WARNING = "No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n\nTo get rid of this warning, change this:\n\n BeautifulSoup([your markup])\n\nto this:\n\n BeautifulSoup([your markup], \"%(parser)s\")\n"
|
||||
|
||||
def __init__(self, markup="", features=None, builder=None,
|
||||
parse_only=None, from_encoding=None, **kwargs):
|
||||
parse_only=None, from_encoding=None, exclude_encodings=None,
|
||||
**kwargs):
|
||||
"""The Soup object is initialized as the 'root tag', and the
|
||||
provided markup (which can be a string or a file-like object)
|
||||
is fed into the underlying parser."""
|
||||
|
@ -156,8 +157,13 @@ class BeautifulSoup(Tag):
|
|||
builder = builder_class()
|
||||
if not (original_features == builder.NAME or
|
||||
original_features in builder.ALTERNATE_NAMES):
|
||||
if builder.is_xml:
|
||||
markup_type = "XML"
|
||||
else:
|
||||
markup_type = "HTML"
|
||||
warnings.warn(self.NO_PARSER_SPECIFIED_WARNING % dict(
|
||||
parser=builder.NAME))
|
||||
parser=builder.NAME,
|
||||
markup_type=markup_type))
|
||||
|
||||
self.builder = builder
|
||||
self.is_xml = builder.is_xml
|
||||
|
@ -202,7 +208,8 @@ class BeautifulSoup(Tag):
|
|||
|
||||
for (self.markup, self.original_encoding, self.declared_html_encoding,
|
||||
self.contains_replacement_characters) in (
|
||||
self.builder.prepare_markup(markup, from_encoding)):
|
||||
self.builder.prepare_markup(
|
||||
markup, from_encoding, exclude_encodings=exclude_encodings)):
|
||||
self.reset()
|
||||
try:
|
||||
self._feed()
|
||||
|
@ -215,6 +222,16 @@ class BeautifulSoup(Tag):
|
|||
self.markup = None
|
||||
self.builder.soup = None
|
||||
|
||||
def __copy__(self):
|
||||
return type(self)(self.encode(), builder=self.builder)
|
||||
|
||||
def __getstate__(self):
|
||||
# Frequently a tree builder can't be pickled.
|
||||
d = dict(self.__dict__)
|
||||
if 'builder' in d and not self.builder.picklable:
|
||||
del d['builder']
|
||||
return d
|
||||
|
||||
def _feed(self):
|
||||
# Convert the document to Unicode.
|
||||
self.builder.reset()
|
||||
|
@ -241,9 +258,7 @@ class BeautifulSoup(Tag):
|
|||
|
||||
def new_string(self, s, subclass=NavigableString):
|
||||
"""Create a new NavigableString associated with this soup."""
|
||||
navigable = subclass(s)
|
||||
navigable.setup()
|
||||
return navigable
|
||||
return subclass(s)
|
||||
|
||||
def insert_before(self, successor):
|
||||
raise NotImplementedError("BeautifulSoup objects don't support insert_before().")
|
||||
|
@ -302,14 +317,49 @@ class BeautifulSoup(Tag):
|
|||
def object_was_parsed(self, o, parent=None, most_recent_element=None):
|
||||
"""Add an object to the parse tree."""
|
||||
parent = parent or self.currentTag
|
||||
most_recent_element = most_recent_element or self._most_recent_element
|
||||
o.setup(parent, most_recent_element)
|
||||
previous_element = most_recent_element or self._most_recent_element
|
||||
|
||||
next_element = previous_sibling = next_sibling = None
|
||||
if isinstance(o, Tag):
|
||||
next_element = o.next_element
|
||||
next_sibling = o.next_sibling
|
||||
previous_sibling = o.previous_sibling
|
||||
if not previous_element:
|
||||
previous_element = o.previous_element
|
||||
|
||||
o.setup(parent, previous_element, next_element, previous_sibling, next_sibling)
|
||||
|
||||
if most_recent_element is not None:
|
||||
most_recent_element.next_element = o
|
||||
self._most_recent_element = o
|
||||
parent.contents.append(o)
|
||||
|
||||
if parent.next_sibling:
|
||||
# This node is being inserted into an element that has
|
||||
# already been parsed. Deal with any dangling references.
|
||||
index = parent.contents.index(o)
|
||||
if index == 0:
|
||||
previous_element = parent
|
||||
previous_sibling = None
|
||||
else:
|
||||
previous_element = previous_sibling = parent.contents[index-1]
|
||||
if index == len(parent.contents)-1:
|
||||
next_element = parent.next_sibling
|
||||
next_sibling = None
|
||||
else:
|
||||
next_element = next_sibling = parent.contents[index+1]
|
||||
|
||||
o.previous_element = previous_element
|
||||
if previous_element:
|
||||
previous_element.next_element = o
|
||||
o.next_element = next_element
|
||||
if next_element:
|
||||
next_element.previous_element = o
|
||||
o.next_sibling = next_sibling
|
||||
if next_sibling:
|
||||
next_sibling.previous_sibling = o
|
||||
o.previous_sibling = previous_sibling
|
||||
if previous_sibling:
|
||||
previous_sibling.next_sibling = o
|
||||
|
||||
def _popToTag(self, name, nsprefix=None, inclusivePop=True):
|
||||
"""Pops the tag stack up to and including the most recent
|
||||
instance of the given tag. If inclusivePop is false, pops the tag
|
||||
|
|
|
@ -85,6 +85,7 @@ class TreeBuilder(object):
|
|||
features = []
|
||||
|
||||
is_xml = False
|
||||
picklable = False
|
||||
preserve_whitespace_tags = set()
|
||||
empty_element_tags = None # A tag will be considered an empty-element
|
||||
# tag when and only when it has no contents.
|
||||
|
|
|
@ -2,6 +2,7 @@ __all__ = [
|
|||
'HTML5TreeBuilder',
|
||||
]
|
||||
|
||||
from pdb import set_trace
|
||||
import warnings
|
||||
from bs4.builder import (
|
||||
PERMISSIVE,
|
||||
|
@ -9,7 +10,10 @@ from bs4.builder import (
|
|||
HTML_5,
|
||||
HTMLTreeBuilder,
|
||||
)
|
||||
from bs4.element import NamespacedAttribute
|
||||
from bs4.element import (
|
||||
NamespacedAttribute,
|
||||
whitespace_re,
|
||||
)
|
||||
import html5lib
|
||||
from html5lib.constants import namespaces
|
||||
from bs4.element import (
|
||||
|
@ -26,9 +30,16 @@ class HTML5TreeBuilder(HTMLTreeBuilder):
|
|||
|
||||
features = [NAME, PERMISSIVE, HTML_5, HTML]
|
||||
|
||||
def prepare_markup(self, markup, user_specified_encoding):
|
||||
def prepare_markup(self, markup, user_specified_encoding,
|
||||
document_declared_encoding=None, exclude_encodings=None):
|
||||
# Store the user-specified encoding for use later on.
|
||||
self.user_specified_encoding = user_specified_encoding
|
||||
|
||||
# document_declared_encoding and exclude_encodings aren't used
|
||||
# ATM because the html5lib TreeBuilder doesn't use
|
||||
# UnicodeDammit.
|
||||
if exclude_encodings:
|
||||
warnings.warn("You provided a value for exclude_encoding, but the html5lib tree builder doesn't support exclude_encoding.")
|
||||
yield (markup, None, None, False)
|
||||
|
||||
# These methods are defined by Beautiful Soup.
|
||||
|
@ -103,7 +114,13 @@ class AttrList(object):
|
|||
def __iter__(self):
|
||||
return list(self.attrs.items()).__iter__()
|
||||
def __setitem__(self, name, value):
|
||||
"set attr", name, value
|
||||
# If this attribute is a multi-valued attribute for this element,
|
||||
# turn its value into a list.
|
||||
list_attr = HTML5TreeBuilder.cdata_list_attributes
|
||||
if (name in list_attr['*']
|
||||
or (self.element.name in list_attr
|
||||
and name in list_attr[self.element.name])):
|
||||
value = whitespace_re.split(value)
|
||||
self.element[name] = value
|
||||
def items(self):
|
||||
return list(self.attrs.items())
|
||||
|
@ -180,6 +197,7 @@ class Element(html5lib.treebuilders._base.Node):
|
|||
return AttrList(self.element)
|
||||
|
||||
def setAttributes(self, attributes):
|
||||
|
||||
if attributes is not None and len(attributes) > 0:
|
||||
|
||||
converted_attributes = []
|
||||
|
@ -226,6 +244,9 @@ class Element(html5lib.treebuilders._base.Node):
|
|||
|
||||
def reparentChildren(self, new_parent):
|
||||
"""Move all of this tag's children into another tag."""
|
||||
# print "MOVE", self.element.contents
|
||||
# print "FROM", self.element
|
||||
# print "TO", new_parent.element
|
||||
element = self.element
|
||||
new_parent_element = new_parent.element
|
||||
# Determine what this tag's next_element will be once all the children
|
||||
|
@ -244,17 +265,28 @@ class Element(html5lib.treebuilders._base.Node):
|
|||
new_parents_last_descendant_next_element = new_parent_element.next_element
|
||||
|
||||
to_append = element.contents
|
||||
append_after = new_parent.element.contents
|
||||
append_after = new_parent_element.contents
|
||||
if len(to_append) > 0:
|
||||
# Set the first child's previous_element and previous_sibling
|
||||
# to elements within the new parent
|
||||
first_child = to_append[0]
|
||||
first_child.previous_element = new_parents_last_descendant
|
||||
if new_parents_last_descendant:
|
||||
first_child.previous_element = new_parents_last_descendant
|
||||
else:
|
||||
first_child.previous_element = new_parent_element
|
||||
first_child.previous_sibling = new_parents_last_child
|
||||
if new_parents_last_descendant:
|
||||
new_parents_last_descendant.next_element = first_child
|
||||
else:
|
||||
new_parent_element.next_element = first_child
|
||||
if new_parents_last_child:
|
||||
new_parents_last_child.next_sibling = first_child
|
||||
|
||||
# Fix the last child's next_element and next_sibling
|
||||
last_child = to_append[-1]
|
||||
last_child.next_element = new_parents_last_descendant_next_element
|
||||
if new_parents_last_descendant_next_element:
|
||||
new_parents_last_descendant_next_element.previous_element = last_child
|
||||
last_child.next_sibling = None
|
||||
|
||||
for child in to_append:
|
||||
|
@ -265,6 +297,10 @@ class Element(html5lib.treebuilders._base.Node):
|
|||
element.contents = []
|
||||
element.next_element = final_next_element
|
||||
|
||||
# print "DONE WITH MOVE"
|
||||
# print "FROM", self.element
|
||||
# print "TO", new_parent_element
|
||||
|
||||
def cloneNode(self):
|
||||
tag = self.soup.new_tag(self.element.name, self.namespace)
|
||||
node = Element(tag, self.soup, self.namespace)
|
||||
|
|
|
@ -4,10 +4,16 @@ __all__ = [
|
|||
'HTMLParserTreeBuilder',
|
||||
]
|
||||
|
||||
from HTMLParser import (
|
||||
HTMLParser,
|
||||
HTMLParseError,
|
||||
)
|
||||
from HTMLParser import HTMLParser
|
||||
|
||||
try:
|
||||
from HTMLParser import HTMLParseError
|
||||
except ImportError, e:
|
||||
# HTMLParseError is removed in Python 3.5. Since it can never be
|
||||
# thrown in 3.5, we can just define our own class as a placeholder.
|
||||
class HTMLParseError(Exception):
|
||||
pass
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
|
@ -20,8 +26,10 @@ import warnings
|
|||
# strict=True works well on Python 3.2.2.
|
||||
major, minor, release = sys.version_info[:3]
|
||||
CONSTRUCTOR_TAKES_STRICT = major == 3 and minor == 2 and release >= 3
|
||||
CONSTRUCTOR_STRICT_IS_DEPRECATED = major == 3 and minor == 3
|
||||
CONSTRUCTOR_TAKES_CONVERT_CHARREFS = major == 3 and minor >= 4
|
||||
|
||||
|
||||
from bs4.element import (
|
||||
CData,
|
||||
Comment,
|
||||
|
@ -119,18 +127,19 @@ class BeautifulSoupHTMLParser(HTMLParser):
|
|||
class HTMLParserTreeBuilder(HTMLTreeBuilder):
|
||||
|
||||
is_xml = False
|
||||
picklable = True
|
||||
NAME = HTMLPARSER
|
||||
features = [NAME, HTML, STRICT]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if CONSTRUCTOR_TAKES_STRICT:
|
||||
if CONSTRUCTOR_TAKES_STRICT and not CONSTRUCTOR_STRICT_IS_DEPRECATED:
|
||||
kwargs['strict'] = False
|
||||
if CONSTRUCTOR_TAKES_CONVERT_CHARREFS:
|
||||
kwargs['convert_charrefs'] = False
|
||||
self.parser_args = (args, kwargs)
|
||||
|
||||
def prepare_markup(self, markup, user_specified_encoding=None,
|
||||
document_declared_encoding=None):
|
||||
document_declared_encoding=None, exclude_encodings=None):
|
||||
"""
|
||||
:return: A 4-tuple (markup, original encoding, encoding
|
||||
declared within markup, whether any characters had to be
|
||||
|
@ -141,7 +150,8 @@ class HTMLParserTreeBuilder(HTMLTreeBuilder):
|
|||
return
|
||||
|
||||
try_encodings = [user_specified_encoding, document_declared_encoding]
|
||||
dammit = UnicodeDammit(markup, try_encodings, is_html=True)
|
||||
dammit = UnicodeDammit(markup, try_encodings, is_html=True,
|
||||
exclude_encodings=exclude_encodings)
|
||||
yield (dammit.markup, dammit.original_encoding,
|
||||
dammit.declared_html_encoding,
|
||||
dammit.contains_replacement_characters)
|
||||
|
|
|
@ -31,6 +31,7 @@ class LXMLTreeBuilderForXML(TreeBuilder):
|
|||
is_xml = True
|
||||
|
||||
NAME = "lxml-xml"
|
||||
ALTERNATE_NAMES = ["xml"]
|
||||
|
||||
# Well, it's permissive by XML parser standards.
|
||||
features = [NAME, LXML, XML, FAST, PERMISSIVE]
|
||||
|
@ -77,6 +78,7 @@ class LXMLTreeBuilderForXML(TreeBuilder):
|
|||
return (None, tag)
|
||||
|
||||
def prepare_markup(self, markup, user_specified_encoding=None,
|
||||
exclude_encodings=None,
|
||||
document_declared_encoding=None):
|
||||
"""
|
||||
:yield: A series of 4-tuples.
|
||||
|
@ -102,7 +104,8 @@ class LXMLTreeBuilderForXML(TreeBuilder):
|
|||
# the document as each one in turn.
|
||||
is_html = not self.is_xml
|
||||
try_encodings = [user_specified_encoding, document_declared_encoding]
|
||||
detector = EncodingDetector(markup, try_encodings, is_html)
|
||||
detector = EncodingDetector(
|
||||
markup, try_encodings, is_html, exclude_encodings)
|
||||
for encoding in detector.encodings:
|
||||
yield (detector.markup, encoding, document_declared_encoding, False)
|
||||
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
This library converts a bytestream to Unicode through any means
|
||||
necessary. It is heavily based on code from Mark Pilgrim's Universal
|
||||
Feed Parser. It works best on XML and XML, but it does not rewrite the
|
||||
Feed Parser. It works best on XML and HTML, but it does not rewrite the
|
||||
XML or HTML to reflect a new encoding; that's the tree builder's job.
|
||||
"""
|
||||
|
||||
from pdb import set_trace
|
||||
import codecs
|
||||
from htmlentitydefs import codepoint2name
|
||||
import re
|
||||
|
@ -212,8 +213,11 @@ class EncodingDetector:
|
|||
|
||||
5. Windows-1252.
|
||||
"""
|
||||
def __init__(self, markup, override_encodings=None, is_html=False):
|
||||
def __init__(self, markup, override_encodings=None, is_html=False,
|
||||
exclude_encodings=None):
|
||||
self.override_encodings = override_encodings or []
|
||||
exclude_encodings = exclude_encodings or []
|
||||
self.exclude_encodings = set([x.lower() for x in exclude_encodings])
|
||||
self.chardet_encoding = None
|
||||
self.is_html = is_html
|
||||
self.declared_encoding = None
|
||||
|
@ -224,6 +228,8 @@ class EncodingDetector:
|
|||
def _usable(self, encoding, tried):
|
||||
if encoding is not None:
|
||||
encoding = encoding.lower()
|
||||
if encoding in self.exclude_encodings:
|
||||
return False
|
||||
if encoding not in tried:
|
||||
tried.add(encoding)
|
||||
return True
|
||||
|
@ -266,6 +272,9 @@ class EncodingDetector:
|
|||
def strip_byte_order_mark(cls, data):
|
||||
"""If a byte-order mark is present, strip it and return the encoding it implies."""
|
||||
encoding = None
|
||||
if isinstance(data, unicode):
|
||||
# Unicode data cannot have a byte-order mark.
|
||||
return data, encoding
|
||||
if (len(data) >= 4) and (data[:2] == b'\xfe\xff') \
|
||||
and (data[2:4] != '\x00\x00'):
|
||||
encoding = 'utf-16be'
|
||||
|
@ -306,7 +315,7 @@ class EncodingDetector:
|
|||
declared_encoding_match = html_meta_re.search(markup, endpos=html_endpos)
|
||||
if declared_encoding_match is not None:
|
||||
declared_encoding = declared_encoding_match.groups()[0].decode(
|
||||
'ascii')
|
||||
'ascii', 'replace')
|
||||
if declared_encoding:
|
||||
return declared_encoding.lower()
|
||||
return None
|
||||
|
@ -331,13 +340,14 @@ class UnicodeDammit:
|
|||
]
|
||||
|
||||
def __init__(self, markup, override_encodings=[],
|
||||
smart_quotes_to=None, is_html=False):
|
||||
smart_quotes_to=None, is_html=False, exclude_encodings=[]):
|
||||
self.smart_quotes_to = smart_quotes_to
|
||||
self.tried_encodings = []
|
||||
self.contains_replacement_characters = False
|
||||
self.is_html = is_html
|
||||
|
||||
self.detector = EncodingDetector(markup, override_encodings, is_html)
|
||||
self.detector = EncodingDetector(
|
||||
markup, override_encodings, is_html, exclude_encodings)
|
||||
|
||||
# Short-circuit if the data is in Unicode to begin with.
|
||||
if isinstance(markup, unicode) or markup == '':
|
||||
|
|
|
@ -33,12 +33,21 @@ def diagnose(data):
|
|||
|
||||
if 'lxml' in basic_parsers:
|
||||
basic_parsers.append(["lxml", "xml"])
|
||||
from lxml import etree
|
||||
print "Found lxml version %s" % ".".join(map(str,etree.LXML_VERSION))
|
||||
try:
|
||||
from lxml import etree
|
||||
print "Found lxml version %s" % ".".join(map(str,etree.LXML_VERSION))
|
||||
except ImportError, e:
|
||||
print (
|
||||
"lxml is not installed or couldn't be imported.")
|
||||
|
||||
|
||||
if 'html5lib' in basic_parsers:
|
||||
import html5lib
|
||||
print "Found html5lib version %s" % html5lib.__version__
|
||||
try:
|
||||
import html5lib
|
||||
print "Found html5lib version %s" % html5lib.__version__
|
||||
except ImportError, e:
|
||||
print (
|
||||
"html5lib is not installed or couldn't be imported.")
|
||||
|
||||
if hasattr(data, 'read'):
|
||||
data = data.read()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from pdb import set_trace
|
||||
import collections
|
||||
import re
|
||||
import sys
|
||||
|
@ -185,24 +186,40 @@ class PageElement(object):
|
|||
return self.HTML_FORMATTERS.get(
|
||||
name, HTMLAwareEntitySubstitution.substitute_xml)
|
||||
|
||||
def setup(self, parent=None, previous_element=None):
|
||||
def setup(self, parent=None, previous_element=None, next_element=None,
|
||||
previous_sibling=None, next_sibling=None):
|
||||
"""Sets up the initial relations between this element and
|
||||
other elements."""
|
||||
self.parent = parent
|
||||
|
||||
self.previous_element = previous_element
|
||||
if previous_element is not None:
|
||||
self.previous_element.next_element = self
|
||||
self.next_element = None
|
||||
self.previous_sibling = None
|
||||
self.next_sibling = None
|
||||
if self.parent is not None and self.parent.contents:
|
||||
self.previous_sibling = self.parent.contents[-1]
|
||||
|
||||
self.next_element = next_element
|
||||
if self.next_element:
|
||||
self.next_element.previous_element = self
|
||||
|
||||
self.next_sibling = next_sibling
|
||||
if self.next_sibling:
|
||||
self.next_sibling.previous_sibling = self
|
||||
|
||||
if (not previous_sibling
|
||||
and self.parent is not None and self.parent.contents):
|
||||
previous_sibling = self.parent.contents[-1]
|
||||
|
||||
self.previous_sibling = previous_sibling
|
||||
if previous_sibling:
|
||||
self.previous_sibling.next_sibling = self
|
||||
|
||||
nextSibling = _alias("next_sibling") # BS3
|
||||
previousSibling = _alias("previous_sibling") # BS3
|
||||
|
||||
def replace_with(self, replace_with):
|
||||
if not self.parent:
|
||||
raise ValueError(
|
||||
"Cannot replace one element with another when the"
|
||||
"element to be replaced is not part of a tree.")
|
||||
if replace_with is self:
|
||||
return
|
||||
if replace_with is self.parent:
|
||||
|
@ -216,6 +233,10 @@ class PageElement(object):
|
|||
|
||||
def unwrap(self):
|
||||
my_parent = self.parent
|
||||
if not self.parent:
|
||||
raise ValueError(
|
||||
"Cannot replace an element with its contents when that"
|
||||
"element is not part of a tree.")
|
||||
my_index = self.parent.index(self)
|
||||
self.extract()
|
||||
for child in reversed(self.contents[:]):
|
||||
|
@ -240,17 +261,20 @@ class PageElement(object):
|
|||
last_child = self._last_descendant()
|
||||
next_element = last_child.next_element
|
||||
|
||||
if self.previous_element is not None:
|
||||
if (self.previous_element is not None and
|
||||
self.previous_element != next_element):
|
||||
self.previous_element.next_element = next_element
|
||||
if next_element is not None:
|
||||
if next_element is not None and next_element != self.previous_element:
|
||||
next_element.previous_element = self.previous_element
|
||||
self.previous_element = None
|
||||
last_child.next_element = None
|
||||
|
||||
self.parent = None
|
||||
if self.previous_sibling is not None:
|
||||
if (self.previous_sibling is not None
|
||||
and self.previous_sibling != self.next_sibling):
|
||||
self.previous_sibling.next_sibling = self.next_sibling
|
||||
if self.next_sibling is not None:
|
||||
if (self.next_sibling is not None
|
||||
and self.next_sibling != self.previous_sibling):
|
||||
self.next_sibling.previous_sibling = self.previous_sibling
|
||||
self.previous_sibling = self.next_sibling = None
|
||||
return self
|
||||
|
@ -478,6 +502,10 @@ class PageElement(object):
|
|||
def _find_all(self, name, attrs, text, limit, generator, **kwargs):
|
||||
"Iterates over a generator looking for things that match."
|
||||
|
||||
if text is None and 'string' in kwargs:
|
||||
text = kwargs['string']
|
||||
del kwargs['string']
|
||||
|
||||
if isinstance(name, SoupStrainer):
|
||||
strainer = name
|
||||
else:
|
||||
|
@ -558,7 +586,7 @@ class PageElement(object):
|
|||
# | Attribute
|
||||
# Tag
|
||||
attribselect_re = re.compile(
|
||||
r'^(?P<tag>[a-zA-Z0-9][-.a-zA-Z0-9:_]*)?\[(?P<attribute>\w+)(?P<operator>[=~\|\^\$\*]?)' +
|
||||
r'^(?P<tag>[a-zA-Z0-9][-.a-zA-Z0-9:_]*)?\[(?P<attribute>[\w-]+)(?P<operator>[=~\|\^\$\*]?)' +
|
||||
r'=?"?(?P<value>[^\]"]*)"?\]$'
|
||||
)
|
||||
|
||||
|
@ -654,11 +682,17 @@ class NavigableString(unicode, PageElement):
|
|||
how to handle non-ASCII characters.
|
||||
"""
|
||||
if isinstance(value, unicode):
|
||||
return unicode.__new__(cls, value)
|
||||
return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
|
||||
u = unicode.__new__(cls, value)
|
||||
else:
|
||||
u = unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
|
||||
u.setup()
|
||||
return u
|
||||
|
||||
def __copy__(self):
|
||||
return self
|
||||
"""A copy of a NavigableString has the same contents and class
|
||||
as the original, but it is not connected to the parse tree.
|
||||
"""
|
||||
return type(self)(self)
|
||||
|
||||
def __getnewargs__(self):
|
||||
return (unicode(self),)
|
||||
|
@ -759,9 +793,12 @@ class Tag(PageElement):
|
|||
self.prefix = prefix
|
||||
if attrs is None:
|
||||
attrs = {}
|
||||
elif attrs and builder.cdata_list_attributes:
|
||||
attrs = builder._replace_cdata_list_attribute_values(
|
||||
self.name, attrs)
|
||||
elif attrs:
|
||||
if builder is not None and builder.cdata_list_attributes:
|
||||
attrs = builder._replace_cdata_list_attribute_values(
|
||||
self.name, attrs)
|
||||
else:
|
||||
attrs = dict(attrs)
|
||||
else:
|
||||
attrs = dict(attrs)
|
||||
self.attrs = attrs
|
||||
|
@ -778,6 +815,18 @@ class Tag(PageElement):
|
|||
|
||||
parserClass = _alias("parser_class") # BS3
|
||||
|
||||
def __copy__(self):
|
||||
"""A copy of a Tag is a new Tag, unconnected to the parse tree.
|
||||
Its contents are a copy of the old Tag's contents.
|
||||
"""
|
||||
clone = type(self)(None, self.builder, self.name, self.namespace,
|
||||
self.nsprefix, self.attrs)
|
||||
for attr in ('can_be_empty_element', 'hidden'):
|
||||
setattr(clone, attr, getattr(self, attr))
|
||||
for child in self.contents:
|
||||
clone.append(child.__copy__())
|
||||
return clone
|
||||
|
||||
@property
|
||||
def is_empty_element(self):
|
||||
"""Is this tag an empty-element tag? (aka a self-closing tag)
|
||||
|
@ -971,15 +1020,25 @@ class Tag(PageElement):
|
|||
as defined in __eq__."""
|
||||
return not self == other
|
||||
|
||||
def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING):
|
||||
def __repr__(self, encoding="unicode-escape"):
|
||||
"""Renders this tag as a string."""
|
||||
return self.encode(encoding)
|
||||
if PY3K:
|
||||
# "The return value must be a string object", i.e. Unicode
|
||||
return self.decode()
|
||||
else:
|
||||
# "The return value must be a string object", i.e. a bytestring.
|
||||
# By convention, the return value of __repr__ should also be
|
||||
# an ASCII string.
|
||||
return self.encode(encoding)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.decode()
|
||||
|
||||
def __str__(self):
|
||||
return self.encode()
|
||||
if PY3K:
|
||||
return self.decode()
|
||||
else:
|
||||
return self.encode()
|
||||
|
||||
if PY3K:
|
||||
__str__ = __repr__ = __unicode__
|
||||
|
@ -1103,12 +1162,18 @@ class Tag(PageElement):
|
|||
formatter="minimal"):
|
||||
"""Renders the contents of this tag as a Unicode string.
|
||||
|
||||
:param indent_level: Each line of the rendering will be
|
||||
indented this many spaces.
|
||||
|
||||
:param eventual_encoding: The tag is destined to be
|
||||
encoded into this encoding. This method is _not_
|
||||
responsible for performing that encoding. This information
|
||||
is passed in so that it can be substituted in if the
|
||||
document contains a <META> tag that mentions the document's
|
||||
encoding.
|
||||
|
||||
:param formatter: The output formatter responsible for converting
|
||||
entities to Unicode characters.
|
||||
"""
|
||||
# First off, turn a string formatter into a function. This
|
||||
# will stop the lookup from happening over and over again.
|
||||
|
@ -1137,7 +1202,17 @@ class Tag(PageElement):
|
|||
def encode_contents(
|
||||
self, indent_level=None, encoding=DEFAULT_OUTPUT_ENCODING,
|
||||
formatter="minimal"):
|
||||
"""Renders the contents of this tag as a bytestring."""
|
||||
"""Renders the contents of this tag as a bytestring.
|
||||
|
||||
:param indent_level: Each line of the rendering will be
|
||||
indented this many spaces.
|
||||
|
||||
:param eventual_encoding: The bytestring will be in this encoding.
|
||||
|
||||
:param formatter: The output formatter responsible for converting
|
||||
entities to Unicode characters.
|
||||
"""
|
||||
|
||||
contents = self.decode_contents(indent_level, encoding, formatter)
|
||||
return contents.encode(encoding)
|
||||
|
||||
|
@ -1201,7 +1276,14 @@ class Tag(PageElement):
|
|||
|
||||
_selector_combinators = ['>', '+', '~']
|
||||
_select_debug = False
|
||||
def select(self, selector, _candidate_generator=None):
|
||||
def select_one(self, selector):
|
||||
"""Perform a CSS selection operation on the current element."""
|
||||
value = self.select(selector, limit=1)
|
||||
if value:
|
||||
return value[0]
|
||||
return None
|
||||
|
||||
def select(self, selector, _candidate_generator=None, limit=None):
|
||||
"""Perform a CSS selection operation on the current element."""
|
||||
|
||||
# Remove whitespace directly after the grouping operator ','
|
||||
|
@ -1272,35 +1354,38 @@ class Tag(PageElement):
|
|||
"A pseudo-class must be prefixed with a tag name.")
|
||||
pseudo_attributes = re.match('([a-zA-Z\d-]+)\(([a-zA-Z\d]+)\)', pseudo)
|
||||
found = []
|
||||
if pseudo_attributes is not None:
|
||||
if pseudo_attributes is None:
|
||||
pseudo_type = pseudo
|
||||
pseudo_value = None
|
||||
else:
|
||||
pseudo_type, pseudo_value = pseudo_attributes.groups()
|
||||
if pseudo_type == 'nth-of-type':
|
||||
try:
|
||||
pseudo_value = int(pseudo_value)
|
||||
except:
|
||||
raise NotImplementedError(
|
||||
'Only numeric values are currently supported for the nth-of-type pseudo-class.')
|
||||
if pseudo_value < 1:
|
||||
raise ValueError(
|
||||
'nth-of-type pseudo-class value must be at least 1.')
|
||||
class Counter(object):
|
||||
def __init__(self, destination):
|
||||
self.count = 0
|
||||
self.destination = destination
|
||||
|
||||
def nth_child_of_type(self, tag):
|
||||
self.count += 1
|
||||
if self.count == self.destination:
|
||||
return True
|
||||
if self.count > self.destination:
|
||||
# Stop the generator that's sending us
|
||||
# these things.
|
||||
raise StopIteration()
|
||||
return False
|
||||
checker = Counter(pseudo_value).nth_child_of_type
|
||||
else:
|
||||
if pseudo_type == 'nth-of-type':
|
||||
try:
|
||||
pseudo_value = int(pseudo_value)
|
||||
except:
|
||||
raise NotImplementedError(
|
||||
'Only the following pseudo-classes are implemented: nth-of-type.')
|
||||
'Only numeric values are currently supported for the nth-of-type pseudo-class.')
|
||||
if pseudo_value < 1:
|
||||
raise ValueError(
|
||||
'nth-of-type pseudo-class value must be at least 1.')
|
||||
class Counter(object):
|
||||
def __init__(self, destination):
|
||||
self.count = 0
|
||||
self.destination = destination
|
||||
|
||||
def nth_child_of_type(self, tag):
|
||||
self.count += 1
|
||||
if self.count == self.destination:
|
||||
return True
|
||||
if self.count > self.destination:
|
||||
# Stop the generator that's sending us
|
||||
# these things.
|
||||
raise StopIteration()
|
||||
return False
|
||||
checker = Counter(pseudo_value).nth_child_of_type
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
'Only the following pseudo-classes are implemented: nth-of-type.')
|
||||
|
||||
elif token == '*':
|
||||
# Star selector -- matches everything
|
||||
|
@ -1376,6 +1461,7 @@ class Tag(PageElement):
|
|||
else:
|
||||
_use_candidate_generator = _candidate_generator
|
||||
|
||||
count = 0
|
||||
for tag in current_context:
|
||||
if self._select_debug:
|
||||
print " Running candidate generator on %s %s" % (
|
||||
|
@ -1400,6 +1486,8 @@ class Tag(PageElement):
|
|||
# don't include it in the context more than once.
|
||||
new_context.append(candidate)
|
||||
new_context_ids.add(id(candidate))
|
||||
if limit and len(new_context) >= limit:
|
||||
break
|
||||
elif self._select_debug:
|
||||
print " FAILURE %s %s" % (candidate.name, repr(candidate.attrs))
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
import logging
|
||||
import re
|
||||
from io import BytesIO
|
||||
|
||||
from .enums import ProbingState
|
||||
|
||||
|
@ -79,16 +78,16 @@ class CharSetProber(object):
|
|||
|
||||
This filter applies to all scripts which do not use English characters.
|
||||
"""
|
||||
filtered = BytesIO()
|
||||
filtered = bytearray()
|
||||
|
||||
# This regex expression filters out only words that have at-least one
|
||||
# international character. The word may include one marker character at
|
||||
# the end.
|
||||
words = re.findall(
|
||||
b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', buf)
|
||||
words = re.findall(b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?',
|
||||
buf)
|
||||
|
||||
for word in words:
|
||||
filtered.write(word[:-1])
|
||||
filtered.extend(word[:-1])
|
||||
|
||||
# If the last character in the word is a marker, replace it with a
|
||||
# space as markers shouldn't affect our analysis (they are used
|
||||
|
@ -97,9 +96,9 @@ class CharSetProber(object):
|
|||
last_char = word[-1:]
|
||||
if not last_char.isalpha() and last_char < b'\x80':
|
||||
last_char = b' '
|
||||
filtered.write(last_char)
|
||||
filtered.extend(last_char)
|
||||
|
||||
return filtered.getvalue()
|
||||
return filtered
|
||||
|
||||
@staticmethod
|
||||
def filter_with_english_letters(buf):
|
||||
|
@ -113,7 +112,7 @@ class CharSetProber(object):
|
|||
characters and extended ASCII characters, but is currently only used by
|
||||
``Latin1Prober``.
|
||||
"""
|
||||
filtered = BytesIO()
|
||||
filtered = bytearray()
|
||||
in_tag = False
|
||||
prev = 0
|
||||
|
||||
|
@ -132,15 +131,15 @@ class CharSetProber(object):
|
|||
if curr > prev and not in_tag:
|
||||
# Keep everything after last non-extended-ASCII,
|
||||
# non-alphabetic character
|
||||
filtered.write(buf[prev:curr])
|
||||
filtered.extend(buf[prev:curr])
|
||||
# Output a space to delimit stretch we kept
|
||||
filtered.write(b' ')
|
||||
filtered.extend(b' ')
|
||||
prev = curr + 1
|
||||
|
||||
# If we're not in a tag...
|
||||
if not in_tag:
|
||||
# Keep everything after last non-extended-ASCII, non-alphabetic
|
||||
# character
|
||||
filtered.write(buf[prev:])
|
||||
filtered.extend(buf[prev:])
|
||||
|
||||
return filtered.getvalue()
|
||||
return filtered
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
"""
|
||||
All of the Enums that are used throughout the chardet package.
|
||||
|
||||
:author: Dan Blanchard (dblanchard@ets.org)
|
||||
:author: Dan Blanchard (dan.blanchard@gmail.com)
|
||||
"""
|
||||
|
||||
try:
|
||||
from enum import IntEnum
|
||||
except ImportError:
|
||||
from enum34 import IntEnum
|
||||
|
||||
|
||||
class InputState(IntEnum):
|
||||
class InputState(object):
|
||||
"""
|
||||
This enum represents the different states a universal detector can be in.
|
||||
"""
|
||||
|
@ -19,7 +14,7 @@ class InputState(IntEnum):
|
|||
high_byte = 2
|
||||
|
||||
|
||||
class LanguageFilter(IntEnum):
|
||||
class LanguageFilter(object):
|
||||
"""
|
||||
This enum represents the different language filters we can apply to a
|
||||
``UniversalDetector``.
|
||||
|
@ -34,7 +29,7 @@ class LanguageFilter(IntEnum):
|
|||
cjk = chinese | japanese | korean
|
||||
|
||||
|
||||
class ProbingState(IntEnum):
|
||||
class ProbingState(object):
|
||||
"""
|
||||
This enum represents the different states a prober can be in.
|
||||
"""
|
||||
|
@ -43,7 +38,7 @@ class ProbingState(IntEnum):
|
|||
not_me = 2
|
||||
|
||||
|
||||
class MachineState(IntEnum):
|
||||
class MachineState(object):
|
||||
"""
|
||||
This enum represents the different states a state machine can be in.
|
||||
"""
|
||||
|
|
|
@ -33,7 +33,7 @@ from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel,
|
|||
Ibm866Model, Ibm855Model)
|
||||
from .langgreekmodel import Latin7GreekModel, Win1253GreekModel
|
||||
from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel
|
||||
from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel
|
||||
# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel
|
||||
from .langthaimodel import TIS620ThaiModel
|
||||
from .langhebrewmodel import Win1255HebrewModel
|
||||
from .hebrewprober import HebrewProber
|
||||
|
@ -63,9 +63,9 @@ class SBCSGroupProber(CharSetGroupProber):
|
|||
]
|
||||
hebrew_prober = HebrewProber()
|
||||
logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel,
|
||||
False, hebrew_prober)
|
||||
False, hebrew_prober)
|
||||
visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True,
|
||||
hebrew_prober)
|
||||
hebrew_prober)
|
||||
hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober)
|
||||
self.probers.extend([hebrew_prober, logical_hebrew_prober,
|
||||
visual_hebrew_prober])
|
||||
|
|
|
@ -122,12 +122,10 @@ class UniversalDetector(object):
|
|||
if byte_str.startswith(codecs.BOM_UTF8):
|
||||
# EF BB BF UTF-8 with BOM
|
||||
self.result = {'encoding': "UTF-8-SIG", 'confidence': 1.0}
|
||||
elif byte_str.startswith(codecs.BOM_UTF32_LE):
|
||||
elif byte_str.startswith(codecs.BOM_UTF32_LE) or byte_str.startswith(codecs.BOM_UTF32_BE):
|
||||
# FF FE 00 00 UTF-32, little-endian BOM
|
||||
self.result = {'encoding': "UTF-32LE", 'confidence': 1.0}
|
||||
elif byte_str.startswith(codecs.BOM_UTF32_BE):
|
||||
# 00 00 FE FF UTF-32, big-endian BOM
|
||||
self.result = {'encoding': "UTF-32BE", 'confidence': 1.0}
|
||||
self.result = {'encoding': "UTF-32", 'confidence': 1.0}
|
||||
elif byte_str.startswith(b'\xFE\xFF\x00\x00'):
|
||||
# FE FF 00 00 UCS-4, unusual octet order BOM (3412)
|
||||
self.result = {'encoding': "X-ISO-10646-UCS-4-3412",
|
||||
|
@ -136,12 +134,10 @@ class UniversalDetector(object):
|
|||
# 00 00 FF FE UCS-4, unusual octet order BOM (2143)
|
||||
self.result = {'encoding': "X-ISO-10646-UCS-4-2143",
|
||||
'confidence': 1.0}
|
||||
elif byte_str.startswith(codecs.BOM_LE):
|
||||
elif byte_str.startswith(codecs.BOM_LE) or byte_str.startswith(codecs.BOM_BE):
|
||||
# FF FE UTF-16, little endian BOM
|
||||
self.result = {'encoding': "UTF-16LE", 'confidence': 1.0}
|
||||
elif byte_str.startswith(codecs.BOM_BE):
|
||||
# FE FF UTF-16, big endian BOM
|
||||
self.result = {'encoding': "UTF-16BE", 'confidence': 1.0}
|
||||
self.result = {'encoding': "UTF-16", 'confidence': 1.0}
|
||||
|
||||
self._got_data = True
|
||||
if self.result['encoding'] is not None:
|
||||
|
@ -207,7 +203,7 @@ class UniversalDetector(object):
|
|||
return
|
||||
self.done = True
|
||||
|
||||
if self._input_state == InputState.pure_ascii:
|
||||
if self._input_state in (InputState.pure_ascii, InputState.esc_ascii):
|
||||
self.result = {'encoding': 'ascii', 'confidence': 1.0}
|
||||
return self.result
|
||||
|
||||
|
@ -229,7 +225,7 @@ class UniversalDetector(object):
|
|||
|
||||
if self.logger.getEffectiveLevel() == logging.DEBUG:
|
||||
self.logger.debug('no probers hit minimum threshhold')
|
||||
for prober in self._charset_probers[0].mProbers:
|
||||
for prober in self._charset_probers[0].probers:
|
||||
if not prober:
|
||||
continue
|
||||
self.logger.debug('%s confidence = %s', prober.charset_name,
|
||||
|
|
|
@ -4,28 +4,29 @@ This module offers a generic date/time string parser which is able to parse
|
|||
most known formats to represent a date and/or time.
|
||||
|
||||
This module attempts to be forgiving with regards to unlikely input formats,
|
||||
returning a datetime object even for dates which are ambiguous. If an element of
|
||||
a date/time stamp is omitted, the following rules are applied:
|
||||
returning a datetime object even for dates which are ambiguous. If an element
|
||||
of a date/time stamp is omitted, the following rules are applied:
|
||||
- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour
|
||||
on a 12-hour clock (`0 <= hour <= 12`) *must* be specified if AM or PM is
|
||||
on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is
|
||||
specified.
|
||||
- If a time zone is omitted, it is assumed to be UTC.
|
||||
- If a time zone is omitted, a timezone-naive datetime is returned.
|
||||
|
||||
If any other elements are missing, they are taken from the `datetime.datetime`
|
||||
object passed to the parameter `default`. If this results in a day number
|
||||
exceeding the valid number of days per month, one can fall back to the last
|
||||
day of the month by setting `fallback_on_invalid_day` parameter to `True`.
|
||||
If any other elements are missing, they are taken from the
|
||||
:class:`datetime.datetime` object passed to the parameter ``default``. If this
|
||||
results in a day number exceeding the valid number of days per month, one can
|
||||
fall back to the last day of the month by setting ``fallback_on_invalid_day``
|
||||
parameter to ``True``.
|
||||
|
||||
Also provided is the `smart_defaults` option, which attempts to fill in the
|
||||
Also provided is the ``smart_defaults`` option, which attempts to fill in the
|
||||
missing elements from context. If specified, the logic is:
|
||||
- If the omitted element is smaller than the largest specified element, select
|
||||
the *earliest* time matching the specified conditions; so `"June 2010"` is
|
||||
interpreted as `June 1, 2010 0:00:00`) and the (somewhat strange)
|
||||
`"Feb 1997 3:15 PM"` is interpreted as `February 1, 1997 15:15:00`.
|
||||
the *earliest* time matching the specified conditions; so ``"June 2010"`` is
|
||||
interpreted as ``June 1, 2010 0:00:00``) and the (somewhat strange)
|
||||
``"Feb 1997 3:15 PM"`` is interpreted as ``February 1, 1997 15:15:00``.
|
||||
- If the element is larger than the largest specified element, select the
|
||||
*most recent* time matching the specified conditions (e.g parsing `"May"`
|
||||
*most recent* time matching the specified conditions (e.g parsing ``"May"``
|
||||
in June 2015 returns the date May 1st, 2015, whereas parsing it in April 2015
|
||||
returns May 1st 2014). If using the `date_in_future` flag, this logic is
|
||||
returns May 1st 2014). If using the ``date_in_future`` flag, this logic is
|
||||
inverted, and instead the *next* time matching the specified conditions is
|
||||
returned.
|
||||
|
||||
|
@ -46,6 +47,7 @@ import datetime
|
|||
import string
|
||||
import time
|
||||
import collections
|
||||
import re
|
||||
from io import StringIO
|
||||
from calendar import monthrange, isleap
|
||||
|
||||
|
@ -58,6 +60,9 @@ __all__ = ["parse", "parserinfo"]
|
|||
|
||||
|
||||
class _timelex(object):
|
||||
# Fractional seconds are sometimes split by a comma
|
||||
_split_decimal = re.compile("([\.,])")
|
||||
|
||||
def __init__(self, instream):
|
||||
if isinstance(instream, binary_type):
|
||||
instream = instream.decode()
|
||||
|
@ -80,8 +85,8 @@ class _timelex(object):
|
|||
"""
|
||||
This function breaks the time string into lexical units (tokens), which
|
||||
can be parsed by the parser. Lexical units are demarcated by changes in
|
||||
the character set, so any continuous string of letters is considered one
|
||||
unit, any continuous string of numbers is considered one unit.
|
||||
the character set, so any continuous string of letters is considered
|
||||
one unit, any continuous string of numbers is considered one unit.
|
||||
|
||||
The main complication arises from the fact that dots ('.') can be used
|
||||
both as separators (e.g. "Sep.20.2009") or decimal points (e.g.
|
||||
|
@ -101,9 +106,9 @@ class _timelex(object):
|
|||
whitespace = self.whitespace
|
||||
|
||||
while not self.eof:
|
||||
# We only realize that we've reached the end of a token when we find
|
||||
# a character that's not part of the current token - since that
|
||||
# character may be part of the next token, it's stored in the
|
||||
# We only realize that we've reached the end of a token when we
|
||||
# find a character that's not part of the current token - since
|
||||
# that character may be part of the next token, it's stored in the
|
||||
# charstack.
|
||||
if self.charstack:
|
||||
nextchar = self.charstack.pop(0)
|
||||
|
@ -145,7 +150,7 @@ class _timelex(object):
|
|||
# numbers until we find something that doesn't fit.
|
||||
if nextchar in numchars:
|
||||
token += nextchar
|
||||
elif nextchar == '.':
|
||||
elif nextchar == '.' or (nextchar == ',' and len(token) >= 2):
|
||||
token += nextchar
|
||||
state = '0.'
|
||||
else:
|
||||
|
@ -176,14 +181,16 @@ class _timelex(object):
|
|||
break # emit token
|
||||
|
||||
if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or
|
||||
token[-1] == '.')):
|
||||
l = token.split('.')
|
||||
token[-1] in '.,')):
|
||||
l = self._split_decimal.split(token)
|
||||
token = l[0]
|
||||
for tok in l[1:]:
|
||||
self.tokenstack.append('.')
|
||||
if tok:
|
||||
self.tokenstack.append(tok)
|
||||
|
||||
if state == '0.' and token.count('.') == 0:
|
||||
token = token.replace(',', '.')
|
||||
|
||||
return token
|
||||
|
||||
def __iter__(self):
|
||||
|
@ -224,20 +231,20 @@ class _resultbase(object):
|
|||
|
||||
class parserinfo(object):
|
||||
"""
|
||||
Class which handles what inputs are accepted. Subclass this to customize the
|
||||
language and acceptable values for each parameter.
|
||||
Class which handles what inputs are accepted. Subclass this to customize
|
||||
the language and acceptable values for each parameter.
|
||||
|
||||
:param dayfirst:
|
||||
Whether to interpret the first value in an ambiguous 3-integer date
|
||||
(e.g. 01/05/09) as the day (`True`) or month (`False`). If
|
||||
`yearfirst` is set to `True`, this distinguishes between YDM and
|
||||
YMD. Default is `False`.
|
||||
(e.g. 01/05/09) as the day (``True``) or month (``False``). If
|
||||
``yearfirst`` is set to ``True``, this distinguishes between YDM
|
||||
and YMD. Default is ``False``.
|
||||
|
||||
:param yearfirst:
|
||||
Whether to interpret the first value in an ambiguous 3-integer date
|
||||
(e.g. 01/05/09) as the year. If `True`, the first number is taken to
|
||||
be the year, otherwise the last number is taken to be the year.
|
||||
Default is `False`.
|
||||
(e.g. 01/05/09) as the year. If ``True``, the first number is taken
|
||||
to be the year, otherwise the last number is taken to be the year.
|
||||
Default is ``False``.
|
||||
"""
|
||||
|
||||
# m from a.m/p.m, t from ISO T separator
|
||||
|
@ -287,7 +294,7 @@ class parserinfo(object):
|
|||
self.smart_defaults = smart_defaults
|
||||
|
||||
self._year = time.localtime().tm_year
|
||||
self._century = self._year // 100*100
|
||||
self._century = self._year // 100 * 100
|
||||
|
||||
def _convert(self, lst):
|
||||
dct = {}
|
||||
|
@ -313,7 +320,7 @@ class parserinfo(object):
|
|||
def month(self, name):
|
||||
if len(name) >= 3:
|
||||
try:
|
||||
return self._months[name.lower()]+1
|
||||
return self._months[name.lower()] + 1
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
@ -345,7 +352,7 @@ class parserinfo(object):
|
|||
def convertyear(self, year):
|
||||
if year < 100:
|
||||
year += self._century
|
||||
if abs(year-self._year) >= 50:
|
||||
if abs(year - self._year) >= 50:
|
||||
if year < self._year:
|
||||
year += 100
|
||||
else:
|
||||
|
@ -373,65 +380,87 @@ class parser(object):
|
|||
smart_defaults=None, date_in_future=False,
|
||||
fallback_on_invalid_day=None, **kwargs):
|
||||
"""
|
||||
Parse the date/time string into a datetime object.
|
||||
Parse the date/time string into a :class:`datetime.datetime` object.
|
||||
|
||||
:param timestr:
|
||||
Any date/time string using the supported formats.
|
||||
|
||||
:param default:
|
||||
The default datetime object, if this is a datetime object and not
|
||||
`None`, elements specified in `timestr` replace elements in the
|
||||
default object, unless `smart_defaults` is set to `True`, in which
|
||||
case to the extent necessary, timestamps are calculated relative to
|
||||
this date.
|
||||
``None``, elements specified in ``timestr`` replace elements in the
|
||||
default object, unless ``smart_defaults`` is set to ``True``, in
|
||||
which case to the extent necessary, timestamps are calculated
|
||||
relative to this date.
|
||||
|
||||
:param smart_defaults:
|
||||
If using smart defaults, the `default` parameter is treated as the
|
||||
effective parsing date/time, and the context of the datetime string
|
||||
is determined relative to `default`. If `None`, this parameter is
|
||||
inherited from the :class:`parserinfo` object.
|
||||
If using smart defaults, the ``default`` parameter is treated as
|
||||
the effective parsing date/time, and the context of the datetime
|
||||
string is determined relative to ``default``. If ``None``, this
|
||||
parameter is inherited from the :class:`parserinfo` object.
|
||||
|
||||
:param date_in_future:
|
||||
If `smart_defaults` is `True`, the parser assumes by default that
|
||||
the timestamp refers to a date in the past, and will return the
|
||||
beginning of the most recent timespan which matches the time string
|
||||
(e.g. if `default` is March 3rd, 2013, "Feb" parses to
|
||||
If ``smart_defaults`` is ``True``, the parser assumes by default
|
||||
that the timestamp refers to a date in the past, and will return
|
||||
the beginning of the most recent timespan which matches the time
|
||||
string (e.g. if ``default`` is March 3rd, 2013, "Feb" parses to
|
||||
"Feb 1, 2013" and "May 3" parses to May 3rd, 2012). Setting this
|
||||
parameter to `True` inverts this assumption, and returns the
|
||||
parameter to ``True`` inverts this assumption, and returns the
|
||||
beginning of the *next* matching timespan.
|
||||
|
||||
:param fallback_on_invalid_day:
|
||||
If specified `True`, an otherwise invalid date such as "Feb 30" or
|
||||
"June 32" falls back to the last day of the month. If specified as
|
||||
"False", the parser is strict about parsing otherwise valid dates
|
||||
that would turn up as invalid because of the fallback rules (e.g.
|
||||
"Feb 2010" run with a default of January 30, 2010 and `smartparser`
|
||||
set to `False` would would throw an error, rather than falling
|
||||
back to the end of February). If `None` or unspecified, the date
|
||||
falls back to the most recent valid date only if the invalid date
|
||||
is created as a result of an unspecified day in the time string.
|
||||
If specified ``True``, an otherwise invalid date such as "Feb 30"
|
||||
or "June 32" falls back to the last day of the month. If specified
|
||||
as "False", the parser is strict about parsing otherwise valid
|
||||
dates that would turn up as invalid because of the fallback rules
|
||||
(e.g. "Feb 2010" run with a default of January 30, 2010 and
|
||||
``smartparser`` set to ``False`` would would throw an error, rather
|
||||
than falling back to the end of February). If ``None`` or
|
||||
unspecified, the date falls back to the most recent valid date only
|
||||
if the invalid date is created as a result of an unspecified day in
|
||||
the time string.
|
||||
|
||||
:param ignoretz:
|
||||
Whether or not to ignore the time zone.
|
||||
If set ``True``, time zones in parsed strings are ignored and a
|
||||
naive :class:`datetime.datetime` object is returned.
|
||||
|
||||
:param tzinfos:
|
||||
A time zone, to be applied to the date, if `ignoretz` is `True`.
|
||||
This can be either a subclass of `tzinfo`, a time zone string or an
|
||||
integer offset.
|
||||
Additional time zone names / aliases which may be present in the
|
||||
string. This argument maps time zone names (and optionally offsets
|
||||
from those time zones) to time zones. This parameter can be a
|
||||
dictionary with timezone aliases mapping time zone names to time
|
||||
zones or a function taking two parameters (``tzname`` and
|
||||
``tzoffset``) and returning a time zone.
|
||||
|
||||
The timezones to which the names are mapped can be an integer
|
||||
offset from UTC in minutes or a :class:`tzinfo` object.
|
||||
|
||||
.. doctest::
|
||||
:options: +NORMALIZE_WHITESPACE
|
||||
|
||||
>>> from dateutil.parser import parse
|
||||
>>> from dateutil.tz import gettz
|
||||
>>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")}
|
||||
>>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
|
||||
datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800))
|
||||
>>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
|
||||
datetime.datetime(2012, 1, 19, 17, 21,
|
||||
tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))
|
||||
|
||||
This parameter is ignored if ``ignoretz`` is set.
|
||||
|
||||
:param **kwargs:
|
||||
Keyword arguments as passed to `_parse()`.
|
||||
Keyword arguments as passed to ``_parse()``.
|
||||
|
||||
:return:
|
||||
Returns a `datetime.datetime` object or, if the `fuzzy_with_tokens`
|
||||
option is `True`, returns a tuple, the first element being a
|
||||
`datetime.datetime` object, the second a tuple containing the
|
||||
fuzzy tokens.
|
||||
Returns a :class:`datetime.datetime` object or, if the
|
||||
``fuzzy_with_tokens`` option is ``True``, returns a tuple, the
|
||||
first element being a :class:`datetime.datetime` object, the second
|
||||
a tuple containing the fuzzy tokens.
|
||||
|
||||
:raises ValueError:
|
||||
Raised for invalid or unknown string format, if the provided
|
||||
`tzinfo` is not in a valid format, or if an invalid date would
|
||||
be created.
|
||||
:class:`tzinfo` is not in a valid format, or if an invalid date
|
||||
would be created.
|
||||
|
||||
:raises OverFlowError:
|
||||
Raised if the parsed date exceeds the largest valid C integer on
|
||||
|
@ -444,14 +473,11 @@ class parser(object):
|
|||
if default is None:
|
||||
effective_dt = datetime.datetime.now()
|
||||
default = datetime.datetime.now().replace(hour=0, minute=0,
|
||||
second=0, microsecond=0)
|
||||
second=0, microsecond=0)
|
||||
else:
|
||||
effective_dt = default
|
||||
|
||||
if kwargs.get('fuzzy_with_tokens', False):
|
||||
res, skipped_tokens = self._parse(timestr, **kwargs)
|
||||
else:
|
||||
res = self._parse(timestr, **kwargs)
|
||||
res, skipped_tokens = self._parse(timestr, **kwargs)
|
||||
|
||||
if res is None:
|
||||
raise ValueError("Unknown string format")
|
||||
|
@ -464,7 +490,7 @@ class parser(object):
|
|||
repl[attr] = value
|
||||
|
||||
# Choose the correct fallback position if requested by the
|
||||
# `smart_defaults` parameter.
|
||||
# ``smart_defaults`` parameter.
|
||||
if smart_defaults:
|
||||
# Determine if it refers to this year, last year or next year
|
||||
if res.year is None:
|
||||
|
@ -472,7 +498,7 @@ class parser(object):
|
|||
# Explicitly deal with leap year problems
|
||||
if res.month == 2 and (res.day is not None and
|
||||
res.day == 29):
|
||||
|
||||
|
||||
ly_offset = 4 if date_in_future else -4
|
||||
next_year = 4 * (default.year // 4)
|
||||
|
||||
|
@ -583,36 +609,42 @@ class parser(object):
|
|||
fuzzy_with_tokens=False):
|
||||
"""
|
||||
Private method which performs the heavy lifting of parsing, called from
|
||||
`parse()`, which passes on its `kwargs` to this function.
|
||||
``parse()``, which passes on its ``kwargs`` to this function.
|
||||
|
||||
:param timestr:
|
||||
The string to parse.
|
||||
|
||||
:param dayfirst:
|
||||
Whether to interpret the first value in an ambiguous 3-integer date
|
||||
(e.g. 01/05/09) as the day (`True`) or month (`False`). If
|
||||
`yearfirst` is set to `True`, this distinguishes between YDM and
|
||||
YMD. If set to `None`, this value is retrieved from the current
|
||||
`parserinfo` object (which itself defaults to `False`).
|
||||
(e.g. 01/05/09) as the day (``True``) or month (``False``). If
|
||||
``yearfirst`` is set to ``True``, this distinguishes between YDM
|
||||
and YMD. If set to ``None``, this value is retrieved from the
|
||||
current :class:`parserinfo` object (which itself defaults to
|
||||
``False``).
|
||||
|
||||
:param yearfirst:
|
||||
Whether to interpret the first value in an ambiguous 3-integer date
|
||||
(e.g. 01/05/09) as the year. If `True`, the first number is taken to
|
||||
be the year, otherwise the last number is taken to be the year. If
|
||||
this is set to `None`, the value is retrieved from the current
|
||||
`parserinfo` object (which itself defaults to `False`).
|
||||
(e.g. 01/05/09) as the year. If ``True``, the first number is taken
|
||||
to be the year, otherwise the last number is taken to be the year.
|
||||
If this is set to ``None``, the value is retrieved from the current
|
||||
:class:`parserinfo` object (which itself defaults to ``False``).
|
||||
|
||||
:param fuzzy:
|
||||
Whether to allow fuzzy parsing, allowing for string like "Today is
|
||||
January 1, 2047 at 8:21:00AM".
|
||||
|
||||
:param fuzzy_with_tokens:
|
||||
If `True`, `fuzzy` is automatically set to True, and the parser will
|
||||
return a tuple where the first element is the parsed
|
||||
`datetime.datetime` datetimestamp and the second element is a tuple
|
||||
containing the portions of the string which were ignored, e.g.
|
||||
"Today is January 1, 2047 at 8:21:00AM" should return
|
||||
`(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))`
|
||||
If ``True``, ``fuzzy`` is automatically set to True, and the parser
|
||||
will return a tuple where the first element is the parsed
|
||||
:class:`datetime.datetime` datetimestamp and the second element is
|
||||
a tuple containing the portions of the string which were ignored:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> from dateutil.parser import parse
|
||||
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
|
||||
(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
|
||||
|
||||
"""
|
||||
if fuzzy_with_tokens:
|
||||
fuzzy = True
|
||||
|
@ -796,7 +828,7 @@ class parser(object):
|
|||
assert mstridx == -1
|
||||
mstridx = len(ymd)-1
|
||||
else:
|
||||
return None
|
||||
return None, None
|
||||
|
||||
i += 1
|
||||
|
||||
|
@ -840,7 +872,7 @@ class parser(object):
|
|||
i += 1
|
||||
|
||||
elif not fuzzy:
|
||||
return None
|
||||
return None, None
|
||||
else:
|
||||
i += 1
|
||||
continue
|
||||
|
@ -969,7 +1001,7 @@ class parser(object):
|
|||
# -[0]3
|
||||
res.tzoffset = int(l[i][:2])*3600
|
||||
else:
|
||||
return None
|
||||
return None, None
|
||||
i += 1
|
||||
|
||||
res.tzoffset *= signal
|
||||
|
@ -987,7 +1019,7 @@ class parser(object):
|
|||
|
||||
# Check jumps
|
||||
if not (info.jump(l[i]) or fuzzy):
|
||||
return None
|
||||
return None, None
|
||||
|
||||
if last_skipped_token_i == i - 1:
|
||||
# recombine the tokens
|
||||
|
@ -1002,7 +1034,7 @@ class parser(object):
|
|||
len_ymd = len(ymd)
|
||||
if len_ymd > 3:
|
||||
# More than three members!?
|
||||
return None
|
||||
return None, None
|
||||
elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2):
|
||||
# One member, or two members with a month string
|
||||
if mstridx != -1:
|
||||
|
@ -1066,72 +1098,113 @@ class parser(object):
|
|||
res.month, res.day, res.year = ymd
|
||||
|
||||
except (IndexError, ValueError, AssertionError):
|
||||
return None
|
||||
return None, None
|
||||
|
||||
if not info.validate(res):
|
||||
return None
|
||||
return None, None
|
||||
|
||||
if fuzzy_with_tokens:
|
||||
return res, tuple(skipped_tokens)
|
||||
else:
|
||||
return res
|
||||
return res, None
|
||||
|
||||
DEFAULTPARSER = parser()
|
||||
|
||||
|
||||
def parse(timestr, parserinfo=None, **kwargs):
|
||||
"""
|
||||
Parse a string in one of the supported formats, using the `parserinfo`
|
||||
parameters.
|
||||
|
||||
Parse a string in one of the supported formats, using the
|
||||
``parserinfo`` parameters.
|
||||
|
||||
:param timestr:
|
||||
A string containing a date/time stamp.
|
||||
|
||||
:param parserinfo:
|
||||
A :class:`parserinfo` object containing parameters for the parser.
|
||||
If `None`, the default arguments to the `parserinfo` constructor are
|
||||
used.
|
||||
If ``None``, the default arguments to the :class:`parserinfo`
|
||||
constructor are used.
|
||||
|
||||
The `**kwargs` parameter takes the following keyword arguments:
|
||||
The ``**kwargs`` parameter takes the following keyword arguments:
|
||||
|
||||
:param default:
|
||||
The default datetime object, if this is a datetime object and not
|
||||
`None`, elements specified in `timestr` replace elements in the
|
||||
``None``, elements specified in ``timestr`` replace elements in the
|
||||
default object.
|
||||
|
||||
:param ignoretz:
|
||||
Whether or not to ignore the time zone (boolean).
|
||||
If set ``True``, time zones in parsed strings are ignored and a naive
|
||||
:class:`datetime` object is returned.
|
||||
|
||||
:param tzinfos:
|
||||
A time zone, to be applied to the date, if `ignoretz` is `True`.
|
||||
This can be either a subclass of `tzinfo`, a time zone string or an
|
||||
integer offset.
|
||||
Additional time zone names / aliases which may be present in the
|
||||
string. This argument maps time zone names (and optionally offsets
|
||||
from those time zones) to time zones. This parameter can be a
|
||||
dictionary with timezone aliases mapping time zone names to time
|
||||
zones or a function taking two parameters (``tzname`` and
|
||||
``tzoffset``) and returning a time zone.
|
||||
|
||||
The timezones to which the names are mapped can be an integer
|
||||
offset from UTC in minutes or a :class:`tzinfo` object.
|
||||
|
||||
.. doctest::
|
||||
:options: +NORMALIZE_WHITESPACE
|
||||
|
||||
>>> from dateutil.parser import parse
|
||||
>>> from dateutil.tz import gettz
|
||||
>>> tzinfos = {"BRST": -10800, "CST": gettz("America/Chicago")}
|
||||
>>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
|
||||
datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -10800))
|
||||
>>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos)
|
||||
datetime.datetime(2012, 1, 19, 17, 21,
|
||||
tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago'))
|
||||
|
||||
This parameter is ignored if ``ignoretz`` is set.
|
||||
|
||||
:param dayfirst:
|
||||
Whether to interpret the first value in an ambiguous 3-integer date
|
||||
(e.g. 01/05/09) as the day (`True`) or month (`False`). If
|
||||
`yearfirst` is set to `True`, this distinguishes between YDM and
|
||||
YMD. If set to `None`, this value is retrieved from the current
|
||||
:class:`parserinfo` object (which itself defaults to `False`).
|
||||
(e.g. 01/05/09) as the day (``True``) or month (``False``). If
|
||||
``yearfirst`` is set to ``True``, this distinguishes between YDM and
|
||||
YMD. If set to ``None``, this value is retrieved from the current
|
||||
:class:`parserinfo` object (which itself defaults to ``False``).
|
||||
|
||||
:param yearfirst:
|
||||
Whether to interpret the first value in an ambiguous 3-integer date
|
||||
(e.g. 01/05/09) as the year. If `True`, the first number is taken to
|
||||
(e.g. 01/05/09) as the year. If ``True``, the first number is taken to
|
||||
be the year, otherwise the last number is taken to be the year. If
|
||||
this is set to `None`, the value is retrieved from the current
|
||||
:class:`parserinfo` object (which itself defaults to `False`).
|
||||
this is set to ``None``, the value is retrieved from the current
|
||||
:class:`parserinfo` object (which itself defaults to ``False``).
|
||||
|
||||
:param fuzzy:
|
||||
Whether to allow fuzzy parsing, allowing for string like "Today is
|
||||
January 1, 2047 at 8:21:00AM".
|
||||
|
||||
:param fuzzy_with_tokens:
|
||||
If `True`, `fuzzy` is automatically set to True, and the parser will
|
||||
return a tuple where the first element is the parsed
|
||||
`datetime.datetime` datetimestamp and the second element is a tuple
|
||||
containing the portions of the string which were ignored, e.g.
|
||||
"Today is January 1, 2047 at 8:21:00AM" should return
|
||||
`(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))`
|
||||
If ``True``, ``fuzzy`` is automatically set to True, and the parser
|
||||
will return a tuple where the first element is the parsed
|
||||
:class:`datetime.datetime` datetimestamp and the second element is
|
||||
a tuple containing the portions of the string which were ignored:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> from dateutil.parser import parse
|
||||
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
|
||||
(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
|
||||
|
||||
:return:
|
||||
Returns a :class:`datetime.datetime` object or, if the
|
||||
``fuzzy_with_tokens`` option is ``True``, returns a tuple, the
|
||||
first element being a :class:`datetime.datetime` object, the second
|
||||
a tuple containing the fuzzy tokens.
|
||||
|
||||
:raises ValueError:
|
||||
Raised for invalid or unknown string format, if the provided
|
||||
:class:`tzinfo` is not in a valid format, or if an invalid date
|
||||
would be created.
|
||||
|
||||
:raises OverFlowError:
|
||||
Raised if the parsed date exceeds the largest valid C integer on
|
||||
your system.
|
||||
"""
|
||||
if parserinfo:
|
||||
return parser(parserinfo).parse(timestr, **kwargs)
|
||||
|
|
|
@ -423,6 +423,7 @@ Here is the behavior of operations with relativedelta:
|
|||
self.hours == other.hours and
|
||||
self.minutes == other.minutes and
|
||||
self.seconds == other.seconds and
|
||||
self.microseconds == other.microseconds and
|
||||
self.leapdays == other.leapdays and
|
||||
self.year == other.year and
|
||||
self.month == other.month and
|
||||
|
|
|
@ -104,12 +104,12 @@ class tzoffset(datetime.tzinfo):
|
|||
|
||||
|
||||
class tzlocal(datetime.tzinfo):
|
||||
|
||||
_std_offset = datetime.timedelta(seconds=-time.timezone)
|
||||
if time.daylight:
|
||||
_dst_offset = datetime.timedelta(seconds=-time.altzone)
|
||||
else:
|
||||
_dst_offset = _std_offset
|
||||
def __init__(self):
|
||||
self._std_offset = datetime.timedelta(seconds=-time.timezone)
|
||||
if time.daylight:
|
||||
self._dst_offset = datetime.timedelta(seconds=-time.altzone)
|
||||
else:
|
||||
self._dst_offset = self._std_offset
|
||||
|
||||
def utcoffset(self, dt):
|
||||
if self._isdst(dt):
|
||||
|
|
|
@ -4,6 +4,8 @@ import struct
|
|||
|
||||
from six.moves import winreg
|
||||
|
||||
from .tz import tzname_in_python2
|
||||
|
||||
__all__ = ["tzwin", "tzwinlocal"]
|
||||
|
||||
ONEWEEK = datetime.timedelta(7)
|
||||
|
@ -42,6 +44,7 @@ class tzwinbase(datetime.tzinfo):
|
|||
else:
|
||||
return datetime.timedelta(0)
|
||||
|
||||
@tzname_in_python2
|
||||
def tzname(self, dt):
|
||||
if self._isdst(dt):
|
||||
return self._dstname
|
||||
|
@ -89,8 +92,8 @@ class tzwin(tzwinbase):
|
|||
"%s\%s" % (TZKEYNAME, name)) as tzkey:
|
||||
keydict = valuestodict(tzkey)
|
||||
|
||||
self._stdname = keydict["Std"].encode("iso-8859-1")
|
||||
self._dstname = keydict["Dlt"].encode("iso-8859-1")
|
||||
self._stdname = keydict["Std"]
|
||||
self._dstname = keydict["Dlt"]
|
||||
|
||||
self._display = keydict["Display"]
|
||||
|
||||
|
@ -129,8 +132,8 @@ class tzwinlocal(tzwinbase):
|
|||
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||
keydict = valuestodict(tzlocalkey)
|
||||
|
||||
self._stdname = keydict["StandardName"].encode("iso-8859-1")
|
||||
self._dstname = keydict["DaylightName"].encode("iso-8859-1")
|
||||
self._stdname = keydict["StandardName"]
|
||||
self._dstname = keydict["DaylightName"]
|
||||
|
||||
try:
|
||||
with winreg.OpenKey(
|
||||
|
|
|
@ -16,14 +16,14 @@ from dateutil.tz import tzfile
|
|||
|
||||
__all__ = ["gettz", "gettz_db_metadata", "rebuild"]
|
||||
|
||||
_ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||
_METADATA_FN = 'METADATA'
|
||||
ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||
METADATA_FN = 'METADATA'
|
||||
|
||||
# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but
|
||||
# it's close enough for python2.6
|
||||
_tar_open = TarFile.open
|
||||
tar_open = TarFile.open
|
||||
if not hasattr(TarFile, '__exit__'):
|
||||
def _tar_open(*args, **kwargs):
|
||||
def tar_open(*args, **kwargs):
|
||||
return closing(TarFile.open(*args, **kwargs))
|
||||
|
||||
|
||||
|
@ -34,7 +34,7 @@ class tzfile(tzfile):
|
|||
|
||||
def getzoneinfofile_stream():
|
||||
try:
|
||||
return BytesIO(get_data(__name__, _ZONEFILENAME))
|
||||
return BytesIO(get_data(__name__, ZONEFILENAME))
|
||||
except IOError as e: # TODO switch to FileNotFoundError?
|
||||
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||
return None
|
||||
|
@ -43,7 +43,7 @@ def getzoneinfofile_stream():
|
|||
class ZoneInfoFile(object):
|
||||
def __init__(self, zonefile_stream=None):
|
||||
if zonefile_stream is not None:
|
||||
with _tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
||||
with tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
||||
# dict comprehension does not work on python2.6
|
||||
# TODO: get back to the nicer syntax when we ditch python2.6
|
||||
# self.zones = {zf.name: tzfile(tf.extractfile(zf),
|
||||
|
@ -52,7 +52,7 @@ class ZoneInfoFile(object):
|
|||
self.zones = dict((zf.name, tzfile(tf.extractfile(zf),
|
||||
filename=zf.name))
|
||||
for zf in tf.getmembers()
|
||||
if zf.isfile() and zf.name != _METADATA_FN)
|
||||
if zf.isfile() and zf.name != METADATA_FN)
|
||||
# deal with links: They'll point to their parent object. Less
|
||||
# waste of memory
|
||||
# links = {zl.name: self.zones[zl.linkname]
|
||||
|
@ -62,7 +62,7 @@ class ZoneInfoFile(object):
|
|||
zl.islnk() or zl.issym())
|
||||
self.zones.update(links)
|
||||
try:
|
||||
metadata_json = tf.extractfile(tf.getmember(_METADATA_FN))
|
||||
metadata_json = tf.extractfile(tf.getmember(METADATA_FN))
|
||||
metadata_str = metadata_json.read().decode('UTF-8')
|
||||
self.metadata = json.loads(metadata_str)
|
||||
except KeyError:
|
||||
|
@ -100,36 +100,3 @@ def gettz_db_metadata():
|
|||
return _CLASS_ZONE_INSTANCE[0].metadata
|
||||
|
||||
|
||||
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
||||
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
||||
|
||||
filename is the timezone tarball from ftp.iana.org/tz.
|
||||
|
||||
"""
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
zonedir = os.path.join(tmpdir, "zoneinfo")
|
||||
moduledir = os.path.dirname(__file__)
|
||||
try:
|
||||
with _tar_open(filename) as tf:
|
||||
for name in zonegroups:
|
||||
tf.extract(name, tmpdir)
|
||||
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
||||
try:
|
||||
check_call(["zic", "-d", zonedir] + filepaths)
|
||||
except OSError as e:
|
||||
if e.errno == 2:
|
||||
logging.error(
|
||||
"Could not find zic. Perhaps you need to install "
|
||||
"libc-bin or some other package that provides it, "
|
||||
"or it's not in your PATH?")
|
||||
raise
|
||||
# write metadata file
|
||||
with open(os.path.join(zonedir, _METADATA_FN), 'w') as f:
|
||||
json.dump(metadata, f, indent=4, sort_keys=True)
|
||||
target = os.path.join(moduledir, _ZONEFILENAME)
|
||||
with _tar_open(target, "w:%s" % format) as tf:
|
||||
for entry in os.listdir(zonedir):
|
||||
entrypath = os.path.join(zonedir, entry)
|
||||
tf.add(entrypath, entry)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
|
43
lib/dateutil/zoneinfo/rebuild.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
import json
|
||||
from subprocess import check_call
|
||||
|
||||
from dateutil.zoneinfo import tar_open, METADATA_FN, ZONEFILENAME
|
||||
|
||||
|
||||
def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None):
|
||||
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
||||
|
||||
filename is the timezone tarball from ftp.iana.org/tz.
|
||||
|
||||
"""
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
zonedir = os.path.join(tmpdir, "zoneinfo")
|
||||
moduledir = os.path.dirname(__file__)
|
||||
try:
|
||||
with tar_open(filename) as tf:
|
||||
for name in zonegroups:
|
||||
tf.extract(name, tmpdir)
|
||||
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
||||
try:
|
||||
check_call(["zic", "-d", zonedir] + filepaths)
|
||||
except OSError as e:
|
||||
if e.errno == 2:
|
||||
logging.error(
|
||||
"Could not find zic. Perhaps you need to install "
|
||||
"libc-bin or some other package that provides it, "
|
||||
"or it's not in your PATH?")
|
||||
raise
|
||||
# write metadata file
|
||||
with open(os.path.join(zonedir, METADATA_FN), 'w') as f:
|
||||
json.dump(metadata, f, indent=4, sort_keys=True)
|
||||
target = os.path.join(moduledir, ZONEFILENAME)
|
||||
with tar_open(target, "w:%s" % format) as tf:
|
||||
for entry in os.listdir(zonedir):
|
||||
entrypath = os.path.join(zonedir, entry)
|
||||
tf.add(entrypath, entry)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
|
@ -1,2 +1,2 @@
|
|||
from lib.hachoir_core.version import VERSION as __version__, PACKAGE, WEBSITE, LICENSE
|
||||
from hachoir_core.version import VERSION as __version__, PACKAGE, WEBSITE, LICENSE
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from lib.hachoir_core.tools import humanDurationNanosec
|
||||
from lib.hachoir_core.i18n import _
|
||||
from hachoir_core.tools import humanDurationNanosec
|
||||
from hachoir_core.i18n import _
|
||||
from math import floor
|
||||
from time import time
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ Utilities to convert integers and binary strings to binary (number), binary
|
|||
string, number, hexadecimal, etc.
|
||||
"""
|
||||
|
||||
from lib.hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN
|
||||
from lib.hachoir_core.compatibility import reversed
|
||||
from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
|
||||
from hachoir_core.compatibility import reversed
|
||||
from itertools import chain, repeat
|
||||
from struct import calcsize, unpack, error as struct_error
|
||||
|
||||
|
@ -30,6 +30,28 @@ def swap32(value):
|
|||
| ((value & 0x00FF0000L) >> 8) \
|
||||
| ((value & 0xFF000000L) >> 24)
|
||||
|
||||
def arrswapmid(data):
|
||||
r"""
|
||||
Convert an array of characters from middle-endian to big-endian and vice-versa.
|
||||
|
||||
>>> arrswapmid("badcfehg")
|
||||
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
|
||||
"""
|
||||
assert len(data)%2 == 0
|
||||
ret = ['']*len(data)
|
||||
ret[1::2] = data[0::2]
|
||||
ret[0::2] = data[1::2]
|
||||
return ret
|
||||
|
||||
def strswapmid(data):
|
||||
r"""
|
||||
Convert raw data from middle-endian to big-endian and vice-versa.
|
||||
|
||||
>>> strswapmid("badcfehg")
|
||||
'abcdefgh'
|
||||
"""
|
||||
return ''.join(arrswapmid(data))
|
||||
|
||||
def bin2long(text, endian):
|
||||
"""
|
||||
Convert binary number written in a string into an integer.
|
||||
|
@ -45,9 +67,10 @@ def bin2long(text, endian):
|
|||
assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
|
||||
bits = [ (ord(character)-ord("0")) \
|
||||
for character in text if character in "01" ]
|
||||
assert len(bits) != 0
|
||||
if endian is not BIG_ENDIAN:
|
||||
bits = reversed(bits)
|
||||
bits = bits[::-1]
|
||||
size = len(bits)
|
||||
assert 0 < size
|
||||
value = 0
|
||||
for bit in bits:
|
||||
value *= 2
|
||||
|
@ -142,7 +165,7 @@ def long2raw(value, endian, size=None):
|
|||
'\x19\x12\x00\x00'
|
||||
"""
|
||||
assert (not size and 0 < value) or (0 <= value)
|
||||
assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
|
||||
assert endian in (LITTLE_ENDIAN, BIG_ENDIAN, MIDDLE_ENDIAN)
|
||||
text = []
|
||||
while (value != 0 or text == ""):
|
||||
byte = value % 256
|
||||
|
@ -153,13 +176,15 @@ def long2raw(value, endian, size=None):
|
|||
else:
|
||||
need = 0
|
||||
if need:
|
||||
if endian is BIG_ENDIAN:
|
||||
text = chain(repeat("\0", need), reversed(text))
|
||||
else:
|
||||
if endian is LITTLE_ENDIAN:
|
||||
text = chain(text, repeat("\0", need))
|
||||
else:
|
||||
text = chain(repeat("\0", need), reversed(text))
|
||||
else:
|
||||
if endian is BIG_ENDIAN:
|
||||
if endian is not LITTLE_ENDIAN:
|
||||
text = reversed(text)
|
||||
if endian is MIDDLE_ENDIAN:
|
||||
text = arrswapmid(text)
|
||||
return "".join(text)
|
||||
|
||||
def long2bin(size, value, endian, classic_mode=False):
|
||||
|
@ -257,6 +282,8 @@ def str2long(data, endian):
|
|||
True
|
||||
>>> str2long("\xff\xff\xff\xff\xff\xff\xff\xff", BIG_ENDIAN) == (2**64-1)
|
||||
True
|
||||
>>> str2long("\x0b\x0a\x0d\x0c", MIDDLE_ENDIAN) == 0x0a0b0c0d
|
||||
True
|
||||
"""
|
||||
assert 1 <= len(data) <= 32 # arbitrary limit: 256 bits
|
||||
try:
|
||||
|
@ -264,14 +291,15 @@ def str2long(data, endian):
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
assert endian in (BIG_ENDIAN, LITTLE_ENDIAN)
|
||||
assert endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
|
||||
shift = 0
|
||||
value = 0
|
||||
if endian is BIG_ENDIAN:
|
||||
data = reversed(data)
|
||||
elif endian is MIDDLE_ENDIAN:
|
||||
data = reversed(strswapmid(data))
|
||||
for character in data:
|
||||
byte = ord(character)
|
||||
value += (byte << shift)
|
||||
shift += 8
|
||||
return value
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from optparse import OptionGroup
|
||||
from lib.hachoir_core.log import log
|
||||
from lib.hachoir_core.i18n import _, getTerminalCharset
|
||||
from lib.hachoir_core.tools import makePrintable
|
||||
import lib.hachoir_core.config as config
|
||||
from hachoir_core.log import log
|
||||
from hachoir_core.i18n import _, getTerminalCharset
|
||||
from hachoir_core.tools import makePrintable
|
||||
import hachoir_core.config as config
|
||||
|
||||
def getHachoirOptions(parser):
|
||||
"""
|
||||
|
|
|
@ -14,7 +14,7 @@ unicode_stdout = True # Replace stdout and stderr with Unicode compatible ob
|
|||
# Global options
|
||||
debug = False # Display many informations usefull to debug
|
||||
verbose = False # Display more informations
|
||||
quiet = False # Don't display warnings
|
||||
quiet = True # Don't display warnings
|
||||
|
||||
# Use internationalization and localization (gettext)?
|
||||
if os.name == "nt":
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
Dictionnary classes which store values order.
|
||||
"""
|
||||
|
||||
from lib.hachoir_core.error import HachoirError
|
||||
from lib.hachoir_core.i18n import _
|
||||
from hachoir_core.error import HachoirError
|
||||
from hachoir_core.i18n import _
|
||||
|
||||
class UniqKeyError(HachoirError):
|
||||
"""
|
||||
|
|