1
0
mirror of https://github.com/moparisthebest/SickRage synced 2024-11-17 06:45:05 -05:00

Merge branch 'release/v4.0.2'

This commit is contained in:
echel0n 2014-12-17 17:32:36 -08:00
commit 514e9d848f
13 changed files with 209 additions and 84 deletions

View File

@ -37,7 +37,6 @@
#else #else
You don't have version checking turned on. Please turn on "Check for Update" in Config > General.<br /> You don't have version checking turned on. Please turn on "Check for Update" in Config > General.<br />
#end if #end if
<font color="red">You are using BETA software</font>
</td></tr> </td></tr>
<tr><td class="infoTableHeader">SR Config file:</td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr> <tr><td class="infoTableHeader">SR Config file:</td><td class="infoTableCell">$sickbeard.CONFIG_FILE</td></tr>
<tr><td class="infoTableHeader">SR Database file:</td><td class="infoTableCell">$db.dbFilename()</td></tr> <tr><td class="infoTableHeader">SR Database file:</td><td class="infoTableCell">$db.dbFilename()</td></tr>

View File

@ -438,48 +438,14 @@
<div id="core-component-group3" class="component-group"> <div id="core-component-group3" class="component-group">
<div class="component-group">
<div class="component-group-desc"> <div class="component-group-desc">
<h3>Advanced Settings</h3> <h3>Advanced Settings</h3>
</div> </div>
<fieldset class="component-group-list"> <fieldset class="component-group-list">
<div class="field-pair">
<label>
<span class="component-title">Branch version:</span>
<span class="component-desc">
<select id="branchVersion" class="form-control form-control-inline input-sm pull-left">
#for $cur_branch in $sickbeard.versionCheckScheduler.action.list_remote_branches():
<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
#end for
</select>
<input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch">
<div class="clear-left"><p>select branch to use (restart required)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="git_remote">
<span class="component-title">Git remote for branch</span>
<span class="component-desc">
<input type="text" name="git_remote" id="git_remote" value="$sickbeard.GIT_REMOTE" class="form-control input-sm input300" />
<div class="clear-left"><p>default:origin. Access repo configured remotes (save then refresh browser)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label>
<span class="component-title">Git executable path</span>
<span class="component-desc">
<input type="text" name="git_path" value="$sickbeard.GIT_PATH" class="form-control input-sm input300" />
<div class="clear-left"><p>only needed if OS is unable to locate git from env</p></div>
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label> <label>
<span class="component-title">CPU throttling:</span> <span class="component-title">CPU throttling:</span>
@ -548,6 +514,86 @@
<input type="submit" class="btn config_submitter" value="Save Changes" /> <input type="submit" class="btn config_submitter" value="Save Changes" />
</fieldset> </fieldset>
</div>
<div class="component-group">
<div class="component-group-desc">
<h3>GitHub</h3>
<p>Options for github related features.</p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
<label>
<span class="component-title">Branch version:</span>
<span class="component-desc">
<select id="branchVersion" class="form-control form-control-inline input-sm pull-left">
#for $cur_branch in $sickbeard.versionCheckScheduler.action.list_remote_branches():
<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
#end for
</select>
<input class="btn btn-inline" style="margin-left: 6px;" type="button" id="branchCheckout" value="Checkout Branch">
<div class="clear-left"><p>select branch to use (restart required)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="git_username">
<span class="component-title">GitHub username</span>
<span class="component-desc">
<input type="text" name="git_username" id="git_username" value="$sickbeard.GIT_USERNAME" class="form-control input-sm input300" />
<div class="clear-left"><p>*** (REQUIRED FOR SUBMITTING ISSUES) ***</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="git_password">
<span class="component-title">GitHub password</span>
<span class="component-desc">
<input type="password" name="git_password" id="git_password" value="$sickbeard.GIT_PASSWORD" class="form-control input-sm input300" />
<div class="clear-left"><p>*** (REQUIRED FOR SUBMITTING ISSUES) ***</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="git_remote">
<span class="component-title">GitHub remote for branch</span>
<span class="component-desc">
<input type="text" name="git_remote" id="git_remote" value="$sickbeard.GIT_REMOTE" class="form-control input-sm input300" />
<div class="clear-left"><p>default:origin. Access repo configured remotes (save then refresh browser)</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label>
<span class="component-title">Git executable path</span>
<span class="component-desc">
<input type="text" name="git_path" value="$sickbeard.GIT_PATH" class="form-control input-sm input300" />
<div class="clear-left"><p>only needed if OS is unable to locate git from env</p></div>
</span>
</label>
</div>
<div class="field-pair">
<label for="git_reset">
<span class="component-title">Git branch reset</span>
<span class="component-desc">
<input type="checkbox" name="git_reset" id="git_reset" #if True == $sickbeard.GIT_RESET then 'checked="checked"' else ''#/>
<p>reset git branch automatically to help resolve update issues</p>
</span>
</label>
</div>
<input type="submit" class="btn config_submitter" value="Save Changes" />
</fieldset>
</div>
</div><!-- /component-group3 //--> </div><!-- /component-group3 //-->
<br/> <br/>

View File

