diff --git a/Dockerfile b/Dockerfile index 221fe2fc9..1f21c71aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,12 @@ -FROM lsiobase/alpine.python +FROM debian:buster EXPOSE 6767 VOLUME /tv # Update -RUN apk add --update build-base python-dev py2-pip py-setuptools jpeg-dev zlib-dev git libgit2-dev +RUN apt-get update +RUN apt-get install -y build-essential python-dev python-pip python-setuptools libjpeg-dev zlib1g-dev git libgit2-dev libffi-dev # Get application source from Github RUN git clone -b master --single-branch https://github.com/morpheus65535/bazarr.git /bazarr diff --git a/README.md b/README.md index 074bc507d..105f83d24 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,9 @@ Linux: * Open your browser and go to `http://localhost:6767/` ## Docker: -* You can use [this image](https://hub.docker.com/r/morpheus65535/bazarr) to quickly build your own isolated app container. Thanks to [Linux Server](https://github.com/linuxserver) for the base image. It's based on the Linux instructions above. For more info about Docker check out the [official website](https://www.docker.com). +* You can use [this image](https://hub.docker.com/r/morpheus65535/bazarr) to quickly build your own isolated app container. It's based on the Linux instructions above. For more info about Docker check out the [official website](https://www.docker.com). + +docker create --name=bazarr -v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro -v /path/to/series/directory:/tv -v /path/to/config/directory/on/host:/bazarr/data -p 6767:6767 --restart=always morpheus65535/bazarr:latest ## First run (important to read!!!): @@ -77,12 +79,16 @@ Linux: * Configure Sonarr ip, port, base url, SSL and API key. ### 4 - In "Subliminal" tab: * Configure enabled providers and enabled languages. Enabled languages are those that you are going to be able to assign to a series later. -### 5 - Save those settings and restart Bazarr. +### 5 - Save those settings and restart (important!!!) Bazarr. + +### 6 - Wait 2 minutes + +### 7 - On the "Series" page, you should now see all your series listed with a wrench icon on yellow background. Those are the series that need to be configured. Click on those one you want to get subtitles for and select desired languages. You don't have to do this for every series but it will looks cleaner without all this yellow ;-). If you don't want any substitles for a series, just click on the wrench icon and then on "Save" without selecting anything. + +### 8 - On each series page, you should see episode files available on disk, existing subtitles and missing subtitles (in case you requested some and they aren't already existing). +* If you don't see your episodes right now, wait some more time. It take time to do the initial synchronization between Sonarr and Bazarr. -### 6 - On the "Series" page, click on "Update Series" -* You should now see all your series listed with a wrench icon on yellow background. Those are the series that need to be configured. Click on each one and select desired languages. You have to do this even if you don't want subtitles for a series. Just click on the wrench icon and then on "Save". -* When you've finished going trough all those series to configure desired languages, you have to "Update All Episodes" from the "Series" page. Don't be impatient, it will take about 1 minute by 1000 episodes Bazarr need to scan for existing internal and external subtitles. If Bazarr is accessing those episodes trough a network share, it's going to take much more longer than that. Keep in mind that Bazarr have to open each and every episode files to analyze the content. -* Once the scan is finished, you should be able to access episodes list for each series and see those missing subtitles on the wanted page. +### 9 - On "Wanted" page, you should see all the episodes who have missing subtitles. ### 10 - Have fun and keep in mind that providers may temporary refuse connection due to connection limit exceeded or problem on the provider web service. diff --git a/bazarr.py b/bazarr.py index 8a4e4fce9..55d549548 100644 --- a/bazarr.py +++ b/bazarr.py @@ -1,7 +1,9 @@ +bazarr_version = '0.1.1' + from bottle import route, run, template, static_file, request, redirect import bottle -bottle.debug(True) -bottle.TEMPLATES.clear() +#bottle.debug(True) +#bottle.TEMPLATES.clear() import os bottle.TEMPLATE_PATH.insert(0,os.path.join(os.path.dirname(__file__), 'views/')) @@ -34,6 +36,7 @@ from scheduler import * import logging from logging.handlers import TimedRotatingFileHandler + logger = logging.getLogger('waitress') db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) c = db.cursor() @@ -41,7 +44,7 @@ c.execute("SELECT log_level FROM table_settings_general") log_level = c.fetchone() log_level = log_level[0] if log_level is None: - log_level = "WARNING" + log_level = "INFO" log_level = getattr(logging, log_level) c.close() @@ -64,6 +67,9 @@ def configure_logging(): f = OneLineExceptionFormatter('%(asctime)s|%(levelname)s|%(message)s|', '%d/%m/%Y %H:%M:%S') fh.setFormatter(f) + logging.getLogger("enzyme").setLevel(logging.ERROR) + logging.getLogger("apscheduler").setLevel(logging.WARNING) + logging.getLogger("subliminal").setLevel(logging.ERROR) root = logging.getLogger() root.setLevel(log_level) root.addHandler(fh) @@ -129,30 +135,6 @@ def edit_series(no): redirect(ref) -@route(base_url + 'update_series') -def update_series_list(): - ref = request.environ['HTTP_REFERER'] - - update_series() - - redirect(ref) - -@route(base_url + 'update_all_episodes') -def update_all_episodes_list(): - ref = request.environ['HTTP_REFERER'] - - update_all_episodes() - - redirect(ref) - -@route(base_url + 'add_new_episodes') -def add_new_episodes_list(): - ref = request.environ['HTTP_REFERER'] - - add_new_episodes() - - redirect(ref) - @route(base_url + 'episodes/', method='GET') def episodes(no): conn = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) @@ -160,7 +142,8 @@ def episodes(no): c = conn.cursor() series_details = [] - series_details = c.execute("SELECT title, overview, poster, fanart, hearing_impaired FROM table_shows WHERE sonarrSeriesId LIKE ?", (str(no),)).fetchone() + series_details = c.execute("SELECT title, overview, poster, fanart, hearing_impaired, tvdbid FROM table_shows WHERE sonarrSeriesId LIKE ?", (str(no),)).fetchone() + tvdbid = series_details[5] episodes = c.execute("SELECT title, path_substitution(path), season, episode, subtitles, sonarrSeriesId, missing_subtitles, sonarrEpisodeId FROM table_episodes WHERE sonarrSeriesId LIKE ? ORDER BY episode ASC", (str(no),)).fetchall() episodes = reversed(sorted(episodes, key=operator.itemgetter(2))) @@ -169,7 +152,7 @@ def episodes(no): seasons_list.append(list(season)) c.close() - return template('episodes', no=no, details=series_details, seasons=seasons_list, url_sonarr_short=url_sonarr_short, base_url=base_url) + return template('episodes', no=no, details=series_details, seasons=seasons_list, url_sonarr_short=url_sonarr_short, base_url=base_url, tvdbid=tvdbid) @route(base_url + 'scan_disk/', method='GET') def scan_disk(no): @@ -302,7 +285,8 @@ def save_settings(): def check_update(): ref = request.environ['HTTP_REFERER'] - check_and_apply_update() + result = check_and_apply_update() + logging.info(result) redirect(ref) @@ -318,11 +302,101 @@ def system(): for line in reversed(open(os.path.join(os.path.dirname(__file__), 'data/log/bazarr.log')).readlines()): logs.append(line.rstrip()) + def get_time_from_interval(interval): + interval_clean = interval.split('[') + interval_clean = interval_clean[1][:-1] + interval_split = interval_clean.split(':') + + hour = interval_split[0] + minute = interval_split[1].lstrip("0") + second = interval_split[2].lstrip("0") + + text = "every " + if hour != "0": + text = text + hour + if hour == "1": + text = text + " hour" + else: + text = text + " hours" + + if minute != "" and second != "": + text = text + ", " + elif minute == "" and second != "": + text = text + " and " + elif minute != "" and second == "": + text = text + " and " + if minute != "": + text = text + minute + if minute == "1": + text = text + " minute" + else: + text = text + " minutes" + + if second != "": + text = text + " and " + if second != "": + text = text + second + if second == "1": + text = text + " second" + else: + text = text + " seconds" + + return text + + def get_time_from_cron(cron): + text = "at " + hour = str(cron[5]) + minute = str(cron[6]) + second = str(cron[7]) + + if hour != "0" and hour != "*": + text = text + hour + if hour == "0" or hour == "1": + text = text + " hour" + else: + text = text + " hours" + + if minute != "*" and second != "0": + text = text + ", " + elif minute == "*" and second != "0": + text = text + " and " + elif minute != "0" and minute != "*" and second == "0": + text = text + " and " + if minute != "0" and minute != "*": + text = text + minute + if minute == "0" or minute == "1": + text = text + " minute" + else: + text = text + " minutes" + + if second != "0" and second != "*": + text = text + " and " + if second != "0" and second != "*": + text = text + second + if second == "0" or second == "1": + text = text + " second" + else: + text = text + " seconds" + + return text + + task_list = [] for job in scheduler.get_jobs(): - task_list.append([job.name, job.trigger.interval.__str__(), pretty.date(job.next_run_time.replace(tzinfo=None))]) + if job.trigger.__str__().startswith('interval'): + task_list.append([job.name, get_time_from_interval(str(job.trigger)), pretty.date(job.next_run_time.replace(tzinfo=None)), job.id]) + elif job.trigger.__str__().startswith('cron'): + task_list.append([job.name, get_time_from_cron(job.trigger.fields), pretty.date(job.next_run_time.replace(tzinfo=None)), job.id]) - return template('system', tasks=tasks, logs=logs, base_url=base_url, task_list=task_list) + return template('system', tasks=tasks, logs=logs, base_url=base_url, task_list=task_list, bazarr_version=bazarr_version) + +@route(base_url + 'execute/') +def execute_task(taskid): + ref = request.environ['HTTP_REFERER'] + + execute_now(taskid) + + redirect(ref) @route(base_url + 'remove_subtitles', method='POST') def remove_subtitles(): @@ -331,6 +405,7 @@ def remove_subtitles(): subtitlesPath = request.forms.get('subtitlesPath') sonarrSeriesId = request.forms.get('sonarrSeriesId') sonarrEpisodeId = request.forms.get('sonarrEpisodeId') + tvdbid = request.forms.get('tvdbid') try: os.remove(subtitlesPath) @@ -339,7 +414,7 @@ def remove_subtitles(): except OSError: pass store_subtitles(episodePath) - list_missing_subtitles(sonarrSeriesId) + list_missing_subtitles(tvdbid) @route(base_url + 'get_subtitle', method='POST') def get_subtitle(): @@ -350,6 +425,7 @@ def get_subtitle(): hi = request.forms.get('hi') sonarrSeriesId = request.forms.get('sonarrSeriesId') sonarrEpisodeId = request.forms.get('sonarrEpisodeId') + tvdbid = request.forms.get('tvdbid') db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) c = db.cursor() @@ -366,9 +442,9 @@ def get_subtitle(): if result is not None: history_log(1, sonarrSeriesId, sonarrEpisodeId, result) store_subtitles(episodePath) - list_missing_subtitles(sonarrSeriesId) + list_missing_subtitles(tvdbid) redirect(ref) except OSError: - redirect(ref + '?error=2') + pass run(host=ip, port=port, server='waitress') diff --git a/check_update.py b/check_update.py index 1b39ec551..f4ecc2e33 100644 --- a/check_update.py +++ b/check_update.py @@ -3,7 +3,7 @@ from get_general_settings import * import os import pygit2 -current_working_directory = os.getcwd() +current_working_directory = os.path.dirname(__file__) repository_path = pygit2.discover_repository(current_working_directory) local_repo = pygit2.Repository(repository_path) @@ -15,13 +15,17 @@ def check_and_apply_update(repo=local_repo, remote_name='origin'): merge_result, _ = repo.merge_analysis(remote_id) # Up to date, do nothing if merge_result & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE: - print 'Up to date' - return + result = 'No new version of Bazarr available.' + pass # We can just fastforward elif merge_result & pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD: repo.checkout_tree(repo.get(remote_id)) - master_ref = repo.lookup_reference('refs/heads/master') + master_ref = repo.lookup_reference('refs/heads/' + branch) master_ref.set_target(remote_id) repo.head.set_target(remote_id) + result = 'Bazarr updated to latest version and restarting.' + os.execlp('python', 'python', os.path.join(os.path.dirname(__file__), 'bazarr.py')) else: raise AssertionError('Unknown merge analysis result') + + return result diff --git a/create_db.sql b/create_db.sql index 57ece98ea..a29b2fdf1 100644 --- a/create_db.sql +++ b/create_db.sql @@ -40,7 +40,7 @@ CREATE TABLE "table_settings_general" ( `branch` TEXT, `auto_update` INTEGER ); -INSERT INTO `table_settings_general` (ip,port,base_url,path_mapping,log_level, branch, auto_update) VALUES ('0.0.0.0',6767,'/',Null,'WARNING','master','True'); +INSERT INTO `table_settings_general` (ip,port,base_url,path_mapping,log_level, branch, auto_update) VALUES ('0.0.0.0',6767,'/',Null,'INFO','master','True'); CREATE TABLE `table_scheduler` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, `name` TEXT NOT NULL, diff --git a/get_episodes.py b/get_episodes.py index bcdcbb006..f586d2e18 100644 --- a/get_episodes.py +++ b/get_episodes.py @@ -21,6 +21,7 @@ def update_all_episodes(): base_url_sonarr = "" else: base_url_sonarr = "/" + config_sonarr[2].strip("/") + apikey_sonarr = config_sonarr[4] # Get current episodes id in DB current_episodes_db = c.execute('SELECT sonarrEpisodeId FROM table_episodes').fetchall() @@ -32,7 +33,7 @@ def update_all_episodes(): seriesIdList = c.fetchall() for seriesId in seriesIdList: # Get episodes data for a series from Sonarr - url_sonarr_api_episode = protocol_sonarr + "://" + config_sonarr[0] + ":" + str(config_sonarr[1]) + base_url_sonarr + "/api/episode?seriesId=" + str(seriesId[0]) + "&apikey=" + config_sonarr[4] + url_sonarr_api_episode = protocol_sonarr + "://" + config_sonarr[0] + ":" + str(config_sonarr[1]) + base_url_sonarr + "/api/episode?seriesId=" + str(seriesId[0]) + "&apikey=" + apikey_sonarr r = requests.get(url_sonarr_api_episode) for episode in r.json(): if episode['hasFile']: @@ -59,7 +60,16 @@ def update_all_episodes(): # Close database connection c.close() - + + #Cleanup variables to free memory + del current_episodes_db + del current_episodes_db_list + del seriesIdList + del r + del current_episodes_sonarr + del deleted_items + del c + # Store substitles for all episodes full_scan_subtitles() list_missing_subtitles() @@ -80,44 +90,61 @@ def add_new_episodes(): base_url_sonarr = "" else: base_url_sonarr = "/" + config_sonarr[2].strip("/") + apikey_sonarr = config_sonarr[4] - # Get current episodes in DB - current_episodes_db = c.execute('SELECT sonarrEpisodeId FROM table_episodes').fetchall() - current_episodes_db_list = [x[0] for x in current_episodes_db] - current_episodes_sonarr = [] - - # Get sonarrId for each series from database - c.execute("SELECT sonarrSeriesId FROM table_shows") - seriesIdList = c.fetchall() - for seriesId in seriesIdList: - # Get episodes data for a series from Sonarr - url_sonarr_api_episode = protocol_sonarr + "://" + config_sonarr[0] + ":" + str(config_sonarr[1]) + base_url_sonarr + "/api/episode?seriesId=" + str(seriesId[0]) + "&apikey=" + config_sonarr[4] - r = requests.get(url_sonarr_api_episode) - - for episode in r.json(): - if episode['hasFile']: - # Add shows in Sonarr to current shows list - current_episodes_sonarr.append(episode['id']) - - try: - c.execute('''INSERT INTO table_episodes(sonarrSeriesId, sonarrEpisodeId, title, path, season, episode) VALUES (?, ?, ?, ?, ?, ?)''', (episode['seriesId'], episode['id'], episode['title'], episode['episodeFile']['path'], episode['seasonNumber'], episode['episodeNumber'])) - except: - pass + if apikey_sonarr == None: + # Close database connection + c.close() + pass + else: + # Get current episodes in DB + current_episodes_db = c.execute('SELECT sonarrEpisodeId FROM table_episodes').fetchall() + current_episodes_db_list = [x[0] for x in current_episodes_db] + current_episodes_sonarr = [] + + # Get sonarrId for each series from database + c.execute("SELECT sonarrSeriesId FROM table_shows") + seriesIdList = c.fetchall() + for seriesId in seriesIdList: + # Get episodes data for a series from Sonarr + url_sonarr_api_episode = protocol_sonarr + "://" + config_sonarr[0] + ":" + str(config_sonarr[1]) + base_url_sonarr + "/api/episode?seriesId=" + str(seriesId[0]) + "&apikey=" + apikey_sonarr + r = requests.get(url_sonarr_api_episode) + for episode in r.json(): + if episode['hasFile']: + # Add shows in Sonarr to current shows list + current_episodes_sonarr.append(episode['id']) + + try: + c.execute('''INSERT INTO table_episodes(sonarrSeriesId, sonarrEpisodeId, title, path, season, episode) VALUES (?, ?, ?, ?, ?, ?)''', (episode['seriesId'], episode['id'], episode['title'], episode['episodeFile']['path'], episode['seasonNumber'], episode['episodeNumber'])) + except: + pass + db.commit() + + # Delete episodes not in Sonarr anymore + deleted_items = [] + for item in current_episodes_db_list: + if item not in current_episodes_sonarr: + deleted_items.append(tuple([item])) + c.executemany('DELETE FROM table_episodes WHERE sonarrEpisodeId = ?',deleted_items) + + # Commit changes to database table db.commit() - # Delete episodes not in Sonarr anymore - deleted_items = [] - for item in current_episodes_db_list: - if item not in current_episodes_sonarr: - deleted_items.append(tuple([item])) - c.executemany('DELETE FROM table_episodes WHERE sonarrEpisodeId = ?',deleted_items) + # Close database connection + c.close() + + #Cleanup variables to free memory + del current_episodes_db + del current_episodes_db_list + del seriesIdList + del r + del current_episodes_sonarr + del deleted_items + del c - # Commit changes to database table - db.commit() - - # Close database connection - c.close() - - # Store substitles from episodes we've just added - new_scan_subtitles() - list_missing_subtitles() + # Store substitles from episodes we've just added + new_scan_subtitles() + try: + list_missing_subtitles() + except: + pass diff --git a/get_series.py b/get_series.py index dd8875f03..07c52f924 100644 --- a/get_series.py +++ b/get_series.py @@ -9,49 +9,52 @@ def update_series(): db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) c = db.cursor() - # Get shows data from Sonarr - url_sonarr_api_series = url_sonarr + "/api/series?apikey=" + apikey_sonarr - r = requests.get(url_sonarr_api_series) - shows_list = [] - - # Get current shows in DB - current_shows_db = c.execute('SELECT tvdbId FROM table_shows').fetchall() - current_shows_db_list = [x[0] for x in current_shows_db] - current_shows_sonarr = [] - - # Parsing data returned from Sonarr - for show in r.json(): - try: - overview = unicode(show['overview']) - except: - overview = "" - try: - poster_big = show['images'][2]['url'].split('?')[0] - poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1] - except: - poster = "" - try: - fanart = show['images'][0]['url'].split('?')[0] - except: - fanart = "" - - # Add shows in Sonarr to current shows list - current_shows_sonarr.append(show['tvdbId']) - - # Update or insert shows list in database table - result = c.execute('''UPDATE table_shows SET title = ?, path = ?, tvdbId = ?, sonarrSeriesId = ?, overview = ?, poster = ?, fanart = ? WHERE tvdbid = ?''', (show["title"],show["path"],show["tvdbId"],show["id"],overview,poster,fanart,show["tvdbId"])) - if result.rowcount == 0: - c.execute('''INSERT INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart) VALUES (?,?,?,(SELECT languages FROM table_shows WHERE tvdbId = ?),(SELECT `hearing_impaired` FROM table_shows WHERE tvdbId = ?), ?, ?, ?, ?)''', (show["title"],show["path"],show["tvdbId"],show["tvdbId"],show["tvdbId"],show["id"],overview,poster,fanart)) - - # Delete shows not in Sonarr anymore - deleted_items = [] - for item in current_shows_db_list: - if item not in current_shows_sonarr: - deleted_items.append(tuple([item])) - c.executemany('DELETE FROM table_shows WHERE tvdbId = ?',deleted_items) - - # Commit changes to database table - db.commit() + if apikey_sonarr == None: + pass + else: + # Get shows data from Sonarr + url_sonarr_api_series = url_sonarr + "/api/series?apikey=" + apikey_sonarr + r = requests.get(url_sonarr_api_series) + shows_list = [] + + # Get current shows in DB + current_shows_db = c.execute('SELECT tvdbId FROM table_shows').fetchall() + current_shows_db_list = [x[0] for x in current_shows_db] + current_shows_sonarr = [] + + # Parsing data returned from Sonarr + for show in r.json(): + try: + overview = unicode(show['overview']) + except: + overview = "" + try: + poster_big = show['images'][2]['url'].split('?')[0] + poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1] + except: + poster = "" + try: + fanart = show['images'][0]['url'].split('?')[0] + except: + fanart = "" + + # Add shows in Sonarr to current shows list + current_shows_sonarr.append(show['tvdbId']) + + # Update or insert shows list in database table + result = c.execute('''UPDATE table_shows SET title = ?, path = ?, tvdbId = ?, sonarrSeriesId = ?, overview = ?, poster = ?, fanart = ? WHERE tvdbid = ?''', (show["title"],show["path"],show["tvdbId"],show["id"],overview,poster,fanart,show["tvdbId"])) + if result.rowcount == 0: + c.execute('''INSERT INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart) VALUES (?,?,?,(SELECT languages FROM table_shows WHERE tvdbId = ?),(SELECT `hearing_impaired` FROM table_shows WHERE tvdbId = ?), ?, ?, ?, ?)''', (show["title"],show["path"],show["tvdbId"],show["tvdbId"],show["tvdbId"],show["id"],overview,poster,fanart)) + + # Delete shows not in Sonarr anymore + deleted_items = [] + for item in current_shows_db_list: + if item not in current_shows_sonarr: + deleted_items.append(tuple([item])) + c.executemany('DELETE FROM table_shows WHERE tvdbId = ?',deleted_items) + + # Commit changes to database table + db.commit() # Close database connection db.close() diff --git a/get_subtitle.py b/get_subtitle.py index 18e2c7baf..605a7c60a 100644 --- a/get_subtitle.py +++ b/get_subtitle.py @@ -21,11 +21,21 @@ def download_subtitle(path, language, hi, providers): downloaded_provider = str(result[0]).strip('<>').split(' ')[0][:-8] downloaded_language = pycountry.languages.lookup(str(str(result[0]).strip('<>').split(' ')[2].strip('[]'))).name message = downloaded_language + " subtitles downloaded from " + downloaded_provider + "." - + return message except: return None + del video + del best_subtitles + try: + del result + del downloaded_provider + del downloaded_language + del message + except: + pass + def series_download_subtitles(no): conn_db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) c_db = conn_db.cursor() @@ -65,6 +75,16 @@ def wanted_download_subtitles(path): list_missing_subtitles(episode[3]) history_log(1, episode[3], episode[2], message) + del conn_db + del c_db + del episodes_details + del enabled_providers + del providers_list + try: + del message + except: + pass + def wanted_search_missing_subtitles(): db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) db.create_function("path_substitution", 1, path_replace) @@ -76,3 +96,7 @@ def wanted_search_missing_subtitles(): for episode in data: wanted_download_subtitles(episode[0]) + + del db + del c + del data diff --git a/list_subtitles.py b/list_subtitles.py index 9c24b5353..a5cd58ada 100644 --- a/list_subtitles.py +++ b/list_subtitles.py @@ -68,21 +68,22 @@ def store_subtitles(file): def list_missing_subtitles(*no): query_string = '' try: - query_string = " WHERE table_episodes.sonarrSeriesId = " + str(no[0]) + query_string = " WHERE table_shows.tvdbId = " + str(no[0]) except: pass conn_db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) c_db = conn_db.cursor() episodes_subtitles = c_db.execute("SELECT table_episodes.sonarrEpisodeId, table_episodes.subtitles, table_shows.languages FROM table_episodes INNER JOIN table_shows on table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId" + query_string).fetchall() - - missing_subtitles_global = [] + missing_subtitles_global = [] + for episode_subtitles in episodes_subtitles: actual_subtitles = [] desired_subtitles = [] missing_subtitles = [] if episode_subtitles[1] != None: actual_subtitles = ast.literal_eval(episode_subtitles[1]) + if episode_subtitles[2] != None: desired_subtitles = ast.literal_eval(episode_subtitles[2]) actual_subtitles_list = [] if desired_subtitles == None: @@ -91,9 +92,10 @@ def list_missing_subtitles(*no): for item in actual_subtitles: actual_subtitles_list.append(item[0]) missing_subtitles = list(set(desired_subtitles) - set(actual_subtitles_list)) - missing_subtitles_global.append(tuple([str(missing_subtitles), episode_subtitles[0]])) - + missing_subtitles_global.append(tuple([str(missing_subtitles), episode_subtitles[0]])) + c_db.executemany("UPDATE table_episodes SET missing_subtitles = ? WHERE sonarrEpisodeId = ?", (missing_subtitles_global)) + conn_db.commit() c_db.close() @@ -105,7 +107,7 @@ def full_scan_subtitles(): c_db.close() for episode in episodes: - store_subtitles(path_replace(episode[0])) + store_subtitles(path_replace(episode[0].encode('utf-8'))) def series_scan_subtitles(no): conn_db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) @@ -114,10 +116,10 @@ def series_scan_subtitles(no): c_db.close() for episode in episodes: - store_subtitles(path_replace(episode[0])) + store_subtitles(path_replace(episode[0].encode('utf-8'))) list_missing_subtitles(no) - + def new_scan_subtitles(): conn_db = sqlite3.connect(os.path.join(os.path.dirname(__file__), 'data/db/bazarr.db')) c_db = conn_db.cursor() @@ -125,8 +127,4 @@ def new_scan_subtitles(): c_db.close() for episode in episodes: - store_subtitles(path_replace(episode[0])) - -#full_scan_subtitles() -#new_scan_subtitles() -#list_missing_subtitles() + store_subtitles(path_replace(episode[0].encode('utf-8'))) diff --git a/scheduler.py b/scheduler.py index 8028fd7c3..a77e9efac 100644 --- a/scheduler.py +++ b/scheduler.py @@ -1,13 +1,22 @@ +from get_general_settings import * from get_series import * from get_episodes import * +from list_subtitles import * from get_subtitle import * from check_update import * from apscheduler.schedulers.background import BackgroundScheduler +from datetime import datetime scheduler = BackgroundScheduler() -scheduler.add_job(check_and_apply_update, 'interval', hours=6, max_instances=1, coalesce=True, id='update_bazarr', name='Update bazarr from source on Github') +if automatic == 'True': + scheduler.add_job(check_and_apply_update, 'interval', hours=6, max_instances=1, coalesce=True, id='update_bazarr', name='Update bazarr from source on Github') scheduler.add_job(update_series, 'interval', minutes=1, max_instances=1, coalesce=True, id='update_series', name='Update series list from Sonarr') scheduler.add_job(add_new_episodes, 'interval', minutes=1, max_instances=1, coalesce=True, id='add_new_episodes', name='Add new episodes from Sonarr') +scheduler.add_job(update_all_episodes, 'cron', hour=4, max_instances=1, coalesce=True, id='update_all_episodes', name='Update all episodes from Sonarr') +scheduler.add_job(list_missing_subtitles, 'interval', minutes=5, max_instances=1, coalesce=True, id='list_missing_subtitles', name='Process missing subtitles for all series') scheduler.add_job(wanted_search_missing_subtitles, 'interval', minutes=15, max_instances=1, coalesce=True, id='wanted_search_missing_subtitles', name='Search for wanted subtitles') scheduler.start() + +def execute_now(taskid): + scheduler.modify_job(taskid, jobstore=None, next_run_time=datetime.now()) diff --git a/views/episodes.tpl b/views/episodes.tpl index 4b391f3e4..fcc318a77 100644 --- a/views/episodes.tpl +++ b/views/episodes.tpl @@ -121,7 +121,7 @@ %if len(seasons) == 0:
-

