diff --git a/gui/slick/interfaces/default/config.tmpl b/gui/slick/interfaces/default/config.tmpl index 9f3897c5..99265456 100644 --- a/gui/slick/interfaces/default/config.tmpl +++ b/gui/slick/interfaces/default/config.tmpl @@ -37,7 +37,6 @@ #else You don't have version checking turned on. Please turn on "Check for Update" in Config > General.
#end if - You are using BETA software SR Config file:$sickbeard.CONFIG_FILE SR Database file:$db.dbFilename() diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index 7c3cd17e..1fe55e8f 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -438,48 +438,14 @@
+
+

Advanced Settings

-
-
- -
- -
- -
- -
- -
-
+
+ +
+ +
+

GitHub

+

Options for github related features.

+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+

diff --git a/gui/slick/interfaces/default/editShow.tmpl b/gui/slick/interfaces/default/editShow.tmpl index f7f7fbc6..c031ec13 100644 --- a/gui/slick/interfaces/default/editShow.tmpl +++ b/gui/slick/interfaces/default/editShow.tmpl @@ -106,7 +106,7 @@ (this will set a default status to be applied to any newly added episodes)


diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl index 93a9057b..857874a3 100644 --- a/gui/slick/interfaces/default/inc_top.tmpl +++ b/gui/slick/interfaces/default/inc_top.tmpl @@ -89,6 +89,7 @@ \$("#SubMenu a:contains('Clear History')").addClass('btn clearhistory').html(' Clear History'); \$("#SubMenu a:contains('Trim History')").addClass('btn trimhistory').html(' Trim History'); \$("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html(' Clear Errors'); + \$("#SubMenu a[href$='/errorlogs/submit_errors/']").addClass('btn').html(' Submit Errors'); \$("#SubMenu a:contains('Re-scan')").addClass('btn').html(' Re-scan'); \$("#SubMenu a:contains('Backlog Overview')").addClass('btn').html(' Backlog Overview'); \$("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html(' Update PLEX'); diff --git a/readme.md b/readme.md index 69bff7c3..9c72cad1 100644 --- a/readme.md +++ b/readme.md @@ -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. ## 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 - XBMC library updates, poster/fanart downloads, and NFO/TBN generation diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 25406c00..95dfbe8c 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -115,6 +115,8 @@ GIT_REMOTE_URL = '' CUR_COMMIT_BRANCH = '' GIT_ORG = 'SiCKRAGETV' GIT_REPO = 'SickRage' +GIT_USERNAME = None +GIT_PASSWORD = None GIT_PATH = None 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, \ AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ 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__: return False @@ -558,14 +560,20 @@ def initialize(consoleLogging=True): fileLogging = True if not helpers.makeDir(LOG_DIR): sys.stderr.write("!!! No log folder, logging to screen only!\n") - fileLogging=False + fileLogging = False # init logging 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 - try:gh = Github().get_organization(GIT_ORG).get_repo(GIT_REPO) - except:gh = None + try: + gh = Github(user_agent="SiCKRAGE").get_organization(GIT_ORG).get_repo(GIT_REPO) + except: + gh = None # git reset on update GIT_RESET = bool(check_setting_int(CFG, 'General', 'git_reset', 0)) @@ -1131,7 +1139,7 @@ def initialize(consoleLogging=True): (METADATA_WDTV, metadata.wdtv), (METADATA_TIVO, metadata.tivo), (METADATA_MEDE8ER, metadata.mede8er), - ]: + ]: (cur_metadata_config, cur_metadata_class) = cur_metadata_tuple tmp_provider = cur_metadata_class.metadata_class() 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() 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']['branch'] = BRANCH new_config['General']['git_remote'] = GIT_REMOTE diff --git a/sickbeard/bug_tracker.py b/sickbeard/bug_tracker.py deleted file mode 100644 index 0c4f303e..00000000 --- a/sickbeard/bug_tracker.py +++ /dev/null @@ -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)]) \ No newline at end of file diff --git a/sickbeard/classes.py b/sickbeard/classes.py index 7036b0ee..6b70edce 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see . import re +import sys import sickbeard @@ -261,6 +262,9 @@ class ErrorViewer(): def clear(): ErrorViewer.errors = [] + @staticmethod + def get(): + return ErrorViewer.errors class UIError(): """ @@ -268,5 +272,7 @@ class UIError(): """ def __init__(self, message): + self.title = sys.exc_info()[1].message or None self.message = message self.time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.exc_info = sys.exc_info() or None diff --git a/sickbeard/common.py b/sickbeard/common.py index 24a31cc8..bc8e9cd7 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -236,8 +236,6 @@ class Quality: def assumeQuality(name): if name.lower().endswith((".avi", ".mp4")): return Quality.SDTV - # elif name.lower().endswith(".mkv"): - # return Quality.HDTV elif name.lower().endswith(".ts"): return Quality.RAWHDTV else: diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index 66ba94bd..7de47c45 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException MIN_DB_VERSION = 9 # oldest db version we support migrating from -MAX_DB_VERSION = 41 +MAX_DB_VERSION = 42 class MainSanityCheck(db.DBSanityCheck): def check(self): @@ -37,6 +37,7 @@ class MainSanityCheck(db.DBSanityCheck): self.fix_orphan_episodes() self.fix_unaired_episodes() self.fix_tvrage_show_statues() + self.fix_episode_statuses() 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')"): 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')"): 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')"): 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')"): 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')"): 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): @@ -163,6 +164,19 @@ class MainSanityCheck(db.DBSanityCheck): for old_status, new_status in status_map.items(): 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): logger.log(u"Backing up database before upgrade") 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 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 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 UNIQUE INDEX idx_indexer_id ON tv_shows (indexer_id)", "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( "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) # 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("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)") - 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("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("DROP TABLE tmp_tv_shows") self.connection.action("CREATE UNIQUE INDEX idx_indexer_id ON tv_shows (indexer_id);") @@ -647,7 +659,7 @@ class ConvertTVEpisodesToIndexerScheme(ConvertTVShowsToIndexerScheme): 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)") 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("CREATE INDEX idx_tv_episodes_showid_airdate ON tv_episodes(showid,airdate);") @@ -678,7 +690,7 @@ class ConvertIMDBInfoToIndexerScheme(ConvertTVEpisodesToIndexerScheme): 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)") 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.incDBVersion() @@ -701,7 +713,7 @@ class ConvertInfoToIndexerScheme(ConvertIMDBInfoToIndexerScheme): self.connection.action( "CREATE TABLE info (last_backlog NUMERIC, last_indexer NUMERIC, last_proper_search NUMERIC)") 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.incDBVersion() @@ -944,6 +956,21 @@ class AddDefaultEpStatusToTvShows(AddVersionToTvEpisodes): backupDatabase(41) 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() diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 0efde9bb..a149b99a 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -240,7 +240,7 @@ class TVShow(object): # if we get an anime get the real season and episode if self.is_anime and absolute_number and not season and not episode: 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]) if len(sqlResults) == 1: @@ -282,7 +282,7 @@ class TVShow(object): def should_update(self, update_date=datetime.date.today()): # 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 # 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: curEp = self.getEpisode(curSeason, curEpisode) 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 deleteEp: @@ -957,7 +957,7 @@ class TVShow(object): if not self.nextaired or self.nextaired and curDate > self.nextaired: myDB = db.DBConnection() 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]) if sqlResults == None or len(sqlResults) == 0: @@ -1526,7 +1526,7 @@ class TVEpisode(object): self.subtitles_lastsearch = sqlResults[0]["subtitles_lastsearch"] 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) - self.status = int(sqlResults[0]["status"]) + self.status = int(sqlResults[0]["status"] or -1) # don't overwrite my location if sqlResults[0]["location"] and sqlResults[0]["location"]: @@ -1677,15 +1677,15 @@ class TVEpisode(object): self.description = getattr(myEp, 'overview', "") 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)) rawAirdate = [int(x) for x in firstaired.split("-")] try: self.airdate = datetime.date(rawAirdate[0], rawAirdate[1], rawAirdate[2]) except (ValueError, IndexError): - logger.log(u"Malformed air date retrieved from " + sickbeard.indexerApi( - self.indexer).name + " (" + self.show.name + " - " + str(season) + "x" + str(episode) + ")", + logger.log(u"Malformed air date of " + str(firstaired) + " retrieved from " + sickbeard.indexerApi( + self.indexer).name + " for (" + self.show.name + " - " + str(season) + "x" + str(episode) + ")", logger.ERROR) # if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now if self.indexerid != -1: diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 2f991009..12916106 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -24,8 +24,11 @@ import time import urllib import re import datetime +import sys +import platform import sickbeard +from github import Github from sickbeard import config, sab from sickbeard import clients from sickbeard import history, notifiers, processTV @@ -180,9 +183,9 @@ class BaseHandler(RequestHandler):

%s

Request Info

%s

+ - """ % (error, error, - trace_info, request_info)) + """ % (error, error, trace_info, request_info, sickbeard.WEB_ROOT)) def redirect(self, url, permanent=False, status=None): if not url.startswith(sickbeard.WEB_ROOT): @@ -200,9 +203,8 @@ class BaseHandler(RequestHandler): class WebHandler(BaseHandler): def __init__(self, *args, **kwargs): super(WebHandler, self).__init__(*args, **kwargs) - - io_loop = IOLoop.current() - executor = ThreadPoolExecutor(50) + self.io_loop = IOLoop.current() + self.executor = ThreadPoolExecutor(50) @coroutine @asynchronous @@ -3437,7 +3439,8 @@ class ConfigGeneral(Config): proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=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 = [] @@ -3459,6 +3462,9 @@ class ConfigGeneral(Config): sickbeard.ANON_REDIRECT = anon_redirect sickbeard.PROXY_SETTING = proxy_setting 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_REMOTE = git_remote sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected) @@ -4637,7 +4643,7 @@ class ErrorLogs(WebRoot): def ErrorLogsMenu(self): menu = [ {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'}, - # { 'title': 'View Log', 'path': 'errorlogs/viewlog' }, + {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveGitHub}, ] return menu @@ -4649,12 +4655,14 @@ class ErrorLogs(WebRoot): return t.respond() + def haveGitHub(self): + if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD: + return True def clearerrors(self): classes.ErrorViewer.clear() return self.redirect("/errorlogs/") - def viewlog(self, minLevel=logger.INFO, maxLines=500): t = PageTemplate(rh=self, file="viewlogs.tmpl") @@ -4706,4 +4714,39 @@ class ErrorLogs(WebRoot): t.logLines = result t.minLevel = minLevel - return t.respond() \ No newline at end of file + 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/") \ No newline at end of file diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py index 8d154dcf..dfc582fb 100644 --- a/sickbeard/webserveInit.py +++ b/sickbeard/webserveInit.py @@ -7,7 +7,7 @@ from sickbeard.webserve import LoginHandler, LogoutHandler, KeyHandler from sickbeard.webapi import ApiHandler from sickbeard import logger 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.ioloop import IOLoop from tornado.routes import route