@ -106,7 +106,7 @@
(this will set a default status to be applied to any newly added episodes)<br /> (this will set a default status to be applied to any newly added episodes)<br />
<select name="defaultEpStatus" id="defaultEpStatusSelect" class="form-control form-control-inline input-sm"> <select name="defaultEpStatus" id="defaultEpStatusSelect" class="form-control form-control-inline input-sm">
#for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED]: #for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED]:
<option value="$curStatus">$statusStrings[$curStatus]</option> <option value="$curStatus" #if $curStatus == $show.default_ep_status then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
#end for #end for
</select><br /> </select><br />
<br /> <br />

View File

@ -89,6 +89,7 @@
\$("#SubMenu a:contains('Clear History')").addClass('btn clearhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear History'); \$("#SubMenu a:contains('Clear History')").addClass('btn clearhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear History');
\$("#SubMenu a:contains('Trim History')").addClass('btn trimhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Trim History'); \$("#SubMenu a:contains('Trim History')").addClass('btn trimhistory').html('<span class="ui-icon ui-icon-trash pull-left"></span> Trim History');
\$("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear Errors'); \$("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear Errors');
\$("#SubMenu a[href$='/errorlogs/submit_errors/']").addClass('btn').html('<span class="ui-icon ui-icon-arrowreturnthick-1-n pull-left"></span> Submit Errors');
\$("#SubMenu a:contains('Re-scan')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Re-scan'); \$("#SubMenu a:contains('Re-scan')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Re-scan');
\$("#SubMenu a:contains('Backlog Overview')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Backlog Overview'); \$("#SubMenu a:contains('Backlog Overview')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Backlog Overview');
\$("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update PLEX'); \$("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update PLEX');

View File

@ -3,7 +3,7 @@ SickRage
Video File Manager for TV Shows, It watches for new episodes of your favorite shows and when they are posted it does its magic. Video File Manager for TV Shows, It watches for new episodes of your favorite shows and when they are posted it does its magic.
## Branch Build Status ## Branch Build Status
[![Build Status](https://travis-ci.org/SiCKRAGETV/SickRage.svg?branch=develop)](https://travis-ci.org/SiCKRAGETV/SickRage) [![Build Status](https://travis-ci.org/SiCKRAGETV/SickRage.svg?branch=nightly)](https://travis-ci.org/SiCKRAGETV/SickRage)
## Features ## Features
- XBMC library updates, poster/fanart downloads, and NFO/TBN generation - XBMC library updates, poster/fanart downloads, and NFO/TBN generation

View File

@ -115,6 +115,8 @@ GIT_REMOTE_URL = ''
CUR_COMMIT_BRANCH = '' CUR_COMMIT_BRANCH = ''
GIT_ORG = 'SiCKRAGETV' GIT_ORG = 'SiCKRAGETV'
GIT_REPO = 'SickRage' GIT_REPO = 'SickRage'
GIT_USERNAME = None
GIT_PASSWORD = None
GIT_PATH = None GIT_PATH = None
INIT_LOCK = Lock() INIT_LOCK = Lock()
@ -522,7 +524,7 @@ def initialize(consoleLogging=True):
USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, PROXY_SETTING, PROXY_INDEXERS, \ USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, PROXY_SETTING, PROXY_INDEXERS, \
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \ ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
ANIME_SPLIT_HOME, SCENE_DEFAULT, PLAY_VIDEOS, BACKLOG_DAYS, GIT_ORG, GIT_REPO, gh ANIME_SPLIT_HOME, SCENE_DEFAULT, PLAY_VIDEOS, BACKLOG_DAYS, GIT_ORG, GIT_REPO, GIT_USERNAME, GIT_PASSWORD, gh
if __INITIALIZED__: if __INITIALIZED__:
return False return False
@ -558,14 +560,20 @@ def initialize(consoleLogging=True):
fileLogging = True fileLogging = True
if not helpers.makeDir(LOG_DIR): if not helpers.makeDir(LOG_DIR):
sys.stderr.write("!!! No log folder, logging to screen only!\n") sys.stderr.write("!!! No log folder, logging to screen only!\n")
fileLogging=False fileLogging = False
# init logging # init logging
logger.initLogging(consoleLogging=consoleLogging, fileLogging=fileLogging, debugLogging=DEBUG) logger.initLogging(consoleLogging=consoleLogging, fileLogging=fileLogging, debugLogging=DEBUG)
# git login info
GIT_USERNAME = check_setting_str(CFG, 'General', 'git_username', '')
GIT_PASSWORD = check_setting_str(CFG, 'General', 'git_password', '', censor_log=True)
# github api # github api
try:gh = Github().get_organization(GIT_ORG).get_repo(GIT_REPO) try:
except:gh = None gh = Github(user_agent="SiCKRAGE").get_organization(GIT_ORG).get_repo(GIT_REPO)
except:
gh = None
# git reset on update # git reset on update
GIT_RESET = bool(check_setting_int(CFG, 'General', 'git_reset', 0)) GIT_RESET = bool(check_setting_int(CFG, 'General', 'git_reset', 0))
@ -1131,7 +1139,7 @@ def initialize(consoleLogging=True):
(METADATA_WDTV, metadata.wdtv), (METADATA_WDTV, metadata.wdtv),
(METADATA_TIVO, metadata.tivo), (METADATA_TIVO, metadata.tivo),
(METADATA_MEDE8ER, metadata.mede8er), (METADATA_MEDE8ER, metadata.mede8er),
]: ]:
(cur_metadata_config, cur_metadata_class) = cur_metadata_tuple (cur_metadata_config, cur_metadata_class) = cur_metadata_tuple
tmp_provider = cur_metadata_class.metadata_class() tmp_provider = cur_metadata_class.metadata_class()
tmp_provider.set_config(cur_metadata_config) tmp_provider.set_config(cur_metadata_config)
@ -1401,6 +1409,8 @@ def save_config():
# For passwords you must include the word `password` in the item_name and add `helpers.encrypt(ITEM_NAME, ENCRYPTION_VERSION)` in save_config() # For passwords you must include the word `password` in the item_name and add `helpers.encrypt(ITEM_NAME, ENCRYPTION_VERSION)` in save_config()
new_config['General'] = {} new_config['General'] = {}
new_config['General']['git_username'] = GIT_USERNAME
new_config['General']['git_password'] = GIT_PASSWORD
new_config['General']['git_reset'] = int(GIT_RESET) new_config['General']['git_reset'] = int(GIT_RESET)
new_config['General']['branch'] = BRANCH new_config['General']['branch'] = BRANCH
new_config['General']['git_remote'] = GIT_REMOTE new_config['General']['git_remote'] = GIT_REMOTE

View File

@ -1,5 +0,0 @@
import sickbeard
class BugTracker:
def submit_bug(self, title, error):
return sickbeard.gh.create_issue(title, error, labels=[sickbeard.gh().get_label(sickbeard.BRANCH)])

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with SickRage. If not, see <http://www.gnu.org/licenses/>. # along with SickRage. If not, see <http://www.gnu.org/licenses/>.
import re import re
import sys
import sickbeard import sickbeard
@ -261,6 +262,9 @@ class ErrorViewer():
def clear(): def clear():
ErrorViewer.errors = [] ErrorViewer.errors = []
@staticmethod
def get():
return ErrorViewer.errors
class UIError(): class UIError():
""" """
@ -268,5 +272,7 @@ class UIError():
""" """
def __init__(self, message): def __init__(self, message):
self.title = sys.exc_info()[1].message or None
self.message = message self.message = message
self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.exc_info = sys.exc_info() or None

View File

@ -236,8 +236,6 @@ class Quality:
def assumeQuality(name): def assumeQuality(name):
if name.lower().endswith((".avi", ".mp4")): if name.lower().endswith((".avi", ".mp4")):
return Quality.SDTV return Quality.SDTV
# elif name.lower().endswith(".mkv"):
# return Quality.HDTV
elif name.lower().endswith(".ts"): elif name.lower().endswith(".ts"):
return Quality.RAWHDTV return Quality.RAWHDTV
else: else:

View File

@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
MIN_DB_VERSION = 9 # oldest db version we support migrating from MIN_DB_VERSION = 9 # oldest db version we support migrating from
MAX_DB_VERSION = 41 MAX_DB_VERSION = 42
class MainSanityCheck(db.DBSanityCheck): class MainSanityCheck(db.DBSanityCheck):
def check(self): def check(self):
@ -37,6 +37,7 @@ class MainSanityCheck(db.DBSanityCheck):
self.fix_orphan_episodes() self.fix_orphan_episodes()
self.fix_unaired_episodes() self.fix_unaired_episodes()
self.fix_tvrage_show_statues() self.fix_tvrage_show_statues()
self.fix_episode_statuses()
def fix_duplicate_shows(self, column='indexer_id'): def fix_duplicate_shows(self, column='indexer_id'):
@ -107,7 +108,7 @@ class MainSanityCheck(db.DBSanityCheck):
if not self.connection.select("PRAGMA index_info('idx_tv_episodes_showid_airdate')"): if not self.connection.select("PRAGMA index_info('idx_tv_episodes_showid_airdate')"):
logger.log(u"Missing idx_tv_episodes_showid_airdate for TV Episodes table detected!, fixing...") logger.log(u"Missing idx_tv_episodes_showid_airdate for TV Episodes table detected!, fixing...")
self.connection.action("CREATE INDEX idx_tv_episodes_showid_airdate ON tv_episodes(showid,airdate);") self.connection.action("CREATE INDEX idx_tv_episodes_showid_airdate ON tv_episodes(showid, airdate);")
if not self.connection.select("PRAGMA index_info('idx_showid')"): if not self.connection.select("PRAGMA index_info('idx_showid')"):
logger.log(u"Missing idx_showid for TV Episodes table detected!, fixing...") logger.log(u"Missing idx_showid for TV Episodes table detected!, fixing...")
@ -115,15 +116,15 @@ class MainSanityCheck(db.DBSanityCheck):
if not self.connection.select("PRAGMA index_info('idx_status')"): if not self.connection.select("PRAGMA index_info('idx_status')"):
logger.log(u"Missing idx_status for TV Episodes table detected!, fixing...") logger.log(u"Missing idx_status for TV Episodes table detected!, fixing...")
self.connection.action("CREATE INDEX idx_status ON tv_episodes (status,season,episode,airdate)") self.connection.action("CREATE INDEX idx_status ON tv_episodes (status, season, episode, airdate)")
if not self.connection.select("PRAGMA index_info('idx_sta_epi_air')"): if not self.connection.select("PRAGMA index_info('idx_sta_epi_air')"):
logger.log(u"Missing idx_sta_epi_air for TV Episodes table detected!, fixing...") logger.log(u"Missing idx_sta_epi_air for TV Episodes table detected!, fixing...")
self.connection.action("CREATE INDEX idx_sta_epi_air ON tv_episodes (status,episode, airdate)") self.connection.action("CREATE INDEX idx_sta_epi_air ON tv_episodes (status, episode, airdate)")
if not self.connection.select("PRAGMA index_info('idx_sta_epi_sta_air')"): if not self.connection.select("PRAGMA index_info('idx_sta_epi_sta_air')"):
logger.log(u"Missing idx_sta_epi_sta_air for TV Episodes table detected!, fixing...") logger.log(u"Missing idx_sta_epi_sta_air for TV Episodes table detected!, fixing...")
self.connection.action("CREATE INDEX idx_sta_epi_sta_air ON tv_episodes (season,episode, status, airdate)") self.connection.action("CREATE INDEX idx_sta_epi_sta_air ON tv_episodes (season, episode, status, airdate)")
def fix_unaired_episodes(self): def fix_unaired_episodes(self):
@ -163,6 +164,19 @@ class MainSanityCheck(db.DBSanityCheck):
for old_status, new_status in status_map.items(): for old_status, new_status in status_map.items():
self.connection.action("UPDATE tv_shows SET status = ? WHERE LOWER(status) = ?", [new_status, old_status]) self.connection.action("UPDATE tv_shows SET status = ? WHERE LOWER(status) = ?", [new_status, old_status])
def fix_episode_statuses(self):
sqlResults = self.connection.select("SELECT episode_id, showid FROM tv_episodes WHERE status IS NULL")
for cur_ep in sqlResults:
logger.log(u"MALFORMED episode status detected! episode_id: " + str(cur_ep["episode_id"]) + " showid: " + str(
cur_ep["showid"]), logger.DEBUG)
logger.log(u"Fixing malformed episode status with episode_id: " + str(cur_ep["episode_id"]))
self.connection.action("UPDATE tv_episodes SET status = ? WHERE episode_id = ?",
[common.UNKNOWN, cur_ep["episode_id"]])
else:
logger.log(u"No MALFORMED episode statuses, check passed")
def backupDatabase(version): def backupDatabase(version):
logger.log(u"Backing up database before upgrade") logger.log(u"Backing up database before upgrade")
if not helpers.backupVersionedFile(db.dbFilename(), version): if not helpers.backupVersionedFile(db.dbFilename(), version):
@ -187,7 +201,7 @@ class InitialSchema(db.SchemaUpgrade):
"CREATE TABLE imdb_info (indexer_id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, year NUMERIC, akas TEXT, runtimes NUMERIC, genres TEXT, countries TEXT, country_codes TEXT, certificates TEXT, rating TEXT, votes INTEGER, last_update NUMERIC)", "CREATE TABLE imdb_info (indexer_id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, year NUMERIC, akas TEXT, runtimes NUMERIC, genres TEXT, countries TEXT, country_codes TEXT, certificates TEXT, rating TEXT, votes INTEGER, last_update NUMERIC)",
"CREATE TABLE info (last_backlog NUMERIC, last_indexer NUMERIC, last_proper_search NUMERIC)", "CREATE TABLE info (last_backlog NUMERIC, last_indexer NUMERIC, last_proper_search NUMERIC)",
"CREATE TABLE scene_numbering(indexer TEXT, indexer_id INTEGER, season INTEGER, episode INTEGER,scene_season INTEGER, scene_episode INTEGER, PRIMARY KEY(indexer_id, season, episode))", "CREATE TABLE scene_numbering(indexer TEXT, indexer_id INTEGER, season INTEGER, episode INTEGER,scene_season INTEGER, scene_episode INTEGER, PRIMARY KEY(indexer_id, season, episode))",
"CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, indexer_id NUMERIC, indexer TEXT, show_name TEXT, location TEXT, network TEXT, genre TEXT, classification TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, air_by_date NUMERIC, lang TEXT, subtitles NUMERIC, notify_list TEXT, imdb_id TEXT, last_update_indexer NUMERIC, dvdorder NUMERIC, archive_firstmatch NUMERIC, rls_require_words TEXT, rls_ignore_words TEXT, sports NUMERIC);", "CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, indexer_id NUMERIC, indexer NUMERIC, show_name TEXT, location TEXT, network TEXT, genre TEXT, classification TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, air_by_date NUMERIC, lang TEXT, subtitles NUMERIC, notify_list TEXT, imdb_id TEXT, last_update_indexer NUMERIC, dvdorder NUMERIC, archive_firstmatch NUMERIC, rls_require_words TEXT, rls_ignore_words TEXT, sports NUMERIC);",
"CREATE TABLE tv_episodes (episode_id INTEGER PRIMARY KEY, showid NUMERIC, indexerid NUMERIC, indexer TEXT, name TEXT, season NUMERIC, episode NUMERIC, description TEXT, airdate NUMERIC, hasnfo NUMERIC, hastbn NUMERIC, status NUMERIC, location TEXT, file_size NUMERIC, release_name TEXT, subtitles TEXT, subtitles_searchcount NUMERIC, subtitles_lastsearch TIMESTAMP, is_proper NUMERIC, scene_season NUMERIC, scene_episode NUMERIC);", "CREATE TABLE tv_episodes (episode_id INTEGER PRIMARY KEY, showid NUMERIC, indexerid NUMERIC, indexer TEXT, name TEXT, season NUMERIC, episode NUMERIC, description TEXT, airdate NUMERIC, hasnfo NUMERIC, hastbn NUMERIC, status NUMERIC, location TEXT, file_size NUMERIC, release_name TEXT, subtitles TEXT, subtitles_searchcount NUMERIC, subtitles_lastsearch TIMESTAMP, is_proper NUMERIC, scene_season NUMERIC, scene_episode NUMERIC);",
"CREATE UNIQUE INDEX idx_indexer_id ON tv_shows (indexer_id)", "CREATE UNIQUE INDEX idx_indexer_id ON tv_shows (indexer_id)",
"CREATE INDEX idx_showid ON tv_episodes (showid);", "CREATE INDEX idx_showid ON tv_episodes (showid);",
@ -339,7 +353,7 @@ class RenameSeasonFolders(AddSizeAndSceneNameFields):
self.connection.action("ALTER TABLE tv_shows RENAME TO tmp_tv_shows") self.connection.action("ALTER TABLE tv_shows RENAME TO tmp_tv_shows")
self.connection.action( self.connection.action(
"CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, location TEXT, show_name TEXT, tvdb_id NUMERIC, network TEXT, genre TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, tvr_id NUMERIC, tvr_name TEXT, air_by_date NUMERIC, lang TEXT)") "CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, location TEXT, show_name TEXT, tvdb_id NUMERIC, network TEXT, genre TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, tvr_id NUMERIC, tvr_name TEXT, air_by_date NUMERIC, lang TEXT)")
sql = "INSERT INTO tv_shows(show_id, location, show_name, tvdb_id, network, genre, runtime, quality, airs, status, flatten_folders, paused, startyear, tvr_id, tvr_name, air_by_date, lang) SELECT show_id, location, show_name, tvdb_id, network, genre, runtime, quality, airs, status, seasonfolders, paused, startyear, tvr_id, tvr_name, air_by_date, lang FROM tmp_tv_shows" sql = "INSERT INTO tv_shows SELECT * FROM tmp_tv_shows"
self.connection.action(sql) self.connection.action(sql)
# flip the values to be opposite of what they were before # flip the values to be opposite of what they were before
@ -616,10 +630,8 @@ class ConvertTVShowsToIndexerScheme(AddSubtitlesSupport):
self.connection.action("DROP TABLE tmp_tv_shows") self.connection.action("DROP TABLE tmp_tv_shows")
self.connection.action("ALTER TABLE tv_shows RENAME TO tmp_tv_shows") self.connection.action("ALTER TABLE tv_shows RENAME TO tmp_tv_shows")
self.connection.action( self.connection.action("CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, indexer_id NUMERIC, indexer NUMERIC, show_name TEXT, location TEXT, network TEXT, genre TEXT, classification TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, air_by_date NUMERIC, lang TEXT, subtitles NUMERIC, notify_list TEXT, imdb_id TEXT, last_update_indexer NUMERIC, dvdorder NUMERIC)")
"CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, indexer_id NUMERIC, indexer NUMERIC, show_name TEXT, location TEXT, network TEXT, genre TEXT, classification TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, air_by_date NUMERIC, lang TEXT, subtitles NUMERIC, notify_list TEXT, imdb_id TEXT, last_update_indexer NUMERIC, dvdorder NUMERIC)") self.connection.action("INSERT INTO tv_shows SELECT * FROM tmp_tv_shows")
self.connection.action(
"INSERT INTO tv_shows(show_id, indexer_id, show_name, location, network, genre, runtime, quality, airs, status, flatten_folders, paused, startyear, air_by_date, lang, subtitles, notify_list, imdb_id, last_update_indexer, dvdorder) SELECT show_id, tvdb_id, show_name, location, network, genre, runtime, quality, airs, status, flatten_folders, paused, startyear, air_by_date, lang, subtitles, notify_list, imdb_id, last_update_tvdb, dvdorder FROM tmp_tv_shows")
self.connection.action("DROP TABLE tmp_tv_shows") self.connection.action("DROP TABLE tmp_tv_shows")
self.connection.action("CREATE UNIQUE INDEX idx_indexer_id ON tv_shows (indexer_id);") self.connection.action("CREATE UNIQUE INDEX idx_indexer_id ON tv_shows (indexer_id);")
@ -647,7 +659,7 @@ class ConvertTVEpisodesToIndexerScheme(ConvertTVShowsToIndexerScheme):
self.connection.action( self.connection.action(
"CREATE TABLE tv_episodes (episode_id INTEGER PRIMARY KEY, showid NUMERIC, indexerid NUMERIC, indexer NUMERIC, name TEXT, season NUMERIC, episode NUMERIC, description TEXT, airdate NUMERIC, hasnfo NUMERIC, hastbn NUMERIC, status NUMERIC, location TEXT, file_size NUMERIC, release_name TEXT, subtitles TEXT, subtitles_searchcount NUMERIC, subtitles_lastsearch TIMESTAMP, is_proper NUMERIC)") "CREATE TABLE tv_episodes (episode_id INTEGER PRIMARY KEY, showid NUMERIC, indexerid NUMERIC, indexer NUMERIC, name TEXT, season NUMERIC, episode NUMERIC, description TEXT, airdate NUMERIC, hasnfo NUMERIC, hastbn NUMERIC, status NUMERIC, location TEXT, file_size NUMERIC, release_name TEXT, subtitles TEXT, subtitles_searchcount NUMERIC, subtitles_lastsearch TIMESTAMP, is_proper NUMERIC)")
self.connection.action( self.connection.action(
"INSERT INTO tv_episodes(episode_id, showid, indexerid, name, season, episode, description, airdate, hasnfo, hastbn, status, location, file_size, release_name, subtitles, subtitles_searchcount, subtitles_lastsearch, is_proper) SELECT episode_id, showid, tvdbid, name, season, episode, description, airdate, hasnfo, hastbn, status, location, file_size, release_name, subtitles, subtitles_searchcount, subtitles_lastsearch, is_proper FROM tmp_tv_episodes") "INSERT INTO tv_episodes SELECT * FROM tmp_tv_episodes")
self.connection.action("DROP TABLE tmp_tv_episodes") self.connection.action("DROP TABLE tmp_tv_episodes")
self.connection.action("CREATE INDEX idx_tv_episodes_showid_airdate ON tv_episodes(showid,airdate);") self.connection.action("CREATE INDEX idx_tv_episodes_showid_airdate ON tv_episodes(showid,airdate);")
@ -678,7 +690,7 @@ class ConvertIMDBInfoToIndexerScheme(ConvertTVEpisodesToIndexerScheme):
self.connection.action( self.connection.action(
"CREATE TABLE imdb_info (indexer_id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, year NUMERIC, akas TEXT, runtimes NUMERIC, genres TEXT, countries TEXT, country_codes TEXT, certificates TEXT, rating TEXT, votes INTEGER, last_update NUMERIC)") "CREATE TABLE imdb_info (indexer_id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, year NUMERIC, akas TEXT, runtimes NUMERIC, genres TEXT, countries TEXT, country_codes TEXT, certificates TEXT, rating TEXT, votes INTEGER, last_update NUMERIC)")
self.connection.action( self.connection.action(
"INSERT INTO imdb_info(indexer_id, imdb_id, title, year, akas, runtimes, genres, countries, country_codes, certificates, rating, votes, last_update) SELECT tvdb_id, imdb_id, title, year, akas, runtimes, genres, countries, country_codes, certificates, rating, votes, last_update FROM tmp_imdb_info") "INSERT INTO imdb_info SELECT * FROM tmp_imdb_info")
self.connection.action("DROP TABLE tmp_imdb_info") self.connection.action("DROP TABLE tmp_imdb_info")
self.incDBVersion() self.incDBVersion()
@ -701,7 +713,7 @@ class ConvertInfoToIndexerScheme(ConvertIMDBInfoToIndexerScheme):
self.connection.action( self.connection.action(
"CREATE TABLE info (last_backlog NUMERIC, last_indexer NUMERIC, last_proper_search NUMERIC)") "CREATE TABLE info (last_backlog NUMERIC, last_indexer NUMERIC, last_proper_search NUMERIC)")
self.connection.action( self.connection.action(
"INSERT INTO info(last_backlog, last_indexer, last_proper_search) SELECT last_backlog, last_tvdb, last_proper_search FROM tmp_info") "INSERT INTO info SELECT * FROM tmp_info")
self.connection.action("DROP TABLE tmp_info") self.connection.action("DROP TABLE tmp_info")
self.incDBVersion() self.incDBVersion()
@ -944,6 +956,21 @@ class AddDefaultEpStatusToTvShows(AddVersionToTvEpisodes):
backupDatabase(41) backupDatabase(41)
logger.log(u"Adding column default_ep_status to tv_shows") logger.log(u"Adding column default_ep_status to tv_shows")
self.addColumn("tv_shows", "default_ep_status", "TEXT", "") self.addColumn("tv_shows", "default_ep_status", "NUMERIC", "-1")
self.incDBVersion()
class AlterTVShowsFieldTypes(AddDefaultEpStatusToTvShows):
def test(self):
return self.checkDBVersion() >= 42
def execute(self):
backupDatabase(42)
logger.log(u"Converting column indexer and default_ep_status field types to numeric")
self.connection.action("ALTER TABLE tv_shows RENAME TO tmp_tv_shows")
self.connection.action("CREATE TABLE tv_shows (show_id INTEGER PRIMARY KEY, indexer_id NUMERIC, indexer NUMERIC, show_name TEXT, location TEXT, network TEXT, genre TEXT, classification TEXT, runtime NUMERIC, quality NUMERIC, airs TEXT, status TEXT, flatten_folders NUMERIC, paused NUMERIC, startyear NUMERIC, air_by_date NUMERIC, lang TEXT, subtitles NUMERIC, notify_list TEXT, imdb_id TEXT, last_update_indexer NUMERIC, dvdorder NUMERIC, archive_firstmatch NUMERIC, rls_require_words TEXT, rls_ignore_words TEXT, sports NUMERIC, anime NUMERIC, scene NUMERIC, default_ep_status NUMERIC)")
self.connection.action("INSERT INTO tv_shows SELECT * FROM tmp_tv_shows")
self.connection.action("DROP TABLE tmp_tv_shows")
self.incDBVersion() self.incDBVersion()

View File

@ -240,7 +240,7 @@ class TVShow(object):
# if we get an anime get the real season and episode # if we get an anime get the real season and episode
if self.is_anime and absolute_number and not season and not episode: if self.is_anime and absolute_number and not season and not episode:
myDB = db.DBConnection() myDB = db.DBConnection()
sql = "SELECT * FROM tv_episodes WHERE showid = ? and absolute_number = ? and season != 0" sql = "SELECT * FROM tv_episodes WHERE showid = ? AND absolute_number = ? AND season != 0"
sqlResults = myDB.select(sql, [self.indexerid, absolute_number]) sqlResults = myDB.select(sql, [self.indexerid, absolute_number])
if len(sqlResults) == 1: if len(sqlResults) == 1:
@ -282,7 +282,7 @@ class TVShow(object):
def should_update(self, update_date=datetime.date.today()): def should_update(self, update_date=datetime.date.today()):
# if show is not 'Ended' always update (status 'Continuing') # if show is not 'Ended' always update (status 'Continuing')
if 'Unknown' not in self.status and 'Ended' not in self.status: if self.status == 'Continuing':
return True return True
# run logic against the current show latest aired and next unaired data to see if we should bypass 'Ended' status # run logic against the current show latest aired and next unaired data to see if we should bypass 'Ended' status
@ -500,7 +500,7 @@ class TVShow(object):
try: try:
curEp = self.getEpisode(curSeason, curEpisode) curEp = self.getEpisode(curSeason, curEpisode)
if not curEp: if not curEp:
raise exceptions.EpisodeNotFoundException raise exceptions.EpisodeNotFoundException
# if we found out that the ep is no longer on TVDB then delete it from our database too # if we found out that the ep is no longer on TVDB then delete it from our database too
if deleteEp: if deleteEp:
@ -957,7 +957,7 @@ class TVShow(object):
if not self.nextaired or self.nextaired and curDate > self.nextaired: if not self.nextaired or self.nextaired and curDate > self.nextaired:
myDB = db.DBConnection() myDB = db.DBConnection()
sqlResults = myDB.select( sqlResults = myDB.select(
"SELECT airdate, season, episode FROM tv_episodes WHERE showid = ? AND airdate >= ? AND status in (?,?) ORDER BY airdate ASC LIMIT 1", "SELECT airdate, season, episode FROM tv_episodes WHERE showid = ? AND airdate >= ? AND status IN (?,?) ORDER BY airdate ASC LIMIT 1",
[self.indexerid, datetime.date.today().toordinal(), UNAIRED, WANTED]) [self.indexerid, datetime.date.today().toordinal(), UNAIRED, WANTED])
if sqlResults == None or len(sqlResults) == 0: if sqlResults == None or len(sqlResults) == 0:
@ -1526,7 +1526,7 @@ class TVEpisode(object):
self.subtitles_lastsearch = sqlResults[0]["subtitles_lastsearch"] self.subtitles_lastsearch = sqlResults[0]["subtitles_lastsearch"]
self.airdate = datetime.date.fromordinal(int(sqlResults[0]["airdate"])) self.airdate = datetime.date.fromordinal(int(sqlResults[0]["airdate"]))
# logger.log(u"1 Status changes from " + str(self.status) + " to " + str(sqlResults[0]["status"]), logger.DEBUG) # logger.log(u"1 Status changes from " + str(self.status) + " to " + str(sqlResults[0]["status"]), logger.DEBUG)
self.status = int(sqlResults[0]["status"]) self.status = int(sqlResults[0]["status"] or -1)
# don't overwrite my location # don't overwrite my location
if sqlResults[0]["location"] and sqlResults[0]["location"]: if sqlResults[0]["location"] and sqlResults[0]["location"]:
@ -1677,15 +1677,15 @@ class TVEpisode(object):
self.description = getattr(myEp, 'overview', "") self.description = getattr(myEp, 'overview', "")
firstaired = getattr(myEp, 'firstaired', None) firstaired = getattr(myEp, 'firstaired', None)
if firstaired is None or firstaired in "0000-00-00": if not firstaired or firstaired == "0000-00-00":
firstaired = str(datetime.date.fromordinal(1)) firstaired = str(datetime.date.fromordinal(1))
rawAirdate = [int(x) for x in firstaired.split("-")] rawAirdate = [int(x) for x in firstaired.split("-")]
try: try:
self.airdate = datetime.date(rawAirdate[0], rawAirdate[1], rawAirdate[2]) self.airdate = datetime.date(rawAirdate[0], rawAirdate[1], rawAirdate[2])
except (ValueError, IndexError): except (ValueError, IndexError):
logger.log(u"Malformed air date retrieved from " + sickbeard.indexerApi( logger.log(u"Malformed air date of " + str(firstaired) + " retrieved from " + sickbeard.indexerApi(
self.indexer).name + " (" + self.show.name + " - " + str(season) + "x" + str(episode) + ")", self.indexer).name + " for (" + self.show.name + " - " + str(season) + "x" + str(episode) + ")",
logger.ERROR) logger.ERROR)
# if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now # if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now
if self.indexerid != -1: if self.indexerid != -1:

View File

@ -24,8 +24,11 @@ import time
import urllib import urllib
import re import re
import datetime import datetime
import sys
import platform
import sickbeard import sickbeard
from github import Github
from sickbeard import config, sab from sickbeard import config, sab
from sickbeard import clients from sickbeard import clients
from sickbeard import history, notifiers, processTV from sickbeard import history, notifiers, processTV
@ -180,9 +183,9 @@ class BaseHandler(RequestHandler):
<p>%s</p> <p>%s</p>
<h2>Request Info</h2> <h2>Request Info</h2>
<p>%s</p> <p>%s</p>
<button onclick="window.location='%s/errorlogs/';">View Log(Errors)</button>
</body> </body>
</html>""" % (error, error, </html>""" % (error, error, trace_info, request_info, sickbeard.WEB_ROOT))
trace_info, request_info))
def redirect(self, url, permanent=False, status=None): def redirect(self, url, permanent=False, status=None):
if not url.startswith(sickbeard.WEB_ROOT): if not url.startswith(sickbeard.WEB_ROOT):
@ -200,9 +203,8 @@ class BaseHandler(RequestHandler):
class WebHandler(BaseHandler): class WebHandler(BaseHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(WebHandler, self).__init__(*args, **kwargs) super(WebHandler, self).__init__(*args, **kwargs)
self.io_loop = IOLoop.current()
io_loop = IOLoop.current() self.executor = ThreadPoolExecutor(50)
executor = ThreadPoolExecutor(50)
@coroutine @coroutine
@asynchronous @asynchronous
@ -3437,7 +3439,8 @@ class ConfigGeneral(Config):
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None,
calendar_unprotected=None, calendar_unprotected=None,
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
indexer_timeout=None, play_videos=None, rootDir=None, theme_name=None): indexer_timeout=None, play_videos=None, rootDir=None, theme_name=None,
git_reset=None, git_username=None, git_password=None):
results = [] results = []
@ -3459,6 +3462,9 @@ class ConfigGeneral(Config):
sickbeard.ANON_REDIRECT = anon_redirect sickbeard.ANON_REDIRECT = anon_redirect
sickbeard.PROXY_SETTING = proxy_setting sickbeard.PROXY_SETTING = proxy_setting
sickbeard.PROXY_INDEXERS = config.checkbox_to_value(proxy_indexers) sickbeard.PROXY_INDEXERS = config.checkbox_to_value(proxy_indexers)
sickbeard.GIT_USERNAME = git_username
sickbeard.GIT_PASSWORD = git_password
sickbeard.GIT_RESET = config.checkbox_to_value(git_reset)
sickbeard.GIT_PATH = git_path sickbeard.GIT_PATH = git_path
sickbeard.GIT_REMOTE = git_remote sickbeard.GIT_REMOTE = git_remote
sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected) sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected)
@ -4637,7 +4643,7 @@ class ErrorLogs(WebRoot):
def ErrorLogsMenu(self): def ErrorLogsMenu(self):
menu = [ menu = [
{'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'}, {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'},
# { 'title': 'View Log', 'path': 'errorlogs/viewlog' }, {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveGitHub},
] ]
return menu return menu
@ -4649,12 +4655,14 @@ class ErrorLogs(WebRoot):
return t.respond() return t.respond()
def haveGitHub(self):
if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD:
return True
def clearerrors(self): def clearerrors(self):
classes.ErrorViewer.clear() classes.ErrorViewer.clear()
return self.redirect("/errorlogs/") return self.redirect("/errorlogs/")
def viewlog(self, minLevel=logger.INFO, maxLines=500): def viewlog(self, minLevel=logger.INFO, maxLines=500):
t = PageTemplate(rh=self, file="viewlogs.tmpl") t = PageTemplate(rh=self, file="viewlogs.tmpl")
@ -4706,4 +4714,39 @@ class ErrorLogs(WebRoot):
t.logLines = result t.logLines = result
t.minLevel = minLevel t.minLevel = minLevel
return t.respond() return t.respond()
def submit_errors(self):
title = "[APP SUBMITTED]: "
gh_org = sickbeard.GIT_ORG or 'SiCKRAGETV'
gh_repo = 'sickrage-issues'
self.gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD,
user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo)
try:
for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]:
title = title + curError.title
message = u"### INFO\n"
message += u"Python Version: **" + sys.version[:120] + "**\n"
message += u"Operating System: **" + platform.platform() + "**\n"
message += u"Branch: **" + sickbeard.BRANCH + "**\n"
message += u"Commit: SiCKRAGETV/SickRage@" + sickbeard.CUR_COMMIT_HASH + "\n"
message += u"### ERROR\n"
message += u"```\n"
message += curError.message
message += u"```\n"
message += u"---\n"
message += u"_STAFF NOTIFIED_: @SiCKRAGETV/owners @SiCKRAGETV/moderators"
issue = self.gh_issues.create_issue(title, message)
if issue:
ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number)
classes.ErrorViewer.clear()
except Exception as e:
logger.log(ex(e), logger.ERROR)
return self.redirect("/errorlogs/")

View File

@ -7,7 +7,7 @@ from sickbeard.webserve import LoginHandler, LogoutHandler, KeyHandler
from sickbeard.webapi import ApiHandler from sickbeard.webapi import ApiHandler
from sickbeard import logger from sickbeard import logger
from sickbeard.helpers import create_https_certificates, generateApiKey from sickbeard.helpers import create_https_certificates, generateApiKey
from tornado.web import Application, StaticFileHandler, HTTPError, RedirectHandler from tornado.web import Application, StaticFileHandler, RedirectHandler
from tornado.httpserver import HTTPServer from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.routes import route from tornado.routes import route