No episode file available for this series.

+

No episode file available for this series or Bazarr is still synchronizing with Sonarr. Please come back later.

%else: %for season in seasons: @@ -155,36 +155,43 @@ %if episode[4] is not None: % actual_languages = ast.literal_eval(episode[4]) %else: - actual_languages = '[]' + % actual_languages = '[]' %end - %if actual_languages is not None: + + %try: %for language in actual_languages: - %if language[1] is not None: - - {{language[0]}} - - - %else: -
- {{language[0]}} -
- %end + %if language[1] is not None: + + {{language[0]}} + + + %else: +
+ {{language[0]}} +
+ %end %end + %except: + %pass %end - %if episode[6] is not None: - % missing_languages = ast.literal_eval(episode[6]) - %else: - % missing_languages = None - %end - %if missing_languages is not None: - %for language in missing_languages: - - {{language}} - - + %try: + %if episode[6] is not None: + % missing_languages = ast.literal_eval(episode[6]) + %else: + % missing_languages = None + %end + %if missing_languages is not None: + %for language in missing_languages: + + {{language}} + + + %end %end + %except: + %pass %end @@ -214,7 +221,8 @@ language: $(this).attr("data-language"), subtitlesPath: $(this).attr("data-subtitlesPath"), sonarrSeriesId: $(this).attr("data-sonarrSeriesId"), - sonarrEpisodeId: $(this).attr("data-sonarrEpisodeId") + sonarrEpisodeId: $(this).attr("data-sonarrEpisodeId"), + tvdbid: {{tvdbid}} }; $.ajax({ url: "{{base_url}}remove_subtitles", @@ -231,7 +239,8 @@ language: $(this).attr("data-language"), hi: $(this).attr("data-hi"), sonarrSeriesId: $(this).attr("data-sonarrSeriesId"), - sonarrEpisodeId: $(this).attr("data-sonarrEpisodeId") + sonarrEpisodeId: $(this).attr("data-sonarrEpisodeId"), + tvdbid: {{tvdbid}} }; $.ajax({ url: "{{base_url}}get_subtitle", diff --git a/views/series.tpl b/views/series.tpl index 8dbe41ea8..2b3d6fe7d 100644 --- a/views/series.tpl +++ b/views/series.tpl @@ -78,12 +78,6 @@
-
- - - -
- @@ -111,7 +105,7 @@ %end %end - + - + + @@ -93,6 +94,11 @@ + %end @@ -135,7 +141,7 @@ icon">
- About + Bazarr version: {{bazarr_version}}
@@ -173,6 +179,10 @@ icon"> .tab() ; + $('.execute').click(function(){ + window.location = '{{base_url}}execute/' + $(this).data("taskid"); + }) + $('.log').click(function(){ $("#message").html($(this).data("message")); $("#exception").html($(this).data("exception"));
{{row[4]}}{{!"" if row[4] == None else row[4]}} <% subs_languages_list = [] @@ -200,18 +194,6 @@ }) ; - $('#update_series').click(function(){ - window.location = '{{base_url}}update_series'; - }) - - $('#update_all_episodes').click(function(){ - window.location = '{{base_url}}update_all_episodes'; - }) - - $('#add_new_episodes').click(function(){ - window.location = '{{base_url}}add_new_episodes'; - }) - $('.config').click(function(){ sessionStorage.scrolly=$(window).scrollTop(); diff --git a/views/settings.tpl b/views/settings.tpl index 0aaa1f1d5..46a69d3ad 100644 --- a/views/settings.tpl +++ b/views/settings.tpl @@ -236,7 +236,7 @@
- + Check now and update
@@ -372,7 +372,7 @@ ; $('#settings_general_check_update').click(function(){ - window.location.href = '{{base_url}}check_update'; + window.location = '{{base_url}}check_update'; }) $('a:not(.tabs), button:not(.cancel)').click(function(){ diff --git a/views/system.tpl b/views/system.tpl index 16a1c7380..7f35d7d3e 100644 --- a/views/system.tpl +++ b/views/system.tpl @@ -83,8 +83,9 @@
NameIntervalExecution Frequency Next Execution
{{task[0]}} {{task[1]}} {{task[2]}} +
+ +
+