mirror of https://github.com/morpheus65535/bazarr
Added PostgreSQL as optional database engine
Wiki: https://wiki.bazarr.media/Additional-Configuration/PostgreSQL-Database/
This commit is contained in:
parent
9f2ba673de
commit
d5911e78b5
|
@ -1,6 +1,5 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import datetime
|
|
||||||
import pretty
|
import pretty
|
||||||
|
|
||||||
from flask_restx import Resource, Namespace, reqparse, fields
|
from flask_restx import Resource, Namespace, reqparse, fields
|
||||||
|
@ -13,7 +12,7 @@ from subtitles.mass_download import episode_download_subtitles
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
from api.swaggerui import subtitles_language_model
|
from api.swaggerui import subtitles_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessEpisode
|
from ..utils import authenticate, postprocess
|
||||||
|
|
||||||
api_ns_episodes_blacklist = Namespace('Episodes Blacklist', description='List, add or remove subtitles to or from '
|
api_ns_episodes_blacklist = Namespace('Episodes Blacklist', description='List, add or remove subtitles to or from '
|
||||||
'episodes blacklist')
|
'episodes blacklist')
|
||||||
|
@ -59,18 +58,17 @@ class EpisodesBlacklist(Resource):
|
||||||
TableBlacklist.timestamp)\
|
TableBlacklist.timestamp)\
|
||||||
.join(TableEpisodes, on=(TableBlacklist.sonarr_episode_id == TableEpisodes.sonarrEpisodeId))\
|
.join(TableEpisodes, on=(TableBlacklist.sonarr_episode_id == TableEpisodes.sonarrEpisodeId))\
|
||||||
.join(TableShows, on=(TableBlacklist.sonarr_series_id == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableBlacklist.sonarr_series_id == TableShows.sonarrSeriesId))\
|
||||||
.order_by(TableBlacklist.timestamp.desc())\
|
.order_by(TableBlacklist.timestamp.desc())
|
||||||
.limit(length)\
|
if length > 0:
|
||||||
.offset(start)\
|
data = data.limit(length).offset(start)
|
||||||
.dicts()
|
data = list(data.dicts())
|
||||||
data = list(data)
|
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
# Make timestamp pretty
|
# Make timestamp pretty
|
||||||
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
item["parsed_timestamp"] = item['timestamp'].strftime('%x %X')
|
||||||
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
|
item.update({'timestamp': pretty.date(item['timestamp'])})
|
||||||
|
|
||||||
postprocessEpisode(item)
|
postprocess(item)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from flask_restx import Resource, Namespace, reqparse, fields
|
||||||
from app.database import TableEpisodes
|
from app.database import TableEpisodes
|
||||||
from api.swaggerui import subtitles_model, subtitles_language_model, audio_language_model
|
from api.swaggerui import subtitles_model, subtitles_language_model, audio_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessEpisode
|
from ..utils import authenticate, postprocess
|
||||||
|
|
||||||
api_ns_episodes = Namespace('Episodes', description='List episodes metadata for specific series or episodes.')
|
api_ns_episodes = Namespace('Episodes', description='List episodes metadata for specific series or episodes.')
|
||||||
|
|
||||||
|
@ -68,6 +68,6 @@ class Episodes(Resource):
|
||||||
|
|
||||||
result = list(result)
|
result = list(result)
|
||||||
for item in result:
|
for item in result:
|
||||||
postprocessEpisode(item)
|
postprocess(item)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -42,13 +42,14 @@ class EpisodesSubtitles(Resource):
|
||||||
args = self.patch_request_parser.parse_args()
|
args = self.patch_request_parser.parse_args()
|
||||||
sonarrSeriesId = args.get('seriesid')
|
sonarrSeriesId = args.get('seriesid')
|
||||||
sonarrEpisodeId = args.get('episodeid')
|
sonarrEpisodeId = args.get('episodeid')
|
||||||
episodeInfo = TableEpisodes.select(TableEpisodes.path,
|
episodeInfo = TableEpisodes.select(
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.path,
|
||||||
TableEpisodes.audio_language,
|
TableEpisodes.sceneName,
|
||||||
TableShows.title) \
|
TableEpisodes.audio_language,
|
||||||
|
TableShows.title) \
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\
|
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
||||||
.dicts()\
|
.dicts() \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
|
|
||||||
if not episodeInfo:
|
if not episodeInfo:
|
||||||
|
@ -56,13 +57,13 @@ class EpisodesSubtitles(Resource):
|
||||||
|
|
||||||
title = episodeInfo['title']
|
title = episodeInfo['title']
|
||||||
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
||||||
sceneName = episodeInfo['scene_name'] or "None"
|
sceneName = episodeInfo['sceneName'] or "None"
|
||||||
|
|
||||||
language = args.get('language')
|
language = args.get('language')
|
||||||
hi = args.get('hi').capitalize()
|
hi = args.get('hi').capitalize()
|
||||||
forced = args.get('forced').capitalize()
|
forced = args.get('forced').capitalize()
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId)
|
audio_language_list = get_audio_profile_languages(episodeInfo["audio_language"])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
@ -119,10 +120,10 @@ class EpisodesSubtitles(Resource):
|
||||||
sonarrEpisodeId = args.get('episodeid')
|
sonarrEpisodeId = args.get('episodeid')
|
||||||
episodeInfo = TableEpisodes.select(TableEpisodes.title,
|
episodeInfo = TableEpisodes.select(TableEpisodes.title,
|
||||||
TableEpisodes.path,
|
TableEpisodes.path,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableEpisodes.audio_language)\
|
TableEpisodes.audio_language) \
|
||||||
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\
|
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
||||||
.dicts()\
|
.dicts() \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
|
|
||||||
if not episodeInfo:
|
if not episodeInfo:
|
||||||
|
@ -130,7 +131,7 @@ class EpisodesSubtitles(Resource):
|
||||||
|
|
||||||
title = episodeInfo['title']
|
title = episodeInfo['title']
|
||||||
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
||||||
sceneName = episodeInfo['scene_name'] or "None"
|
sceneName = episodeInfo['sceneName'] or "None"
|
||||||
audio_language = episodeInfo['audio_language']
|
audio_language = episodeInfo['audio_language']
|
||||||
|
|
||||||
language = args.get('language')
|
language = args.get('language')
|
||||||
|
@ -149,7 +150,7 @@ class EpisodesSubtitles(Resource):
|
||||||
forced=forced,
|
forced=forced,
|
||||||
hi=hi,
|
hi=hi,
|
||||||
title=title,
|
title=title,
|
||||||
scene_name=sceneName,
|
sceneName=sceneName,
|
||||||
media_type='series',
|
media_type='series',
|
||||||
subtitle=subFile,
|
subtitle=subFile,
|
||||||
audio_language=audio_language)
|
audio_language=audio_language)
|
||||||
|
@ -199,10 +200,10 @@ class EpisodesSubtitles(Resource):
|
||||||
sonarrEpisodeId = args.get('episodeid')
|
sonarrEpisodeId = args.get('episodeid')
|
||||||
episodeInfo = TableEpisodes.select(TableEpisodes.title,
|
episodeInfo = TableEpisodes.select(TableEpisodes.title,
|
||||||
TableEpisodes.path,
|
TableEpisodes.path,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableEpisodes.audio_language)\
|
TableEpisodes.audio_language) \
|
||||||
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\
|
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
||||||
.dicts()\
|
.dicts() \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
|
|
||||||
if not episodeInfo:
|
if not episodeInfo:
|
||||||
|
|
|
@ -15,7 +15,7 @@ from app.config import settings
|
||||||
from utilities.path_mappings import path_mappings
|
from utilities.path_mappings import path_mappings
|
||||||
from api.swaggerui import subtitles_language_model
|
from api.swaggerui import subtitles_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessEpisode
|
from ..utils import authenticate, postprocess
|
||||||
|
|
||||||
api_ns_episodes_history = Namespace('Episodes History', description='List episodes history events')
|
api_ns_episodes_history = Namespace('Episodes History', description='List episodes history events')
|
||||||
|
|
||||||
|
@ -73,8 +73,7 @@ class EpisodesHistory(Resource):
|
||||||
upgradable_episodes_not_perfect = []
|
upgradable_episodes_not_perfect = []
|
||||||
if settings.general.getboolean('upgrade_subs'):
|
if settings.general.getboolean('upgrade_subs'):
|
||||||
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
||||||
minimum_timestamp = ((datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs))) -
|
minimum_timestamp = (datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs)))
|
||||||
datetime.datetime(1970, 1, 1)).total_seconds()
|
|
||||||
|
|
||||||
if settings.general.getboolean('upgrade_manual'):
|
if settings.general.getboolean('upgrade_manual'):
|
||||||
query_actions = [1, 2, 3, 6]
|
query_actions = [1, 2, 3, 6]
|
||||||
|
@ -90,11 +89,15 @@ class EpisodesHistory(Resource):
|
||||||
TableHistory.score,
|
TableHistory.score,
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableEpisodes.monitored,
|
TableEpisodes.monitored,
|
||||||
TableShows.seriesType)\
|
TableShows.seriesType) \
|
||||||
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\
|
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId)) \
|
||||||
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
.where(reduce(operator.and_, upgradable_episodes_conditions))\
|
.where(reduce(operator.and_, upgradable_episodes_conditions)) \
|
||||||
.group_by(TableHistory.video_path)\
|
.group_by(TableHistory.video_path,
|
||||||
|
TableHistory.score,
|
||||||
|
TableShows.tags,
|
||||||
|
TableEpisodes.monitored,
|
||||||
|
TableShows.seriesType) \
|
||||||
.dicts()
|
.dicts()
|
||||||
upgradable_episodes = list(upgradable_episodes)
|
upgradable_episodes = list(upgradable_episodes)
|
||||||
for upgradable_episode in upgradable_episodes:
|
for upgradable_episode in upgradable_episodes:
|
||||||
|
@ -114,7 +117,8 @@ class EpisodesHistory(Resource):
|
||||||
episode_history = TableHistory.select(TableHistory.id,
|
episode_history = TableHistory.select(TableHistory.id,
|
||||||
TableShows.title.alias('seriesTitle'),
|
TableShows.title.alias('seriesTitle'),
|
||||||
TableEpisodes.monitored,
|
TableEpisodes.monitored,
|
||||||
TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias('episode_number'),
|
TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias(
|
||||||
|
'episode_number'),
|
||||||
TableEpisodes.title.alias('episodeTitle'),
|
TableEpisodes.title.alias('episodeTitle'),
|
||||||
TableHistory.timestamp,
|
TableHistory.timestamp,
|
||||||
TableHistory.subs_id,
|
TableHistory.subs_id,
|
||||||
|
@ -129,15 +133,14 @@ class EpisodesHistory(Resource):
|
||||||
TableHistory.subtitles_path,
|
TableHistory.subtitles_path,
|
||||||
TableHistory.sonarrEpisodeId,
|
TableHistory.sonarrEpisodeId,
|
||||||
TableHistory.provider,
|
TableHistory.provider,
|
||||||
TableShows.seriesType)\
|
TableShows.seriesType) \
|
||||||
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\
|
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId)) \
|
||||||
.where(query_condition)\
|
.where(query_condition) \
|
||||||
.order_by(TableHistory.timestamp.desc())\
|
.order_by(TableHistory.timestamp.desc())
|
||||||
.limit(length)\
|
if length > 0:
|
||||||
.offset(start)\
|
episode_history = episode_history.limit(length).offset(start)
|
||||||
.dicts()
|
episode_history = list(episode_history.dicts())
|
||||||
episode_history = list(episode_history)
|
|
||||||
|
|
||||||
blacklist_db = TableBlacklist.select(TableBlacklist.provider, TableBlacklist.subs_id).dicts()
|
blacklist_db = TableBlacklist.select(TableBlacklist.provider, TableBlacklist.subs_id).dicts()
|
||||||
blacklist_db = list(blacklist_db)
|
blacklist_db = list(blacklist_db)
|
||||||
|
@ -145,7 +148,7 @@ class EpisodesHistory(Resource):
|
||||||
for item in episode_history:
|
for item in episode_history:
|
||||||
# Mark episode as upgradable or not
|
# Mark episode as upgradable or not
|
||||||
item.update({"upgradable": False})
|
item.update({"upgradable": False})
|
||||||
if {"video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score']),
|
if {"video_path": str(item['path']), "timestamp": item['timestamp'], "score": str(item['score']),
|
||||||
"tags": str(item['tags']), "monitored": str(item['monitored']),
|
"tags": str(item['tags']), "monitored": str(item['monitored']),
|
||||||
"seriesType": str(item['seriesType'])} in upgradable_episodes_not_perfect: # noqa: E129
|
"seriesType": str(item['seriesType'])} in upgradable_episodes_not_perfect: # noqa: E129
|
||||||
if os.path.exists(path_mappings.path_replace(item['subtitles_path'])) and \
|
if os.path.exists(path_mappings.path_replace(item['subtitles_path'])) and \
|
||||||
|
@ -154,16 +157,16 @@ class EpisodesHistory(Resource):
|
||||||
|
|
||||||
del item['path']
|
del item['path']
|
||||||
|
|
||||||
postprocessEpisode(item)
|
postprocess(item)
|
||||||
|
|
||||||
if item['score']:
|
if item['score']:
|
||||||
item['score'] = str(round((int(item['score']) * 100 / 360), 2)) + "%"
|
item['score'] = str(round((int(item['score']) * 100 / 360), 2)) + "%"
|
||||||
|
|
||||||
# Make timestamp pretty
|
# Make timestamp pretty
|
||||||
if item['timestamp']:
|
if item['timestamp']:
|
||||||
item["raw_timestamp"] = int(item['timestamp'])
|
item["raw_timestamp"] = item['timestamp'].timestamp()
|
||||||
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
item["parsed_timestamp"] = item['timestamp'].strftime('%x %X')
|
||||||
item['timestamp'] = pretty.date(item["raw_timestamp"])
|
item['timestamp'] = pretty.date(item["timestamp"])
|
||||||
|
|
||||||
# Check if subtitles is blacklisted
|
# Check if subtitles is blacklisted
|
||||||
item.update({"blacklisted": False})
|
item.update({"blacklisted": False})
|
||||||
|
@ -174,8 +177,8 @@ class EpisodesHistory(Resource):
|
||||||
item.update({"blacklisted": True})
|
item.update({"blacklisted": True})
|
||||||
break
|
break
|
||||||
|
|
||||||
count = TableHistory.select()\
|
count = TableHistory.select() \
|
||||||
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\
|
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId)) \
|
||||||
.where(TableEpisodes.title.is_null(False)).count()
|
.where(TableEpisodes.title.is_null(False)).count()
|
||||||
|
|
||||||
return {'data': episode_history, 'total': count}
|
return {'data': episode_history, 'total': count}
|
||||||
|
|
|
@ -8,7 +8,7 @@ from functools import reduce
|
||||||
from app.database import get_exclusion_clause, TableEpisodes, TableShows
|
from app.database import get_exclusion_clause, TableEpisodes, TableShows
|
||||||
from api.swaggerui import subtitles_language_model
|
from api.swaggerui import subtitles_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessEpisode
|
from ..utils import authenticate, postprocess
|
||||||
|
|
||||||
api_ns_episodes_wanted = Namespace('Episodes Wanted', description='List episodes wanted subtitles')
|
api_ns_episodes_wanted = Namespace('Episodes Wanted', description='List episodes wanted subtitles')
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class EpisodesWanted(Resource):
|
||||||
TableEpisodes.missing_subtitles,
|
TableEpisodes.missing_subtitles,
|
||||||
TableEpisodes.sonarrSeriesId,
|
TableEpisodes.sonarrSeriesId,
|
||||||
TableEpisodes.sonarrEpisodeId,
|
TableEpisodes.sonarrEpisodeId,
|
||||||
TableEpisodes.scene_name.alias('sceneName'),
|
TableEpisodes.sceneName,
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableEpisodes.failedAttempts,
|
TableEpisodes.failedAttempts,
|
||||||
TableShows.seriesType)\
|
TableShows.seriesType)\
|
||||||
|
@ -82,20 +82,20 @@ class EpisodesWanted(Resource):
|
||||||
TableEpisodes.missing_subtitles,
|
TableEpisodes.missing_subtitles,
|
||||||
TableEpisodes.sonarrSeriesId,
|
TableEpisodes.sonarrSeriesId,
|
||||||
TableEpisodes.sonarrEpisodeId,
|
TableEpisodes.sonarrEpisodeId,
|
||||||
TableEpisodes.scene_name.alias('sceneName'),
|
TableEpisodes.sceneName,
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableEpisodes.failedAttempts,
|
TableEpisodes.failedAttempts,
|
||||||
TableShows.seriesType)\
|
TableShows.seriesType)\
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||||
.where(wanted_condition)\
|
.where(wanted_condition)\
|
||||||
.order_by(TableEpisodes.rowid.desc())\
|
.order_by(TableEpisodes.rowid.desc())
|
||||||
.limit(length)\
|
if length > 0:
|
||||||
.offset(start)\
|
data = data.limit(length).offset(start)
|
||||||
.dicts()
|
data = data.dicts()
|
||||||
data = list(data)
|
data = list(data)
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
postprocessEpisode(item)
|
postprocess(item)
|
||||||
|
|
||||||
count_conditions = [(TableEpisodes.missing_subtitles != '[]')]
|
count_conditions = [(TableEpisodes.missing_subtitles != '[]')]
|
||||||
count_conditions += get_exclusion_clause('series')
|
count_conditions += get_exclusion_clause('series')
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
import operator
|
import operator
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -63,8 +62,8 @@ class HistoryStats(Resource):
|
||||||
elif timeframe == 'week':
|
elif timeframe == 'week':
|
||||||
delay = 6 * 24 * 60 * 60
|
delay = 6 * 24 * 60 * 60
|
||||||
|
|
||||||
now = time.time()
|
now = datetime.datetime.now()
|
||||||
past = now - delay
|
past = now - datetime.timedelta(seconds=delay)
|
||||||
|
|
||||||
history_where_clauses = [(TableHistory.timestamp.between(past, now))]
|
history_where_clauses = [(TableHistory.timestamp.between(past, now))]
|
||||||
history_where_clauses_movie = [(TableHistoryMovie.timestamp.between(past, now))]
|
history_where_clauses_movie = [(TableHistoryMovie.timestamp.between(past, now))]
|
||||||
|
@ -92,7 +91,7 @@ class HistoryStats(Resource):
|
||||||
.dicts()
|
.dicts()
|
||||||
data_series = [{'date': date[0], 'count': sum(1 for item in date[1])} for date in
|
data_series = [{'date': date[0], 'count': sum(1 for item in date[1])} for date in
|
||||||
itertools.groupby(list(data_series),
|
itertools.groupby(list(data_series),
|
||||||
key=lambda x: datetime.datetime.fromtimestamp(x['timestamp']).strftime(
|
key=lambda x: x['timestamp'].strftime(
|
||||||
'%Y-%m-%d'))]
|
'%Y-%m-%d'))]
|
||||||
|
|
||||||
data_movies = TableHistoryMovie.select(TableHistoryMovie.timestamp, TableHistoryMovie.id) \
|
data_movies = TableHistoryMovie.select(TableHistoryMovie.timestamp, TableHistoryMovie.id) \
|
||||||
|
@ -100,7 +99,7 @@ class HistoryStats(Resource):
|
||||||
.dicts()
|
.dicts()
|
||||||
data_movies = [{'date': date[0], 'count': sum(1 for item in date[1])} for date in
|
data_movies = [{'date': date[0], 'count': sum(1 for item in date[1])} for date in
|
||||||
itertools.groupby(list(data_movies),
|
itertools.groupby(list(data_movies),
|
||||||
key=lambda x: datetime.datetime.fromtimestamp(x['timestamp']).strftime(
|
key=lambda x: x['timestamp'].strftime(
|
||||||
'%Y-%m-%d'))]
|
'%Y-%m-%d'))]
|
||||||
|
|
||||||
for dt in rrule.rrule(rrule.DAILY,
|
for dt in rrule.rrule(rrule.DAILY,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import datetime
|
|
||||||
import pretty
|
import pretty
|
||||||
|
|
||||||
from flask_restx import Resource, Namespace, reqparse, fields
|
from flask_restx import Resource, Namespace, reqparse, fields
|
||||||
|
@ -13,7 +12,7 @@ from subtitles.mass_download import movies_download_subtitles
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
from api.swaggerui import subtitles_language_model
|
from api.swaggerui import subtitles_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessMovie
|
from ..utils import authenticate, postprocess
|
||||||
|
|
||||||
api_ns_movies_blacklist = Namespace('Movies Blacklist', description='List, add or remove subtitles to or from '
|
api_ns_movies_blacklist = Namespace('Movies Blacklist', description='List, add or remove subtitles to or from '
|
||||||
'movies blacklist')
|
'movies blacklist')
|
||||||
|
@ -54,18 +53,17 @@ class MoviesBlacklist(Resource):
|
||||||
TableBlacklistMovie.language,
|
TableBlacklistMovie.language,
|
||||||
TableBlacklistMovie.timestamp)\
|
TableBlacklistMovie.timestamp)\
|
||||||
.join(TableMovies, on=(TableBlacklistMovie.radarr_id == TableMovies.radarrId))\
|
.join(TableMovies, on=(TableBlacklistMovie.radarr_id == TableMovies.radarrId))\
|
||||||
.order_by(TableBlacklistMovie.timestamp.desc())\
|
.order_by(TableBlacklistMovie.timestamp.desc())
|
||||||
.limit(length)\
|
if length > 0:
|
||||||
.offset(start)\
|
data = data.limit(length).offset(start)
|
||||||
.dicts()
|
data = list(data.dicts())
|
||||||
data = list(data)
|
|
||||||
|
|
||||||
for item in data:
|
for item in data:
|
||||||
postprocessMovie(item)
|
postprocess(item)
|
||||||
|
|
||||||
# Make timestamp pretty
|
# Make timestamp pretty
|
||||||
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
item["parsed_timestamp"] = item['timestamp'].strftime('%x %X')
|
||||||
item.update({'timestamp': pretty.date(datetime.datetime.fromtimestamp(item['timestamp']))})
|
item.update({'timestamp': pretty.date(item['timestamp'])})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ from app.config import settings
|
||||||
from utilities.path_mappings import path_mappings
|
from utilities.path_mappings import path_mappings
|
||||||
from api.swaggerui import subtitles_language_model
|
from api.swaggerui import subtitles_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessMovie
|
from api.utils import authenticate, postprocess
|
||||||
|
|
||||||
api_ns_movies_history = Namespace('Movies History', description='List movies history events')
|
api_ns_movies_history = Namespace('Movies History', description='List movies history events')
|
||||||
|
|
||||||
|
@ -70,8 +70,7 @@ class MoviesHistory(Resource):
|
||||||
upgradable_movies_not_perfect = []
|
upgradable_movies_not_perfect = []
|
||||||
if settings.general.getboolean('upgrade_subs'):
|
if settings.general.getboolean('upgrade_subs'):
|
||||||
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
||||||
minimum_timestamp = ((datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs))) -
|
minimum_timestamp = (datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs)))
|
||||||
datetime.datetime(1970, 1, 1)).total_seconds()
|
|
||||||
|
|
||||||
if settings.general.getboolean('upgrade_manual'):
|
if settings.general.getboolean('upgrade_manual'):
|
||||||
query_actions = [1, 2, 3, 6]
|
query_actions = [1, 2, 3, 6]
|
||||||
|
@ -86,10 +85,14 @@ class MoviesHistory(Resource):
|
||||||
fn.MAX(TableHistoryMovie.timestamp).alias('timestamp'),
|
fn.MAX(TableHistoryMovie.timestamp).alias('timestamp'),
|
||||||
TableHistoryMovie.score,
|
TableHistoryMovie.score,
|
||||||
TableMovies.tags,
|
TableMovies.tags,
|
||||||
TableMovies.monitored)\
|
TableMovies.monitored) \
|
||||||
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\
|
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId)) \
|
||||||
.where(reduce(operator.and_, upgradable_movies_conditions))\
|
.where(reduce(operator.and_, upgradable_movies_conditions)) \
|
||||||
.group_by(TableHistoryMovie.video_path)\
|
.group_by(TableHistoryMovie.video_path,
|
||||||
|
TableHistoryMovie.score,
|
||||||
|
TableMovies.tags,
|
||||||
|
TableMovies.monitored
|
||||||
|
) \
|
||||||
.dicts()
|
.dicts()
|
||||||
upgradable_movies = list(upgradable_movies)
|
upgradable_movies = list(upgradable_movies)
|
||||||
|
|
||||||
|
@ -122,14 +125,13 @@ class MoviesHistory(Resource):
|
||||||
TableHistoryMovie.subs_id,
|
TableHistoryMovie.subs_id,
|
||||||
TableHistoryMovie.provider,
|
TableHistoryMovie.provider,
|
||||||
TableHistoryMovie.subtitles_path,
|
TableHistoryMovie.subtitles_path,
|
||||||
TableHistoryMovie.video_path)\
|
TableHistoryMovie.video_path) \
|
||||||
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\
|
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId)) \
|
||||||
.where(query_condition)\
|
.where(query_condition) \
|
||||||
.order_by(TableHistoryMovie.timestamp.desc())\
|
.order_by(TableHistoryMovie.timestamp.desc())
|
||||||
.limit(length)\
|
if length > 0:
|
||||||
.offset(start)\
|
movie_history = movie_history.limit(length).offset(start)
|
||||||
.dicts()
|
movie_history = list(movie_history.dicts())
|
||||||
movie_history = list(movie_history)
|
|
||||||
|
|
||||||
blacklist_db = TableBlacklistMovie.select(TableBlacklistMovie.provider, TableBlacklistMovie.subs_id).dicts()
|
blacklist_db = TableBlacklistMovie.select(TableBlacklistMovie.provider, TableBlacklistMovie.subs_id).dicts()
|
||||||
blacklist_db = list(blacklist_db)
|
blacklist_db = list(blacklist_db)
|
||||||
|
@ -137,24 +139,25 @@ class MoviesHistory(Resource):
|
||||||
for item in movie_history:
|
for item in movie_history:
|
||||||
# Mark movies as upgradable or not
|
# Mark movies as upgradable or not
|
||||||
item.update({"upgradable": False})
|
item.update({"upgradable": False})
|
||||||
if {"video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score']),
|
if {"video_path": str(item['path']), "timestamp": item['timestamp'], "score": str(item['score']),
|
||||||
"tags": str(item['tags']), "monitored": str(item['monitored'])} in upgradable_movies_not_perfect: # noqa: E129
|
"tags": str(item['tags']),
|
||||||
|
"monitored": str(item['monitored'])} in upgradable_movies_not_perfect: # noqa: E129
|
||||||
if os.path.exists(path_mappings.path_replace_movie(item['subtitles_path'])) and \
|
if os.path.exists(path_mappings.path_replace_movie(item['subtitles_path'])) and \
|
||||||
os.path.exists(path_mappings.path_replace_movie(item['video_path'])):
|
os.path.exists(path_mappings.path_replace_movie(item['video_path'])):
|
||||||
item.update({"upgradable": True})
|
item.update({"upgradable": True})
|
||||||
|
|
||||||
del item['path']
|
del item['path']
|
||||||
|
|
||||||
postprocessMovie(item)
|
postprocess(item)
|
||||||
|
|
||||||
if item['score']:
|
if item['score']:
|
||||||
item['score'] = str(round((int(item['score']) * 100 / 120), 2)) + "%"
|
item['score'] = str(round((int(item['score']) * 100 / 120), 2)) + "%"
|
||||||
|
|
||||||
# Make timestamp pretty
|
# Make timestamp pretty
|
||||||
if item['timestamp']:
|
if item['timestamp']:
|
||||||
item["raw_timestamp"] = int(item['timestamp'])
|
item["raw_timestamp"] = item['timestamp'].timestamp()
|
||||||
item["parsed_timestamp"] = datetime.datetime.fromtimestamp(int(item['timestamp'])).strftime('%x %X')
|
item["parsed_timestamp"] = item['timestamp'].strftime('%x %X')
|
||||||
item['timestamp'] = pretty.date(item["raw_timestamp"])
|
item['timestamp'] = pretty.date(item["timestamp"])
|
||||||
|
|
||||||
# Check if subtitles is blacklisted
|
# Check if subtitles is blacklisted
|
||||||
item.update({"blacklisted": False})
|
item.update({"blacklisted": False})
|
||||||
|
@ -165,9 +168,9 @@ class MoviesHistory(Resource):
|
||||||
item.update({"blacklisted": True})
|
item.update({"blacklisted": True})
|
||||||
break
|
break
|
||||||
|
|
||||||
count = TableHistoryMovie.select()\
|
count = TableHistoryMovie.select() \
|
||||||
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\
|
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId)) \
|
||||||
.where(TableMovies.title.is_null(False))\
|
.where(TableMovies.title.is_null(False)) \
|
||||||
.count()
|
.count()
|
||||||
|
|
||||||
return {'data': movie_history, 'total': count}
|
return {'data': movie_history, 'total': count}
|
||||||
|
|
|
@ -9,8 +9,7 @@ from subtitles.wanted import wanted_search_missing_subtitles_movies
|
||||||
from subtitles.mass_download import movies_download_subtitles
|
from subtitles.mass_download import movies_download_subtitles
|
||||||
from api.swaggerui import subtitles_model, subtitles_language_model, audio_language_model
|
from api.swaggerui import subtitles_model, subtitles_language_model, audio_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessMovie, None_Keys
|
from api.utils import authenticate, None_Keys, postprocess
|
||||||
|
|
||||||
|
|
||||||
api_ns_movies = Namespace('Movies', description='List movies metadata, update movie languages profile or run actions '
|
api_ns_movies = Namespace('Movies', description='List movies metadata, update movie languages profile or run actions '
|
||||||
'for specific movies.')
|
'for specific movies.')
|
||||||
|
@ -82,10 +81,13 @@ class Movies(Resource):
|
||||||
.order_by(TableMovies.sortTitle)\
|
.order_by(TableMovies.sortTitle)\
|
||||||
.dicts()
|
.dicts()
|
||||||
else:
|
else:
|
||||||
result = TableMovies.select().order_by(TableMovies.sortTitle).limit(length).offset(start).dicts()
|
result = TableMovies.select().order_by(TableMovies.sortTitle)
|
||||||
|
if length > 0:
|
||||||
|
result = result.limit(length).offset(start)
|
||||||
|
result = result.dicts()
|
||||||
result = list(result)
|
result = list(result)
|
||||||
for item in result:
|
for item in result:
|
||||||
postprocessMovie(item)
|
postprocess(item)
|
||||||
|
|
||||||
return {'data': result, 'total': count}
|
return {'data': result, 'total': count}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@ from app.config import settings
|
||||||
|
|
||||||
from ..utils import authenticate
|
from ..utils import authenticate
|
||||||
|
|
||||||
|
|
||||||
api_ns_movies_subtitles = Namespace('Movies Subtitles', description='Download, upload or delete movies subtitles')
|
api_ns_movies_subtitles = Namespace('Movies Subtitles', description='Download, upload or delete movies subtitles')
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,12 +42,13 @@ class MoviesSubtitles(Resource):
|
||||||
args = self.patch_request_parser.parse_args()
|
args = self.patch_request_parser.parse_args()
|
||||||
radarrId = args.get('radarrid')
|
radarrId = args.get('radarrid')
|
||||||
|
|
||||||
movieInfo = TableMovies.select(TableMovies.title,
|
movieInfo = TableMovies.select(
|
||||||
TableMovies.path,
|
TableMovies.title,
|
||||||
TableMovies.sceneName,
|
TableMovies.path,
|
||||||
TableMovies.audio_language)\
|
TableMovies.sceneName,
|
||||||
.where(TableMovies.radarrId == radarrId)\
|
TableMovies.audio_language) \
|
||||||
.dicts()\
|
.where(TableMovies.radarrId == radarrId) \
|
||||||
|
.dicts() \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
|
|
||||||
if not movieInfo:
|
if not movieInfo:
|
||||||
|
@ -57,19 +58,18 @@ class MoviesSubtitles(Resource):
|
||||||
sceneName = movieInfo['sceneName'] or 'None'
|
sceneName = movieInfo['sceneName'] or 'None'
|
||||||
|
|
||||||
title = movieInfo['title']
|
title = movieInfo['title']
|
||||||
audio_language = movieInfo['audio_language']
|
|
||||||
|
|
||||||
language = args.get('language')
|
language = args.get('language')
|
||||||
hi = args.get('hi').capitalize()
|
hi = args.get('hi').capitalize()
|
||||||
forced = args.get('forced').capitalize()
|
forced = args.get('forced').capitalize()
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(movie_id=radarrId)
|
audio_language_list = get_audio_profile_languages(movieInfo["audio_language"])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
audio_language = None
|
audio_language = None
|
||||||
|
|
||||||
try:
|
with contextlib.suppress(OSError):
|
||||||
result = list(generate_subtitles(moviePath, [(language, hi, forced)], audio_language,
|
result = list(generate_subtitles(moviePath, [(language, hi, forced)], audio_language,
|
||||||
sceneName, title, 'movie', profile_id=get_profile_id(movie_id=radarrId)))
|
sceneName, title, 'movie', profile_id=get_profile_id(movie_id=radarrId)))
|
||||||
if result:
|
if result:
|
||||||
|
@ -78,9 +78,9 @@ class MoviesSubtitles(Resource):
|
||||||
path = result[1]
|
path = result[1]
|
||||||
forced = result[5]
|
forced = result[5]
|
||||||
if result[8]:
|
if result[8]:
|
||||||
language_code = result[2] + ":hi"
|
language_code = f"{result[2]}:hi"
|
||||||
elif forced:
|
elif forced:
|
||||||
language_code = result[2] + ":forced"
|
language_code = f"{result[2]}:forced"
|
||||||
else:
|
else:
|
||||||
language_code = result[2]
|
language_code = result[2]
|
||||||
provider = result[3]
|
provider = result[3]
|
||||||
|
@ -92,9 +92,6 @@ class MoviesSubtitles(Resource):
|
||||||
store_subtitles_movie(path, moviePath)
|
store_subtitles_movie(path, moviePath)
|
||||||
else:
|
else:
|
||||||
event_stream(type='movie', payload=radarrId)
|
event_stream(type='movie', payload=radarrId)
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
# POST: Upload Subtitles
|
# POST: Upload Subtitles
|
||||||
|
@ -134,8 +131,8 @@ class MoviesSubtitles(Resource):
|
||||||
audioLanguage = movieInfo['audio_language']
|
audioLanguage = movieInfo['audio_language']
|
||||||
|
|
||||||
language = args.get('language')
|
language = args.get('language')
|
||||||
forced = True if args.get('forced') == 'true' else False
|
forced = args.get('forced') == 'true'
|
||||||
hi = True if args.get('hi') == 'true' else False
|
hi = args.get('hi') == 'true'
|
||||||
subFile = args.get('file')
|
subFile = args.get('file')
|
||||||
|
|
||||||
_, ext = os.path.splitext(subFile.filename)
|
_, ext = os.path.splitext(subFile.filename)
|
||||||
|
@ -143,7 +140,7 @@ class MoviesSubtitles(Resource):
|
||||||
if not isinstance(ext, str) or ext.lower() not in SUBTITLE_EXTENSIONS:
|
if not isinstance(ext, str) or ext.lower() not in SUBTITLE_EXTENSIONS:
|
||||||
raise ValueError('A subtitle of an invalid format was uploaded.')
|
raise ValueError('A subtitle of an invalid format was uploaded.')
|
||||||
|
|
||||||
try:
|
with contextlib.suppress(OSError):
|
||||||
result = manual_upload_subtitle(path=moviePath,
|
result = manual_upload_subtitle(path=moviePath,
|
||||||
language=language,
|
language=language,
|
||||||
forced=forced,
|
forced=forced,
|
||||||
|
@ -161,9 +158,9 @@ class MoviesSubtitles(Resource):
|
||||||
path = result[1]
|
path = result[1]
|
||||||
subs_path = result[2]
|
subs_path = result[2]
|
||||||
if hi:
|
if hi:
|
||||||
language_code = language + ":hi"
|
language_code = f"{language}:hi"
|
||||||
elif forced:
|
elif forced:
|
||||||
language_code = language + ":forced"
|
language_code = f"{language}:forced"
|
||||||
else:
|
else:
|
||||||
language_code = language
|
language_code = language
|
||||||
provider = "manual"
|
provider = "manual"
|
||||||
|
@ -172,9 +169,6 @@ class MoviesSubtitles(Resource):
|
||||||
if not settings.general.getboolean('dont_notify_manual_actions'):
|
if not settings.general.getboolean('dont_notify_manual_actions'):
|
||||||
send_notifications_movie(radarrId, message)
|
send_notifications_movie(radarrId, message)
|
||||||
store_subtitles_movie(path, moviePath)
|
store_subtitles_movie(path, moviePath)
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
# DELETE: Delete Subtitles
|
# DELETE: Delete Subtitles
|
||||||
|
|
|
@ -8,7 +8,7 @@ from functools import reduce
|
||||||
from app.database import get_exclusion_clause, TableMovies
|
from app.database import get_exclusion_clause, TableMovies
|
||||||
from api.swaggerui import subtitles_language_model
|
from api.swaggerui import subtitles_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessMovie
|
from api.utils import authenticate, postprocess
|
||||||
|
|
||||||
|
|
||||||
api_ns_movies_wanted = Namespace('Movies Wanted', description='List movies wanted subtitles')
|
api_ns_movies_wanted = Namespace('Movies Wanted', description='List movies wanted subtitles')
|
||||||
|
@ -75,14 +75,14 @@ class MoviesWanted(Resource):
|
||||||
TableMovies.tags,
|
TableMovies.tags,
|
||||||
TableMovies.monitored)\
|
TableMovies.monitored)\
|
||||||
.where(wanted_condition)\
|
.where(wanted_condition)\
|
||||||
.order_by(TableMovies.rowid.desc())\
|
.order_by(TableMovies.rowid.desc())
|
||||||
.limit(length)\
|
if length > 0:
|
||||||
.offset(start)\
|
result = result.limit(length).offset(start)
|
||||||
.dicts()
|
result = result.dicts()
|
||||||
result = list(result)
|
result = list(result)
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
postprocessMovie(item)
|
postprocess(item)
|
||||||
|
|
||||||
count_conditions = [(TableMovies.missing_subtitles != '[]')]
|
count_conditions = [(TableMovies.missing_subtitles != '[]')]
|
||||||
count_conditions += get_exclusion_clause('movie')
|
count_conditions += get_exclusion_clause('movie')
|
||||||
|
|
|
@ -13,7 +13,6 @@ from subtitles.indexer.series import store_subtitles
|
||||||
|
|
||||||
from ..utils import authenticate
|
from ..utils import authenticate
|
||||||
|
|
||||||
|
|
||||||
api_ns_providers_episodes = Namespace('Providers Episodes', description='List and download episodes subtitles manually')
|
api_ns_providers_episodes = Namespace('Providers Episodes', description='List and download episodes subtitles manually')
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,10 +48,10 @@ class ProviderEpisodes(Resource):
|
||||||
args = self.get_request_parser.parse_args()
|
args = self.get_request_parser.parse_args()
|
||||||
sonarrEpisodeId = args.get('episodeid')
|
sonarrEpisodeId = args.get('episodeid')
|
||||||
episodeInfo = TableEpisodes.select(TableEpisodes.path,
|
episodeInfo = TableEpisodes.select(TableEpisodes.path,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableShows.title,
|
TableShows.title,
|
||||||
TableShows.profileId) \
|
TableShows.profileId) \
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
||||||
.dicts() \
|
.dicts() \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
|
@ -62,7 +61,7 @@ class ProviderEpisodes(Resource):
|
||||||
|
|
||||||
title = episodeInfo['title']
|
title = episodeInfo['title']
|
||||||
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
||||||
sceneName = episodeInfo['scene_name'] or "None"
|
sceneName = episodeInfo['sceneName'] or "None"
|
||||||
profileId = episodeInfo['profileId']
|
profileId = episodeInfo['profileId']
|
||||||
|
|
||||||
providers_list = get_providers()
|
providers_list = get_providers()
|
||||||
|
@ -92,9 +91,11 @@ class ProviderEpisodes(Resource):
|
||||||
args = self.post_request_parser.parse_args()
|
args = self.post_request_parser.parse_args()
|
||||||
sonarrSeriesId = args.get('seriesid')
|
sonarrSeriesId = args.get('seriesid')
|
||||||
sonarrEpisodeId = args.get('episodeid')
|
sonarrEpisodeId = args.get('episodeid')
|
||||||
episodeInfo = TableEpisodes.select(TableEpisodes.path,
|
episodeInfo = TableEpisodes.select(
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.audio_language,
|
||||||
TableShows.title) \
|
TableEpisodes.path,
|
||||||
|
TableEpisodes.sceneName,
|
||||||
|
TableShows.title) \
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \
|
||||||
.dicts() \
|
.dicts() \
|
||||||
|
@ -105,7 +106,7 @@ class ProviderEpisodes(Resource):
|
||||||
|
|
||||||
title = episodeInfo['title']
|
title = episodeInfo['title']
|
||||||
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
episodePath = path_mappings.path_replace(episodeInfo['path'])
|
||||||
sceneName = episodeInfo['scene_name'] or "None"
|
sceneName = episodeInfo['sceneName'] or "None"
|
||||||
|
|
||||||
hi = args.get('hi').capitalize()
|
hi = args.get('hi').capitalize()
|
||||||
forced = args.get('forced').capitalize()
|
forced = args.get('forced').capitalize()
|
||||||
|
@ -113,7 +114,7 @@ class ProviderEpisodes(Resource):
|
||||||
selected_provider = args.get('provider')
|
selected_provider = args.get('provider')
|
||||||
subtitle = args.get('subtitle')
|
subtitle = args.get('subtitle')
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId)
|
audio_language_list = get_audio_profile_languages(episodeInfo["audio_language"])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -110,7 +110,7 @@ class ProviderMovies(Resource):
|
||||||
selected_provider = args.get('provider')
|
selected_provider = args.get('provider')
|
||||||
subtitle = args.get('subtitle')
|
subtitle = args.get('subtitle')
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(movie_id=radarrId)
|
audio_language_list = get_audio_profile_languages(movieInfo["audio_language"])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -4,6 +4,7 @@ import operator
|
||||||
|
|
||||||
from flask_restx import Resource, Namespace, reqparse, fields
|
from flask_restx import Resource, Namespace, reqparse, fields
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from peewee import fn, JOIN
|
||||||
|
|
||||||
from app.database import get_exclusion_clause, TableEpisodes, TableShows
|
from app.database import get_exclusion_clause, TableEpisodes, TableShows
|
||||||
from subtitles.indexer.series import list_missing_subtitles, series_scan_subtitles
|
from subtitles.indexer.series import list_missing_subtitles, series_scan_subtitles
|
||||||
|
@ -12,8 +13,7 @@ from subtitles.wanted import wanted_search_missing_subtitles_series
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
from api.swaggerui import subtitles_model, subtitles_language_model, audio_language_model
|
from api.swaggerui import subtitles_model, subtitles_language_model, audio_language_model
|
||||||
|
|
||||||
from ..utils import authenticate, postprocessSeries, None_Keys
|
from api.utils import authenticate, None_Keys, postprocess
|
||||||
|
|
||||||
|
|
||||||
api_ns_series = Namespace('Series', description='List series metadata, update series languages profile or run actions '
|
api_ns_series = Namespace('Series', description='List series metadata, update series languages profile or run actions '
|
||||||
'for specific series.')
|
'for specific series.')
|
||||||
|
@ -34,8 +34,8 @@ class Series(Resource):
|
||||||
data_model = api_ns_series.model('series_data_model', {
|
data_model = api_ns_series.model('series_data_model', {
|
||||||
'alternativeTitles': fields.List(fields.String),
|
'alternativeTitles': fields.List(fields.String),
|
||||||
'audio_language': fields.Nested(get_audio_language_model),
|
'audio_language': fields.Nested(get_audio_language_model),
|
||||||
'episodeFileCount': fields.Integer(),
|
'episodeFileCount': fields.Integer(default=0),
|
||||||
'episodeMissingCount': fields.Integer(),
|
'episodeMissingCount': fields.Integer(default=0),
|
||||||
'fanart': fields.String(),
|
'fanart': fields.String(),
|
||||||
'imdbId': fields.String(),
|
'imdbId': fields.String(),
|
||||||
'monitored': fields.Boolean(),
|
'monitored': fields.Boolean(),
|
||||||
|
@ -70,40 +70,37 @@ class Series(Resource):
|
||||||
seriesId = args.get('seriesid[]')
|
seriesId = args.get('seriesid[]')
|
||||||
|
|
||||||
count = TableShows.select().count()
|
count = TableShows.select().count()
|
||||||
|
episodeFileCount = TableEpisodes.select(TableShows.sonarrSeriesId,
|
||||||
|
fn.COUNT(TableEpisodes.sonarrSeriesId).coerce(False).alias('episodeFileCount')) \
|
||||||
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
|
.group_by(TableShows.sonarrSeriesId).alias('episodeFileCount')
|
||||||
|
|
||||||
|
episodes_missing_conditions = [(TableEpisodes.missing_subtitles != '[]')]
|
||||||
|
episodes_missing_conditions += get_exclusion_clause('series')
|
||||||
|
|
||||||
|
episodeMissingCount = (TableEpisodes.select(TableShows.sonarrSeriesId,
|
||||||
|
fn.COUNT(TableEpisodes.sonarrSeriesId).coerce(False).alias('episodeMissingCount'))
|
||||||
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))
|
||||||
|
.where(reduce(operator.and_, episodes_missing_conditions)).group_by(
|
||||||
|
TableShows.sonarrSeriesId).alias('episodeMissingCount'))
|
||||||
|
|
||||||
|
result = TableShows.select(TableShows, episodeFileCount.c.episodeFileCount,
|
||||||
|
episodeMissingCount.c.episodeMissingCount).join(episodeFileCount,
|
||||||
|
join_type=JOIN.LEFT_OUTER, on=(
|
||||||
|
TableShows.sonarrSeriesId ==
|
||||||
|
episodeFileCount.c.sonarrSeriesId)
|
||||||
|
) \
|
||||||
|
.join(episodeMissingCount, join_type=JOIN.LEFT_OUTER,
|
||||||
|
on=(TableShows.sonarrSeriesId == episodeMissingCount.c.sonarrSeriesId)).order_by(TableShows.sortTitle)
|
||||||
|
|
||||||
if len(seriesId) != 0:
|
if len(seriesId) != 0:
|
||||||
result = TableShows.select() \
|
result = result.where(TableShows.sonarrSeriesId.in_(seriesId))
|
||||||
.where(TableShows.sonarrSeriesId.in_(seriesId)) \
|
elif length > 0:
|
||||||
.order_by(TableShows.sortTitle).dicts()
|
result = result.limit(length).offset(start)
|
||||||
else:
|
result = list(result.dicts())
|
||||||
result = TableShows.select().order_by(TableShows.sortTitle).limit(length).offset(start).dicts()
|
|
||||||
|
|
||||||
result = list(result)
|
|
||||||
|
|
||||||
for item in result:
|
for item in result:
|
||||||
postprocessSeries(item)
|
postprocess(item)
|
||||||
|
|
||||||
# Add missing subtitles episode count
|
|
||||||
episodes_missing_conditions = [(TableEpisodes.sonarrSeriesId == item['sonarrSeriesId']),
|
|
||||||
(TableEpisodes.missing_subtitles != '[]')]
|
|
||||||
episodes_missing_conditions += get_exclusion_clause('series')
|
|
||||||
|
|
||||||
episodeMissingCount = TableEpisodes.select(TableShows.tags,
|
|
||||||
TableEpisodes.monitored,
|
|
||||||
TableShows.seriesType) \
|
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
|
||||||
.where(reduce(operator.and_, episodes_missing_conditions)) \
|
|
||||||
.count()
|
|
||||||
item.update({"episodeMissingCount": episodeMissingCount})
|
|
||||||
|
|
||||||
# Add episode count
|
|
||||||
episodeFileCount = TableEpisodes.select(TableShows.tags,
|
|
||||||
TableEpisodes.monitored,
|
|
||||||
TableShows.seriesType) \
|
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
|
||||||
.where(TableEpisodes.sonarrSeriesId == item['sonarrSeriesId']) \
|
|
||||||
.count()
|
|
||||||
item.update({"episodeFileCount": episodeFileCount})
|
|
||||||
|
|
||||||
return {'data': result, 'total': count}
|
return {'data': result, 'total': count}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from flask import request, jsonify
|
||||||
from flask_restx import Resource, Namespace
|
from flask_restx import Resource, Namespace
|
||||||
|
|
||||||
from app.database import TableLanguagesProfiles, TableSettingsLanguages, TableShows, TableMovies, \
|
from app.database import TableLanguagesProfiles, TableSettingsLanguages, TableShows, TableMovies, \
|
||||||
TableSettingsNotifier
|
TableSettingsNotifier, update_profile_id_list
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
from app.config import settings, save_settings, get_settings
|
from app.config import settings, save_settings, get_settings
|
||||||
from app.scheduler import scheduler
|
from app.scheduler import scheduler
|
||||||
|
@ -92,6 +92,9 @@ class SystemSettings(Resource):
|
||||||
# Remove deleted profiles
|
# Remove deleted profiles
|
||||||
TableLanguagesProfiles.delete().where(TableLanguagesProfiles.profileId == profileId).execute()
|
TableLanguagesProfiles.delete().where(TableLanguagesProfiles.profileId == profileId).execute()
|
||||||
|
|
||||||
|
# invalidate cache
|
||||||
|
update_profile_id_list.invalidate()
|
||||||
|
|
||||||
event_stream("languages")
|
event_stream("languages")
|
||||||
|
|
||||||
if settings.general.getboolean('use_sonarr'):
|
if settings.general.getboolean('use_sonarr'):
|
||||||
|
|
|
@ -36,186 +36,55 @@ def authenticate(actual_method):
|
||||||
|
|
||||||
def postprocess(item):
|
def postprocess(item):
|
||||||
# Remove ffprobe_cache
|
# Remove ffprobe_cache
|
||||||
if 'ffprobe_cache' in item:
|
if item.get('movie_file_id'):
|
||||||
del (item['ffprobe_cache'])
|
path_replace = path_mappings.path_replace_movie
|
||||||
|
else:
|
||||||
|
path_replace = path_mappings.path_replace
|
||||||
|
if item.get('ffprobe_cache'):
|
||||||
|
del item['ffprobe_cache']
|
||||||
|
|
||||||
# Parse tags
|
|
||||||
if 'tags' in item:
|
|
||||||
if item['tags'] is None:
|
|
||||||
item['tags'] = []
|
|
||||||
else:
|
|
||||||
item['tags'] = ast.literal_eval(item['tags'])
|
|
||||||
|
|
||||||
if 'monitored' in item:
|
|
||||||
if item['monitored'] is None:
|
|
||||||
item['monitored'] = False
|
|
||||||
else:
|
|
||||||
item['monitored'] = item['monitored'] == 'True'
|
|
||||||
|
|
||||||
if 'hearing_impaired' in item and item['hearing_impaired'] is not None:
|
|
||||||
if item['hearing_impaired'] is None:
|
|
||||||
item['hearing_impaired'] = False
|
|
||||||
else:
|
|
||||||
item['hearing_impaired'] = item['hearing_impaired'] == 'True'
|
|
||||||
|
|
||||||
if 'language' in item:
|
|
||||||
if item['language'] == 'None':
|
|
||||||
item['language'] = None
|
|
||||||
elif item['language'] is not None:
|
|
||||||
splitted_language = item['language'].split(':')
|
|
||||||
item['language'] = {"name": language_from_alpha2(splitted_language[0]),
|
|
||||||
"code2": splitted_language[0],
|
|
||||||
"code3": alpha3_from_alpha2(splitted_language[0]),
|
|
||||||
"forced": True if item['language'].endswith(':forced') else False,
|
|
||||||
"hi": True if item['language'].endswith(':hi') else False}
|
|
||||||
|
|
||||||
|
|
||||||
def postprocessSeries(item):
|
|
||||||
postprocess(item)
|
|
||||||
# Parse audio language
|
# Parse audio language
|
||||||
if 'audio_language' in item and item['audio_language'] is not None:
|
if item.get('audio_language') is not None:
|
||||||
item['audio_language'] = get_audio_profile_languages(series_id=item['sonarrSeriesId'])
|
item['audio_language'] = get_audio_profile_languages(item['audio_language'])
|
||||||
|
|
||||||
# Make sure profileId is a valid None value
|
# Make sure profileId is a valid None value
|
||||||
if 'profileId' in item and item['profileId'] in None_Keys:
|
if item.get('profileId') and item['profileId'] in None_Keys:
|
||||||
item['profileId'] = None
|
|
||||||
|
|
||||||
if 'alternateTitles' in item:
|
|
||||||
if item['alternateTitles'] is None:
|
|
||||||
item['alternativeTitles'] = []
|
|
||||||
else:
|
|
||||||
item['alternativeTitles'] = ast.literal_eval(item['alternateTitles'])
|
|
||||||
del item["alternateTitles"]
|
|
||||||
|
|
||||||
# Parse seriesType
|
|
||||||
if 'seriesType' in item and item['seriesType'] is not None:
|
|
||||||
item['seriesType'] = item['seriesType'].capitalize()
|
|
||||||
|
|
||||||
if 'path' in item:
|
|
||||||
item['path'] = path_mappings.path_replace(item['path'])
|
|
||||||
|
|
||||||
# map poster and fanart to server proxy
|
|
||||||
if 'poster' in item:
|
|
||||||
poster = item['poster']
|
|
||||||
item['poster'] = f"{base_url}/images/series{poster}" if poster else None
|
|
||||||
|
|
||||||
if 'fanart' in item:
|
|
||||||
fanart = item['fanart']
|
|
||||||
item['fanart'] = f"{base_url}/images/series{fanart}" if fanart else None
|
|
||||||
|
|
||||||
|
|
||||||
def postprocessEpisode(item):
|
|
||||||
postprocess(item)
|
|
||||||
if 'audio_language' in item and item['audio_language'] is not None:
|
|
||||||
item['audio_language'] = get_audio_profile_languages(episode_id=item['sonarrEpisodeId'])
|
|
||||||
|
|
||||||
if 'subtitles' in item:
|
|
||||||
if item['subtitles'] is None:
|
|
||||||
raw_subtitles = []
|
|
||||||
else:
|
|
||||||
raw_subtitles = ast.literal_eval(item['subtitles'])
|
|
||||||
subtitles = []
|
|
||||||
|
|
||||||
for subs in raw_subtitles:
|
|
||||||
subtitle = subs[0].split(':')
|
|
||||||
sub = {"name": language_from_alpha2(subtitle[0]),
|
|
||||||
"code2": subtitle[0],
|
|
||||||
"code3": alpha3_from_alpha2(subtitle[0]),
|
|
||||||
"path": path_mappings.path_replace(subs[1]),
|
|
||||||
"forced": False,
|
|
||||||
"hi": False}
|
|
||||||
if len(subtitle) > 1:
|
|
||||||
sub["forced"] = True if subtitle[1] == 'forced' else False
|
|
||||||
sub["hi"] = True if subtitle[1] == 'hi' else False
|
|
||||||
|
|
||||||
subtitles.append(sub)
|
|
||||||
|
|
||||||
item.update({"subtitles": subtitles})
|
|
||||||
|
|
||||||
# Parse missing subtitles
|
|
||||||
if 'missing_subtitles' in item:
|
|
||||||
if item['missing_subtitles'] is None:
|
|
||||||
item['missing_subtitles'] = []
|
|
||||||
else:
|
|
||||||
item['missing_subtitles'] = ast.literal_eval(item['missing_subtitles'])
|
|
||||||
for i, subs in enumerate(item['missing_subtitles']):
|
|
||||||
subtitle = subs.split(':')
|
|
||||||
item['missing_subtitles'][i] = {"name": language_from_alpha2(subtitle[0]),
|
|
||||||
"code2": subtitle[0],
|
|
||||||
"code3": alpha3_from_alpha2(subtitle[0]),
|
|
||||||
"forced": False,
|
|
||||||
"hi": False}
|
|
||||||
if len(subtitle) > 1:
|
|
||||||
item['missing_subtitles'][i].update({
|
|
||||||
"forced": True if subtitle[1] == 'forced' else False,
|
|
||||||
"hi": True if subtitle[1] == 'hi' else False
|
|
||||||
})
|
|
||||||
|
|
||||||
if 'scene_name' in item:
|
|
||||||
item["sceneName"] = item["scene_name"]
|
|
||||||
del item["scene_name"]
|
|
||||||
|
|
||||||
if 'path' in item and item['path']:
|
|
||||||
# Provide mapped path
|
|
||||||
item['path'] = path_mappings.path_replace(item['path'])
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Move
|
|
||||||
def postprocessMovie(item):
|
|
||||||
postprocess(item)
|
|
||||||
# Parse audio language
|
|
||||||
if 'audio_language' in item and item['audio_language'] is not None:
|
|
||||||
item['audio_language'] = get_audio_profile_languages(movie_id=item['radarrId'])
|
|
||||||
|
|
||||||
# Make sure profileId is a valid None value
|
|
||||||
if 'profileId' in item and item['profileId'] in None_Keys:
|
|
||||||
item['profileId'] = None
|
item['profileId'] = None
|
||||||
|
|
||||||
# Parse alternate titles
|
# Parse alternate titles
|
||||||
if 'alternativeTitles' in item:
|
if item.get('alternativeTitles'):
|
||||||
if item['alternativeTitles'] is None:
|
item['alternativeTitles'] = ast.literal_eval(item['alternativeTitles'])
|
||||||
item['alternativeTitles'] = []
|
|
||||||
else:
|
|
||||||
item['alternativeTitles'] = ast.literal_eval(item['alternativeTitles'])
|
|
||||||
|
|
||||||
# Parse failed attempts
|
# Parse failed attempts
|
||||||
if 'failedAttempts' in item:
|
if item.get('failedAttempts'):
|
||||||
if item['failedAttempts']:
|
item['failedAttempts'] = ast.literal_eval(item['failedAttempts'])
|
||||||
item['failedAttempts'] = ast.literal_eval(item['failedAttempts'])
|
|
||||||
|
|
||||||
# Parse subtitles
|
# Parse subtitles
|
||||||
if 'subtitles' in item:
|
if item.get('subtitles'):
|
||||||
if item['subtitles'] is None:
|
item['subtitles'] = ast.literal_eval(item['subtitles'])
|
||||||
item['subtitles'] = []
|
|
||||||
else:
|
|
||||||
item['subtitles'] = ast.literal_eval(item['subtitles'])
|
|
||||||
for i, subs in enumerate(item['subtitles']):
|
for i, subs in enumerate(item['subtitles']):
|
||||||
language = subs[0].split(':')
|
language = subs[0].split(':')
|
||||||
item['subtitles'][i] = {"path": path_mappings.path_replace_movie(subs[1]),
|
item['subtitles'][i] = {"path": path_replace(subs[1]),
|
||||||
"name": language_from_alpha2(language[0]),
|
"name": language_from_alpha2(language[0]),
|
||||||
"code2": language[0],
|
"code2": language[0],
|
||||||
"code3": alpha3_from_alpha2(language[0]),
|
"code3": alpha3_from_alpha2(language[0]),
|
||||||
"forced": False,
|
"forced": False,
|
||||||
"hi": False}
|
"hi": False}
|
||||||
if len(language) > 1:
|
if len(language) > 1:
|
||||||
item['subtitles'][i].update({
|
item['subtitles'][i].update(
|
||||||
"forced": True if language[1] == 'forced' else False,
|
{
|
||||||
"hi": True if language[1] == 'hi' else False
|
"forced": language[1] == 'forced',
|
||||||
})
|
"hi": language[1] == 'hi',
|
||||||
|
}
|
||||||
if settings.general.getboolean('embedded_subs_show_desired'):
|
)
|
||||||
|
if settings.general.getboolean('embedded_subs_show_desired') and item.get('profileId'):
|
||||||
desired_lang_list = get_desired_languages(item['profileId'])
|
desired_lang_list = get_desired_languages(item['profileId'])
|
||||||
item['subtitles'] = [x for x in item['subtitles'] if x['code2'] in desired_lang_list or x['path']]
|
item['subtitles'] = [x for x in item['subtitles'] if x['code2'] in desired_lang_list or x['path']]
|
||||||
|
item['subtitles'] = sorted(item['subtitles'], key=itemgetter('name', 'forced'))
|
||||||
if item['subtitles']:
|
|
||||||
item['subtitles'] = sorted(item['subtitles'], key=itemgetter('name', 'forced'))
|
|
||||||
|
|
||||||
# Parse missing subtitles
|
# Parse missing subtitles
|
||||||
if 'missing_subtitles' in item:
|
if item.get('missing_subtitles'):
|
||||||
if item['missing_subtitles'] is None:
|
item['missing_subtitles'] = ast.literal_eval(item['missing_subtitles'])
|
||||||
item['missing_subtitles'] = []
|
|
||||||
else:
|
|
||||||
item['missing_subtitles'] = ast.literal_eval(item['missing_subtitles'])
|
|
||||||
for i, subs in enumerate(item['missing_subtitles']):
|
for i, subs in enumerate(item['missing_subtitles']):
|
||||||
language = subs.split(':')
|
language = subs.split(':')
|
||||||
item['missing_subtitles'][i] = {"name": language_from_alpha2(language[0]),
|
item['missing_subtitles'][i] = {"name": language_from_alpha2(language[0]),
|
||||||
|
@ -224,25 +93,50 @@ def postprocessMovie(item):
|
||||||
"forced": False,
|
"forced": False,
|
||||||
"hi": False}
|
"hi": False}
|
||||||
if len(language) > 1:
|
if len(language) > 1:
|
||||||
item['missing_subtitles'][i].update({
|
item['missing_subtitles'][i].update(
|
||||||
"forced": True if language[1] == 'forced' else False,
|
{
|
||||||
"hi": True if language[1] == 'hi' else False
|
"forced": language[1] == 'forced',
|
||||||
})
|
"hi": language[1] == 'hi',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Provide mapped path
|
# Parse tags
|
||||||
if 'path' in item:
|
if item.get('tags') is not None:
|
||||||
if item['path']:
|
item['tags'] = ast.literal_eval(item.get('tags', '[]'))
|
||||||
item['path'] = path_mappings.path_replace_movie(item['path'])
|
if item.get('monitored'):
|
||||||
|
item['monitored'] = item.get('monitored') == 'True'
|
||||||
|
if item.get('hearing_impaired'):
|
||||||
|
item['hearing_impaired'] = item.get('hearing_impaired') == 'True'
|
||||||
|
|
||||||
if 'subtitles_path' in item:
|
if item.get('language'):
|
||||||
|
if item['language'] == 'None':
|
||||||
|
item['language'] = None
|
||||||
|
if item['language'] is not None:
|
||||||
|
splitted_language = item['language'].split(':')
|
||||||
|
item['language'] = {
|
||||||
|
"name": language_from_alpha2(splitted_language[0]),
|
||||||
|
"code2": splitted_language[0],
|
||||||
|
"code3": alpha3_from_alpha2(splitted_language[0]),
|
||||||
|
"forced": bool(item['language'].endswith(':forced')),
|
||||||
|
"hi": bool(item['language'].endswith(':hi')),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse seriesType
|
||||||
|
if item.get('seriesType') is not None:
|
||||||
|
item['seriesType'] = item['seriesType'].capitalize()
|
||||||
|
|
||||||
|
if item.get('path'):
|
||||||
|
item['path'] = path_replace(item['path'])
|
||||||
|
|
||||||
|
if item.get('subtitles_path'):
|
||||||
# Provide mapped subtitles path
|
# Provide mapped subtitles path
|
||||||
item['subtitles_path'] = path_mappings.path_replace_movie(item['subtitles_path'])
|
item['subtitles_path'] = path_replace(item['subtitles_path'])
|
||||||
|
|
||||||
# map poster and fanart to server proxy
|
# map poster and fanart to server proxy
|
||||||
if 'poster' in item:
|
if item.get('poster') is not None:
|
||||||
poster = item['poster']
|
poster = item['poster']
|
||||||
item['poster'] = f"{base_url}/images/movies{poster}" if poster else None
|
item['poster'] = f"{base_url}/images/{'movies' if item.get('movie_file_id') else 'series'}{poster}" if poster else None
|
||||||
|
|
||||||
if 'fanart' in item:
|
if item.get('fanart') is not None:
|
||||||
fanart = item['fanart']
|
fanart = item['fanart']
|
||||||
item['fanart'] = f"{base_url}/images/movies{fanart}" if fanart else None
|
item['fanart'] = f"{base_url}/images/{'movies' if item.get('movie_file_id') else 'series'}{fanart}" if fanart else None
|
||||||
|
|
|
@ -5,6 +5,7 @@ from flask import Flask, redirect
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
|
from .database import database
|
||||||
from .get_args import args
|
from .get_args import args
|
||||||
from .config import settings, base_url
|
from .config import settings, base_url
|
||||||
|
|
||||||
|
@ -37,6 +38,19 @@ def create_app():
|
||||||
def page_not_found(_):
|
def page_not_found(_):
|
||||||
return redirect(base_url, code=302)
|
return redirect(base_url, code=302)
|
||||||
|
|
||||||
|
# This hook ensures that a connection is opened to handle any queries
|
||||||
|
# generated by the request.
|
||||||
|
@app.before_request
|
||||||
|
def _db_connect():
|
||||||
|
database.connect()
|
||||||
|
|
||||||
|
# This hook ensures that the connection is closed when we've finished
|
||||||
|
# processing the request.
|
||||||
|
@app.teardown_request
|
||||||
|
def _db_close(exc):
|
||||||
|
if not database.is_closed():
|
||||||
|
database.close()
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -261,6 +261,14 @@ defaults = {
|
||||||
"streaming_service": 1,
|
"streaming_service": 1,
|
||||||
"edition": 1,
|
"edition": 1,
|
||||||
"hearing_impaired": 1,
|
"hearing_impaired": 1,
|
||||||
|
},
|
||||||
|
'postgresql': {
|
||||||
|
'enabled': 'False',
|
||||||
|
'host': 'localhost',
|
||||||
|
'port': '5432',
|
||||||
|
'database': '',
|
||||||
|
'username': '',
|
||||||
|
'password': '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,58 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import atexit
|
import atexit
|
||||||
import json
|
import json
|
||||||
import ast
|
import ast
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from peewee import Model, AutoField, TextField, IntegerField, ForeignKeyField, BlobField, BooleanField
|
from peewee import Model, AutoField, TextField, IntegerField, ForeignKeyField, BlobField, BooleanField, BigIntegerField, \
|
||||||
|
DateTimeField
|
||||||
from playhouse.sqliteq import SqliteQueueDatabase
|
from playhouse.sqliteq import SqliteQueueDatabase
|
||||||
from playhouse.migrate import SqliteMigrator, migrate
|
from playhouse.migrate import SqliteMigrator, migrate
|
||||||
from playhouse.sqlite_ext import RowIDField
|
from playhouse.sqlite_ext import RowIDField
|
||||||
|
|
||||||
|
from dogpile.cache import make_region
|
||||||
from utilities.path_mappings import path_mappings
|
from utilities.path_mappings import path_mappings
|
||||||
|
|
||||||
|
from peewee import PostgresqlDatabase
|
||||||
|
from playhouse.migrate import PostgresqlMigrator
|
||||||
|
|
||||||
from .config import settings, get_array_from
|
from .config import settings, get_array_from
|
||||||
from .get_args import args
|
from .get_args import args
|
||||||
|
|
||||||
database = SqliteQueueDatabase(os.path.join(args.config_dir, 'db', 'bazarr.db'),
|
logger = logging.getLogger(__name__)
|
||||||
use_gevent=False,
|
|
||||||
autostart=True,
|
postgresql = settings.postgresql.getboolean('enabled')
|
||||||
queue_max_size=256)
|
|
||||||
migrator = SqliteMigrator(database)
|
region = make_region().configure('dogpile.cache.memory')
|
||||||
|
|
||||||
|
if postgresql:
|
||||||
|
logger.debug(
|
||||||
|
f"Connecting to PostgreSQL database: {settings.postgresql.host}:{settings.postgresql.port}/{settings.postgresql.database}")
|
||||||
|
database = PostgresqlDatabase(settings.postgresql.database,
|
||||||
|
user=settings.postgresql.username,
|
||||||
|
password=settings.postgresql.password,
|
||||||
|
host=settings.postgresql.host,
|
||||||
|
port=settings.postgresql.port,
|
||||||
|
autoconnect=True
|
||||||
|
)
|
||||||
|
migrator = PostgresqlMigrator(database)
|
||||||
|
else:
|
||||||
|
db_path = os.path.join(args.config_dir, 'db', 'bazarr.db')
|
||||||
|
logger.debug(f"Connecting to SQLite database: {db_path}")
|
||||||
|
database = SqliteQueueDatabase(db_path,
|
||||||
|
use_gevent=False,
|
||||||
|
autostart=True,
|
||||||
|
queue_max_size=256)
|
||||||
|
migrator = SqliteMigrator(database)
|
||||||
|
|
||||||
|
|
||||||
@atexit.register
|
@atexit.register
|
||||||
def _stop_worker_threads():
|
def _stop_worker_threads():
|
||||||
database.stop()
|
if not postgresql:
|
||||||
|
database.stop()
|
||||||
|
|
||||||
|
|
||||||
class UnknownField(object):
|
class UnknownField(object):
|
||||||
|
@ -52,7 +79,7 @@ class TableBlacklist(BaseModel):
|
||||||
sonarr_episode_id = IntegerField(null=True)
|
sonarr_episode_id = IntegerField(null=True)
|
||||||
sonarr_series_id = IntegerField(null=True)
|
sonarr_series_id = IntegerField(null=True)
|
||||||
subs_id = TextField(null=True)
|
subs_id = TextField(null=True)
|
||||||
timestamp = IntegerField(null=True)
|
timestamp = DateTimeField(null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'table_blacklist'
|
table_name = 'table_blacklist'
|
||||||
|
@ -64,7 +91,7 @@ class TableBlacklistMovie(BaseModel):
|
||||||
provider = TextField(null=True)
|
provider = TextField(null=True)
|
||||||
radarr_id = IntegerField(null=True)
|
radarr_id = IntegerField(null=True)
|
||||||
subs_id = TextField(null=True)
|
subs_id = TextField(null=True)
|
||||||
timestamp = IntegerField(null=True)
|
timestamp = DateTimeField(null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = 'table_blacklist_movie'
|
table_name = 'table_blacklist_movie'
|
||||||
|
@ -79,13 +106,13 @@ class TableEpisodes(BaseModel):
|
||||||
episode_file_id = IntegerField(null=True)
|
episode_file_id = IntegerField(null=True)
|
||||||
failedAttempts = TextField(null=True)
|
failedAttempts = TextField(null=True)
|
||||||
ffprobe_cache = BlobField(null=True)
|
ffprobe_cache = BlobField(null=True)
|
||||||
file_size = IntegerField(default=0, null=True)
|
file_size = BigIntegerField(default=0, null=True)
|
||||||
format = TextField(null=True)
|
format = TextField(null=True)
|
||||||
missing_subtitles = TextField(null=True)
|
missing_subtitles = TextField(null=True)
|
||||||
monitored = TextField(null=True)
|
monitored = TextField(null=True)
|
||||||
path = TextField()
|
path = TextField()
|
||||||
resolution = TextField(null=True)
|
resolution = TextField(null=True)
|
||||||
scene_name = TextField(null=True)
|
sceneName = TextField(null=True)
|
||||||
season = IntegerField()
|
season = IntegerField()
|
||||||
sonarrEpisodeId = IntegerField(unique=True)
|
sonarrEpisodeId = IntegerField(unique=True)
|
||||||
sonarrSeriesId = IntegerField()
|
sonarrSeriesId = IntegerField()
|
||||||
|
@ -104,12 +131,12 @@ class TableHistory(BaseModel):
|
||||||
id = AutoField()
|
id = AutoField()
|
||||||
language = TextField(null=True)
|
language = TextField(null=True)
|
||||||
provider = TextField(null=True)
|
provider = TextField(null=True)
|
||||||
score = TextField(null=True)
|
score = IntegerField(null=True)
|
||||||
sonarrEpisodeId = IntegerField()
|
sonarrEpisodeId = IntegerField()
|
||||||
sonarrSeriesId = IntegerField()
|
sonarrSeriesId = IntegerField()
|
||||||
subs_id = TextField(null=True)
|
subs_id = TextField(null=True)
|
||||||
subtitles_path = TextField(null=True)
|
subtitles_path = TextField(null=True)
|
||||||
timestamp = IntegerField()
|
timestamp = DateTimeField()
|
||||||
video_path = TextField(null=True)
|
video_path = TextField(null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -123,10 +150,10 @@ class TableHistoryMovie(BaseModel):
|
||||||
language = TextField(null=True)
|
language = TextField(null=True)
|
||||||
provider = TextField(null=True)
|
provider = TextField(null=True)
|
||||||
radarrId = IntegerField()
|
radarrId = IntegerField()
|
||||||
score = TextField(null=True)
|
score = IntegerField(null=True)
|
||||||
subs_id = TextField(null=True)
|
subs_id = TextField(null=True)
|
||||||
subtitles_path = TextField(null=True)
|
subtitles_path = TextField(null=True)
|
||||||
timestamp = IntegerField()
|
timestamp = DateTimeField()
|
||||||
video_path = TextField(null=True)
|
video_path = TextField(null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -154,7 +181,7 @@ class TableMovies(BaseModel):
|
||||||
failedAttempts = TextField(null=True)
|
failedAttempts = TextField(null=True)
|
||||||
fanart = TextField(null=True)
|
fanart = TextField(null=True)
|
||||||
ffprobe_cache = BlobField(null=True)
|
ffprobe_cache = BlobField(null=True)
|
||||||
file_size = IntegerField(default=0, null=True)
|
file_size = BigIntegerField(default=0, null=True)
|
||||||
format = TextField(null=True)
|
format = TextField(null=True)
|
||||||
imdbId = TextField(null=True)
|
imdbId = TextField(null=True)
|
||||||
missing_subtitles = TextField(null=True)
|
missing_subtitles = TextField(null=True)
|
||||||
|
@ -211,7 +238,7 @@ class TableSettingsNotifier(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class TableShows(BaseModel):
|
class TableShows(BaseModel):
|
||||||
alternateTitles = TextField(null=True)
|
alternativeTitles = TextField(null=True)
|
||||||
audio_language = TextField(null=True)
|
audio_language = TextField(null=True)
|
||||||
fanart = TextField(null=True)
|
fanart = TextField(null=True)
|
||||||
imdbId = TextField(default='""', null=True)
|
imdbId = TextField(default='""', null=True)
|
||||||
|
@ -296,51 +323,185 @@ def init_db():
|
||||||
|
|
||||||
|
|
||||||
def migrate_db():
|
def migrate_db():
|
||||||
migrate(
|
table_shows = [t.name for t in database.get_columns('table_shows')]
|
||||||
migrator.add_column('table_shows', 'year', TextField(null=True)),
|
table_episodes = [t.name for t in database.get_columns('table_episodes')]
|
||||||
migrator.add_column('table_shows', 'alternateTitles', TextField(null=True)),
|
table_movies = [t.name for t in database.get_columns('table_movies')]
|
||||||
migrator.add_column('table_shows', 'tags', TextField(default='[]', null=True)),
|
table_history = [t.name for t in database.get_columns('table_history')]
|
||||||
migrator.add_column('table_shows', 'seriesType', TextField(default='""', null=True)),
|
table_history_movie = [t.name for t in database.get_columns('table_history_movie')]
|
||||||
migrator.add_column('table_shows', 'imdbId', TextField(default='""', null=True)),
|
table_languages_profiles = [t.name for t in database.get_columns('table_languages_profiles')]
|
||||||
migrator.add_column('table_shows', 'profileId', IntegerField(null=True)),
|
if "year" not in table_shows:
|
||||||
migrator.add_column('table_shows', 'monitored', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'year', TextField(null=True)))
|
||||||
migrator.add_column('table_episodes', 'format', TextField(null=True)),
|
if "alternativeTitle" not in table_shows:
|
||||||
migrator.add_column('table_episodes', 'resolution', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'alternativeTitle', TextField(null=True)))
|
||||||
migrator.add_column('table_episodes', 'video_codec', TextField(null=True)),
|
if "tags" not in table_shows:
|
||||||
migrator.add_column('table_episodes', 'audio_codec', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'tags', TextField(default='[]', null=True)))
|
||||||
migrator.add_column('table_episodes', 'episode_file_id', IntegerField(null=True)),
|
if "seriesType" not in table_shows:
|
||||||
migrator.add_column('table_episodes', 'audio_language', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'seriesType', TextField(default='""', null=True)))
|
||||||
migrator.add_column('table_episodes', 'file_size', IntegerField(default=0, null=True)),
|
if "imdbId" not in table_shows:
|
||||||
migrator.add_column('table_episodes', 'ffprobe_cache', BlobField(null=True)),
|
migrate(migrator.add_column('table_shows', 'imdbId', TextField(default='""', null=True)))
|
||||||
migrator.add_column('table_movies', 'sortTitle', TextField(null=True)),
|
if "profileId" not in table_shows:
|
||||||
migrator.add_column('table_movies', 'year', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'profileId', IntegerField(null=True)))
|
||||||
migrator.add_column('table_movies', 'alternativeTitles', TextField(null=True)),
|
if "profileId" not in table_shows:
|
||||||
migrator.add_column('table_movies', 'format', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'profileId', IntegerField(null=True)))
|
||||||
migrator.add_column('table_movies', 'resolution', TextField(null=True)),
|
if "monitored" not in table_shows:
|
||||||
migrator.add_column('table_movies', 'video_codec', TextField(null=True)),
|
migrate(migrator.add_column('table_shows', 'monitored', TextField(null=True)))
|
||||||
migrator.add_column('table_movies', 'audio_codec', TextField(null=True)),
|
|
||||||
migrator.add_column('table_movies', 'imdbId', TextField(null=True)),
|
if "format" not in table_episodes:
|
||||||
migrator.add_column('table_movies', 'movie_file_id', IntegerField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'format', TextField(null=True)))
|
||||||
migrator.add_column('table_movies', 'tags', TextField(default='[]', null=True)),
|
if "resolution" not in table_episodes:
|
||||||
migrator.add_column('table_movies', 'profileId', IntegerField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'resolution', TextField(null=True)))
|
||||||
migrator.add_column('table_movies', 'file_size', IntegerField(default=0, null=True)),
|
if "video_codec" not in table_episodes:
|
||||||
migrator.add_column('table_movies', 'ffprobe_cache', BlobField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'video_codec', TextField(null=True)))
|
||||||
migrator.add_column('table_history', 'video_path', TextField(null=True)),
|
if "audio_codec" not in table_episodes:
|
||||||
migrator.add_column('table_history', 'language', TextField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'audio_codec', TextField(null=True)))
|
||||||
migrator.add_column('table_history', 'provider', TextField(null=True)),
|
if "episode_file_id" not in table_episodes:
|
||||||
migrator.add_column('table_history', 'score', TextField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'episode_file_id', IntegerField(null=True)))
|
||||||
migrator.add_column('table_history', 'subs_id', TextField(null=True)),
|
if "audio_language" not in table_episodes:
|
||||||
migrator.add_column('table_history', 'subtitles_path', TextField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'audio_language', TextField(null=True)))
|
||||||
migrator.add_column('table_history_movie', 'video_path', TextField(null=True)),
|
if "file_size" not in table_episodes:
|
||||||
migrator.add_column('table_history_movie', 'language', TextField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'file_size', BigIntegerField(default=0, null=True)))
|
||||||
migrator.add_column('table_history_movie', 'provider', TextField(null=True)),
|
if "ffprobe_cache" not in table_episodes:
|
||||||
migrator.add_column('table_history_movie', 'score', TextField(null=True)),
|
migrate(migrator.add_column('table_episodes', 'ffprobe_cache', BlobField(null=True)))
|
||||||
migrator.add_column('table_history_movie', 'subs_id', TextField(null=True)),
|
|
||||||
migrator.add_column('table_history_movie', 'subtitles_path', TextField(null=True)),
|
if "sortTitle" not in table_movies:
|
||||||
migrator.add_column('table_languages_profiles', 'mustContain', TextField(null=True)),
|
migrate(migrator.add_column('table_movies', 'sortTitle', TextField(null=True)))
|
||||||
migrator.add_column('table_languages_profiles', 'mustNotContain', TextField(null=True)),
|
if "year" not in table_movies:
|
||||||
migrator.add_column('table_languages_profiles', 'originalFormat', BooleanField(null=True)),
|
migrate(migrator.add_column('table_movies', 'year', TextField(null=True)))
|
||||||
)
|
if "alternativeTitles" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'alternativeTitles', TextField(null=True)))
|
||||||
|
if "format" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'format', TextField(null=True)))
|
||||||
|
if "resolution" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'resolution', TextField(null=True)))
|
||||||
|
if "video_codec" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'video_codec', TextField(null=True)))
|
||||||
|
if "audio_codec" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'audio_codec', TextField(null=True)))
|
||||||
|
if "imdbId" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'imdbId', TextField(null=True)))
|
||||||
|
if "movie_file_id" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'movie_file_id', IntegerField(null=True)))
|
||||||
|
if "tags" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'tags', TextField(default='[]', null=True)))
|
||||||
|
if "profileId" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'profileId', IntegerField(null=True)))
|
||||||
|
if "file_size" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'file_size', BigIntegerField(default=0, null=True)))
|
||||||
|
if "ffprobe_cache" not in table_movies:
|
||||||
|
migrate(migrator.add_column('table_movies', 'ffprobe_cache', BlobField(null=True)))
|
||||||
|
|
||||||
|
if "video_path" not in table_history:
|
||||||
|
migrate(migrator.add_column('table_history', 'video_path', TextField(null=True)))
|
||||||
|
if "language" not in table_history:
|
||||||
|
migrate(migrator.add_column('table_history', 'language', TextField(null=True)))
|
||||||
|
if "provider" not in table_history:
|
||||||
|
migrate(migrator.add_column('table_history', 'provider', TextField(null=True)))
|
||||||
|
if "score" not in table_history:
|
||||||
|
migrate(migrator.add_column('table_history', 'score', TextField(null=True)))
|
||||||
|
if "subs_id" not in table_history:
|
||||||
|
migrate(migrator.add_column('table_history', 'subs_id', TextField(null=True)))
|
||||||
|
if "subtitles_path" not in table_history:
|
||||||
|
migrate(migrator.add_column('table_history', 'subtitles_path', TextField(null=True)))
|
||||||
|
|
||||||
|
if "video_path" not in table_history_movie:
|
||||||
|
migrate(migrator.add_column('table_history_movie', 'video_path', TextField(null=True)))
|
||||||
|
if "language" not in table_history_movie:
|
||||||
|
migrate(migrator.add_column('table_history_movie', 'language', TextField(null=True)))
|
||||||
|
if "provider" not in table_history_movie:
|
||||||
|
migrate(migrator.add_column('table_history_movie', 'provider', TextField(null=True)))
|
||||||
|
if "score" not in table_history_movie:
|
||||||
|
migrate(migrator.add_column('table_history_movie', 'score', TextField(null=True)))
|
||||||
|
if "subs_id" not in table_history_movie:
|
||||||
|
migrate(migrator.add_column('table_history_movie', 'subs_id', TextField(null=True)))
|
||||||
|
if "subtitles_path" not in table_history_movie:
|
||||||
|
migrate(migrator.add_column('table_history_movie', 'subtitles_path', TextField(null=True)))
|
||||||
|
|
||||||
|
if "mustContain" not in table_languages_profiles:
|
||||||
|
migrate(migrator.add_column('table_languages_profiles', 'mustContain', TextField(null=True)))
|
||||||
|
if "mustNotContain" not in table_languages_profiles:
|
||||||
|
migrate(migrator.add_column('table_languages_profiles', 'mustNotContain', TextField(null=True)))
|
||||||
|
if "originalFormat" not in table_languages_profiles:
|
||||||
|
migrate(migrator.add_column('table_languages_profiles', 'originalFormat', BooleanField(null=True)))
|
||||||
|
|
||||||
|
if "languages" in table_shows:
|
||||||
|
migrate(migrator.drop_column('table_shows', 'languages'))
|
||||||
|
if "hearing_impaired" in table_shows:
|
||||||
|
migrate(migrator.drop_column('table_shows', 'hearing_impaired'))
|
||||||
|
|
||||||
|
if "languages" in table_movies:
|
||||||
|
migrate(migrator.drop_column('table_movies', 'languages'))
|
||||||
|
if "hearing_impaired" in table_movies:
|
||||||
|
migrate(migrator.drop_column('table_movies', 'hearing_impaired'))
|
||||||
|
if not any(
|
||||||
|
x
|
||||||
|
for x in database.get_columns('table_blacklist')
|
||||||
|
if x.name == "timestamp" and x.data_type in ["DATETIME", "timestamp without time zone"]
|
||||||
|
):
|
||||||
|
migrate(migrator.alter_column_type('table_blacklist', 'timestamp', DateTimeField(default=datetime.now)))
|
||||||
|
update = TableBlacklist.select()
|
||||||
|
for item in update:
|
||||||
|
item.update({"timestamp": datetime.fromtimestamp(int(item.timestamp))}).execute()
|
||||||
|
|
||||||
|
if not any(
|
||||||
|
x
|
||||||
|
for x in database.get_columns('table_blacklist_movie')
|
||||||
|
if x.name == "timestamp" and x.data_type in ["DATETIME", "timestamp without time zone"]
|
||||||
|
):
|
||||||
|
migrate(migrator.alter_column_type('table_blacklist_movie', 'timestamp', DateTimeField(default=datetime.now)))
|
||||||
|
update = TableBlacklistMovie.select()
|
||||||
|
for item in update:
|
||||||
|
item.update({"timestamp": datetime.fromtimestamp(int(item.timestamp))}).execute()
|
||||||
|
|
||||||
|
if not any(
|
||||||
|
x for x in database.get_columns('table_history') if x.name == "score" and x.data_type.lower() == "integer"):
|
||||||
|
migrate(migrator.alter_column_type('table_history', 'score', IntegerField(null=True)))
|
||||||
|
if not any(
|
||||||
|
x
|
||||||
|
for x in database.get_columns('table_history')
|
||||||
|
if x.name == "timestamp" and x.data_type in ["DATETIME", "timestamp without time zone"]
|
||||||
|
):
|
||||||
|
migrate(migrator.alter_column_type('table_history', 'timestamp', DateTimeField(default=datetime.now)))
|
||||||
|
update = TableHistory.select()
|
||||||
|
list_to_update = []
|
||||||
|
for i, item in enumerate(update):
|
||||||
|
item.timestamp = datetime.fromtimestamp(int(item.timestamp))
|
||||||
|
list_to_update.append(item)
|
||||||
|
if i % 100 == 0:
|
||||||
|
TableHistory.bulk_update(list_to_update, fields=[TableHistory.timestamp])
|
||||||
|
list_to_update = []
|
||||||
|
if list_to_update:
|
||||||
|
TableHistory.bulk_update(list_to_update, fields=[TableHistory.timestamp])
|
||||||
|
|
||||||
|
if not any(x for x in database.get_columns('table_history_movie') if
|
||||||
|
x.name == "score" and x.data_type.lower() == "integer"):
|
||||||
|
migrate(migrator.alter_column_type('table_history_movie', 'score', IntegerField(null=True)))
|
||||||
|
if not any(
|
||||||
|
x
|
||||||
|
for x in database.get_columns('table_history_movie')
|
||||||
|
if x.name == "timestamp" and x.data_type in ["DATETIME", "timestamp without time zone"]
|
||||||
|
):
|
||||||
|
migrate(migrator.alter_column_type('table_history_movie', 'timestamp', DateTimeField(default=datetime.now)))
|
||||||
|
update = TableHistoryMovie.select()
|
||||||
|
list_to_update = []
|
||||||
|
for i, item in enumerate(update):
|
||||||
|
item.timestamp = datetime.fromtimestamp(int(item.timestamp))
|
||||||
|
list_to_update.append(item)
|
||||||
|
if i % 100 == 0:
|
||||||
|
TableHistoryMovie.bulk_update(list_to_update, fields=[TableHistoryMovie.timestamp])
|
||||||
|
list_to_update = []
|
||||||
|
if list_to_update:
|
||||||
|
TableHistoryMovie.bulk_update(list_to_update, fields=[TableHistoryMovie.timestamp])
|
||||||
|
# if not any(x for x in database.get_columns('table_movies') if x.name == "monitored" and x.data_type == "BOOLEAN"):
|
||||||
|
# migrate(migrator.alter_column_type('table_movies', 'monitored', BooleanField(null=True)))
|
||||||
|
|
||||||
|
if database.get_columns('table_settings_providers'):
|
||||||
|
database.execute_sql('drop table if exists table_settings_providers;')
|
||||||
|
|
||||||
|
if "alternateTitles" in table_shows:
|
||||||
|
migrate(migrator.rename_column('table_shows', 'alternateTitles', "alternativeTitles"))
|
||||||
|
|
||||||
|
if "scene_name" in table_episodes:
|
||||||
|
migrate(migrator.rename_column('table_episodes', 'scene_name', "sceneName"))
|
||||||
|
|
||||||
|
|
||||||
class SqliteDictPathMapper:
|
class SqliteDictPathMapper:
|
||||||
|
@ -376,21 +537,21 @@ def get_exclusion_clause(exclusion_type):
|
||||||
if exclusion_type == 'series':
|
if exclusion_type == 'series':
|
||||||
tagsList = ast.literal_eval(settings.sonarr.excluded_tags)
|
tagsList = ast.literal_eval(settings.sonarr.excluded_tags)
|
||||||
for tag in tagsList:
|
for tag in tagsList:
|
||||||
where_clause.append(~(TableShows.tags.contains("\'"+tag+"\'")))
|
where_clause.append(~(TableShows.tags.contains("\'" + tag + "\'")))
|
||||||
else:
|
else:
|
||||||
tagsList = ast.literal_eval(settings.radarr.excluded_tags)
|
tagsList = ast.literal_eval(settings.radarr.excluded_tags)
|
||||||
for tag in tagsList:
|
for tag in tagsList:
|
||||||
where_clause.append(~(TableMovies.tags.contains("\'"+tag+"\'")))
|
where_clause.append(~(TableMovies.tags.contains("\'" + tag + "\'")))
|
||||||
|
|
||||||
if exclusion_type == 'series':
|
if exclusion_type == 'series':
|
||||||
monitoredOnly = settings.sonarr.getboolean('only_monitored')
|
monitoredOnly = settings.sonarr.getboolean('only_monitored')
|
||||||
if monitoredOnly:
|
if monitoredOnly:
|
||||||
where_clause.append((TableEpisodes.monitored == 'True'))
|
where_clause.append((TableEpisodes.monitored == True)) # noqa E712
|
||||||
where_clause.append((TableShows.monitored == 'True'))
|
where_clause.append((TableShows.monitored == True)) # noqa E712
|
||||||
else:
|
else:
|
||||||
monitoredOnly = settings.radarr.getboolean('only_monitored')
|
monitoredOnly = settings.radarr.getboolean('only_monitored')
|
||||||
if monitoredOnly:
|
if monitoredOnly:
|
||||||
where_clause.append((TableMovies.monitored == 'True'))
|
where_clause.append((TableMovies.monitored == True)) # noqa E712
|
||||||
|
|
||||||
if exclusion_type == 'series':
|
if exclusion_type == 'series':
|
||||||
typesList = get_array_from(settings.sonarr.excluded_series_types)
|
typesList = get_array_from(settings.sonarr.excluded_series_types)
|
||||||
|
@ -404,6 +565,7 @@ def get_exclusion_clause(exclusion_type):
|
||||||
return where_clause
|
return where_clause
|
||||||
|
|
||||||
|
|
||||||
|
@region.cache_on_arguments()
|
||||||
def update_profile_id_list():
|
def update_profile_id_list():
|
||||||
profile_id_list = TableLanguagesProfiles.select(TableLanguagesProfiles.profileId,
|
profile_id_list = TableLanguagesProfiles.select(TableLanguagesProfiles.profileId,
|
||||||
TableLanguagesProfiles.name,
|
TableLanguagesProfiles.name,
|
||||||
|
@ -487,21 +649,12 @@ def get_profile_cutoff(profile_id):
|
||||||
return cutoff_language
|
return cutoff_language
|
||||||
|
|
||||||
|
|
||||||
def get_audio_profile_languages(series_id=None, episode_id=None, movie_id=None):
|
def get_audio_profile_languages(audio_languages_list_str):
|
||||||
from languages.get_languages import alpha2_from_language, alpha3_from_language
|
from languages.get_languages import alpha2_from_language, alpha3_from_language
|
||||||
audio_languages = []
|
audio_languages = []
|
||||||
|
|
||||||
if series_id:
|
|
||||||
audio_languages_list_str = TableShows.get(TableShows.sonarrSeriesId == series_id).audio_language
|
|
||||||
elif episode_id:
|
|
||||||
audio_languages_list_str = TableEpisodes.get(TableEpisodes.sonarrEpisodeId == episode_id).audio_language
|
|
||||||
elif movie_id:
|
|
||||||
audio_languages_list_str = TableMovies.get(TableMovies.radarrId == movie_id).audio_language
|
|
||||||
else:
|
|
||||||
return audio_languages
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
audio_languages_list = ast.literal_eval(audio_languages_list_str)
|
audio_languages_list = ast.literal_eval(audio_languages_list_str or '[]')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -517,22 +670,22 @@ def get_audio_profile_languages(series_id=None, episode_id=None, movie_id=None):
|
||||||
|
|
||||||
def get_profile_id(series_id=None, episode_id=None, movie_id=None):
|
def get_profile_id(series_id=None, episode_id=None, movie_id=None):
|
||||||
if series_id:
|
if series_id:
|
||||||
data = TableShows.select(TableShows.profileId)\
|
data = TableShows.select(TableShows.profileId) \
|
||||||
.where(TableShows.sonarrSeriesId == series_id)\
|
.where(TableShows.sonarrSeriesId == series_id) \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
if data:
|
if data:
|
||||||
return data.profileId
|
return data.profileId
|
||||||
elif episode_id:
|
elif episode_id:
|
||||||
data = TableShows.select(TableShows.profileId)\
|
data = TableShows.select(TableShows.profileId) \
|
||||||
.join(TableEpisodes, on=(TableShows.sonarrSeriesId == TableEpisodes.sonarrSeriesId))\
|
.join(TableEpisodes, on=(TableShows.sonarrSeriesId == TableEpisodes.sonarrSeriesId)) \
|
||||||
.where(TableEpisodes.sonarrEpisodeId == episode_id)\
|
.where(TableEpisodes.sonarrEpisodeId == episode_id) \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
if data:
|
if data:
|
||||||
return data.profileId
|
return data.profileId
|
||||||
|
|
||||||
elif movie_id:
|
elif movie_id:
|
||||||
data = TableMovies.select(TableMovies.profileId)\
|
data = TableMovies.select(TableMovies.profileId) \
|
||||||
.where(TableMovies.radarrId == movie_id)\
|
.where(TableMovies.radarrId == movie_id) \
|
||||||
.get_or_none()
|
.get_or_none()
|
||||||
if data:
|
if data:
|
||||||
return data.profileId
|
return data.profileId
|
||||||
|
|
|
@ -65,7 +65,7 @@ import logging # noqa E402
|
||||||
def is_virtualenv():
|
def is_virtualenv():
|
||||||
# return True if Bazarr have been start from within a virtualenv or venv
|
# return True if Bazarr have been start from within a virtualenv or venv
|
||||||
base_prefix = getattr(sys, "base_prefix", None)
|
base_prefix = getattr(sys, "base_prefix", None)
|
||||||
# real_prefix will return None if not in a virtualenv enviroment or the default python path
|
# real_prefix will return None if not in a virtualenv environment or the default python path
|
||||||
real_prefix = getattr(sys, "real_prefix", None) or sys.prefix
|
real_prefix = getattr(sys, "real_prefix", None) or sys.prefix
|
||||||
return base_prefix != real_prefix
|
return base_prefix != real_prefix
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import time
|
from datetime import datetime
|
||||||
|
|
||||||
from app.database import TableBlacklistMovie
|
from app.database import TableBlacklistMovie
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
|
@ -19,7 +19,7 @@ def get_blacklist_movie():
|
||||||
def blacklist_log_movie(radarr_id, provider, subs_id, language):
|
def blacklist_log_movie(radarr_id, provider, subs_id, language):
|
||||||
TableBlacklistMovie.insert({
|
TableBlacklistMovie.insert({
|
||||||
TableBlacklistMovie.radarr_id: radarr_id,
|
TableBlacklistMovie.radarr_id: radarr_id,
|
||||||
TableBlacklistMovie.timestamp: time.time(),
|
TableBlacklistMovie.timestamp: datetime.now(),
|
||||||
TableBlacklistMovie.provider: provider,
|
TableBlacklistMovie.provider: provider,
|
||||||
TableBlacklistMovie.subs_id: subs_id,
|
TableBlacklistMovie.subs_id: subs_id,
|
||||||
TableBlacklistMovie.language: language
|
TableBlacklistMovie.language: language
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import time
|
from datetime import datetime
|
||||||
|
|
||||||
from app.database import TableHistoryMovie
|
from app.database import TableHistoryMovie
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
|
@ -11,7 +11,7 @@ def history_log_movie(action, radarr_id, description, video_path=None, language=
|
||||||
TableHistoryMovie.insert({
|
TableHistoryMovie.insert({
|
||||||
TableHistoryMovie.action: action,
|
TableHistoryMovie.action: action,
|
||||||
TableHistoryMovie.radarrId: radarr_id,
|
TableHistoryMovie.radarrId: radarr_id,
|
||||||
TableHistoryMovie.timestamp: time.time(),
|
TableHistoryMovie.timestamp: datetime.now(),
|
||||||
TableHistoryMovie.description: description,
|
TableHistoryMovie.description: description,
|
||||||
TableHistoryMovie.video_path: video_path,
|
TableHistoryMovie.video_path: video_path,
|
||||||
TableHistoryMovie.language: language,
|
TableHistoryMovie.language: language,
|
||||||
|
|
|
@ -147,12 +147,12 @@ def update_movies(send_event=True):
|
||||||
# Insert new movies in DB
|
# Insert new movies in DB
|
||||||
for added_movie in movies_to_add:
|
for added_movie in movies_to_add:
|
||||||
try:
|
try:
|
||||||
result = TableMovies.insert(added_movie).on_conflict(action='IGNORE').execute()
|
result = TableMovies.insert(added_movie).on_conflict_ignore().execute()
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
logging.error(f"BAZARR cannot insert movie {added_movie['path']} because of {e}")
|
logging.error(f"BAZARR cannot insert movie {added_movie['path']} because of {e}")
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if result > 0:
|
if result and result > 0:
|
||||||
altered_movies.append([added_movie['tmdbId'],
|
altered_movies.append([added_movie['tmdbId'],
|
||||||
added_movie['path'],
|
added_movie['path'],
|
||||||
added_movie['radarrId'],
|
added_movie['radarrId'],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import time
|
from datetime import datetime
|
||||||
|
|
||||||
from app.database import TableBlacklist
|
from app.database import TableBlacklist
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
|
@ -20,7 +20,7 @@ def blacklist_log(sonarr_series_id, sonarr_episode_id, provider, subs_id, langua
|
||||||
TableBlacklist.insert({
|
TableBlacklist.insert({
|
||||||
TableBlacklist.sonarr_series_id: sonarr_series_id,
|
TableBlacklist.sonarr_series_id: sonarr_series_id,
|
||||||
TableBlacklist.sonarr_episode_id: sonarr_episode_id,
|
TableBlacklist.sonarr_episode_id: sonarr_episode_id,
|
||||||
TableBlacklist.timestamp: time.time(),
|
TableBlacklist.timestamp: datetime.now(),
|
||||||
TableBlacklist.provider: provider,
|
TableBlacklist.provider: provider,
|
||||||
TableBlacklist.subs_id: subs_id,
|
TableBlacklist.subs_id: subs_id,
|
||||||
TableBlacklist.language: language
|
TableBlacklist.language: language
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
|
||||||
import time
|
from datetime import datetime
|
||||||
|
|
||||||
from app.database import TableHistory
|
from app.database import TableHistory
|
||||||
from app.event_handler import event_stream
|
from app.event_handler import event_stream
|
||||||
|
@ -12,7 +12,7 @@ def history_log(action, sonarr_series_id, sonarr_episode_id, description, video_
|
||||||
TableHistory.action: action,
|
TableHistory.action: action,
|
||||||
TableHistory.sonarrSeriesId: sonarr_series_id,
|
TableHistory.sonarrSeriesId: sonarr_series_id,
|
||||||
TableHistory.sonarrEpisodeId: sonarr_episode_id,
|
TableHistory.sonarrEpisodeId: sonarr_episode_id,
|
||||||
TableHistory.timestamp: time.time(),
|
TableHistory.timestamp: datetime.now(),
|
||||||
TableHistory.description: description,
|
TableHistory.description: description,
|
||||||
TableHistory.video_path: video_path,
|
TableHistory.video_path: video_path,
|
||||||
TableHistory.language: language,
|
TableHistory.language: language,
|
||||||
|
|
|
@ -119,7 +119,7 @@ def sync_episodes(series_id=None, send_event=True):
|
||||||
TableEpisodes.path,
|
TableEpisodes.path,
|
||||||
TableEpisodes.season,
|
TableEpisodes.season,
|
||||||
TableEpisodes.episode,
|
TableEpisodes.episode,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableEpisodes.monitored,
|
TableEpisodes.monitored,
|
||||||
TableEpisodes.format,
|
TableEpisodes.format,
|
||||||
TableEpisodes.resolution,
|
TableEpisodes.resolution,
|
||||||
|
@ -149,12 +149,12 @@ def sync_episodes(series_id=None, send_event=True):
|
||||||
# Insert new episodes in DB
|
# Insert new episodes in DB
|
||||||
for added_episode in episodes_to_add:
|
for added_episode in episodes_to_add:
|
||||||
try:
|
try:
|
||||||
result = TableEpisodes.insert(added_episode).on_conflict(action='IGNORE').execute()
|
result = TableEpisodes.insert(added_episode).on_conflict_ignore().execute()
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
logging.error(f"BAZARR cannot insert episode {added_episode['path']} because of {e}")
|
logging.error(f"BAZARR cannot insert episode {added_episode['path']} because of {e}")
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if result > 0:
|
if result and result > 0:
|
||||||
altered_episodes.append([added_episode['sonarrEpisodeId'],
|
altered_episodes.append([added_episode['sonarrEpisodeId'],
|
||||||
added_episode['path'],
|
added_episode['path'],
|
||||||
added_episode['monitored']])
|
added_episode['monitored']])
|
||||||
|
|
|
@ -49,7 +49,7 @@ def seriesParser(show, action, tags_dict, serie_default_profile, audio_profiles)
|
||||||
'audio_language': str(audio_language),
|
'audio_language': str(audio_language),
|
||||||
'sortTitle': show['sortTitle'],
|
'sortTitle': show['sortTitle'],
|
||||||
'year': str(show['year']),
|
'year': str(show['year']),
|
||||||
'alternateTitles': alternate_titles,
|
'alternativeTitles': alternate_titles,
|
||||||
'tags': str(tags),
|
'tags': str(tags),
|
||||||
'seriesType': show['seriesType'],
|
'seriesType': show['seriesType'],
|
||||||
'imdbId': imdbId,
|
'imdbId': imdbId,
|
||||||
|
@ -65,7 +65,7 @@ def seriesParser(show, action, tags_dict, serie_default_profile, audio_profiles)
|
||||||
'audio_language': str(audio_language),
|
'audio_language': str(audio_language),
|
||||||
'sortTitle': show['sortTitle'],
|
'sortTitle': show['sortTitle'],
|
||||||
'year': str(show['year']),
|
'year': str(show['year']),
|
||||||
'alternateTitles': alternate_titles,
|
'alternativeTitles': alternate_titles,
|
||||||
'tags': str(tags),
|
'tags': str(tags),
|
||||||
'seriesType': show['seriesType'],
|
'seriesType': show['seriesType'],
|
||||||
'imdbId': imdbId,
|
'imdbId': imdbId,
|
||||||
|
@ -141,7 +141,7 @@ def episodeParser(episode):
|
||||||
'path': episode['episodeFile']['path'],
|
'path': episode['episodeFile']['path'],
|
||||||
'season': episode['seasonNumber'],
|
'season': episode['seasonNumber'],
|
||||||
'episode': episode['episodeNumber'],
|
'episode': episode['episodeNumber'],
|
||||||
'scene_name': sceneName,
|
'sceneName': sceneName,
|
||||||
'monitored': str(bool(episode['monitored'])),
|
'monitored': str(bool(episode['monitored'])),
|
||||||
'format': video_format,
|
'format': video_format,
|
||||||
'resolution': video_resolution,
|
'resolution': video_resolution,
|
||||||
|
|
|
@ -97,7 +97,7 @@ def update_series(send_event=True):
|
||||||
TableShows.audio_language,
|
TableShows.audio_language,
|
||||||
TableShows.sortTitle,
|
TableShows.sortTitle,
|
||||||
TableShows.year,
|
TableShows.year,
|
||||||
TableShows.alternateTitles,
|
TableShows.alternativeTitles,
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableShows.seriesType,
|
TableShows.seriesType,
|
||||||
TableShows.imdbId,
|
TableShows.imdbId,
|
||||||
|
|
|
@ -42,7 +42,7 @@ def movies_download_subtitles(no):
|
||||||
else:
|
else:
|
||||||
count_movie = 0
|
count_movie = 0
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
|
audio_language_list = get_audio_profile_languages(movie['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -26,7 +26,7 @@ def series_download_subtitles(no):
|
||||||
TableEpisodes.missing_subtitles,
|
TableEpisodes.missing_subtitles,
|
||||||
TableEpisodes.monitored,
|
TableEpisodes.monitored,
|
||||||
TableEpisodes.sonarrEpisodeId,
|
TableEpisodes.sonarrEpisodeId,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableShows.seriesType,
|
TableShows.seriesType,
|
||||||
TableEpisodes.audio_language,
|
TableEpisodes.audio_language,
|
||||||
|
@ -57,7 +57,7 @@ def series_download_subtitles(no):
|
||||||
value=i,
|
value=i,
|
||||||
count=count_episodes_details)
|
count=count_episodes_details)
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
audio_language_list = get_audio_profile_languages(episode['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
@ -76,7 +76,7 @@ def series_download_subtitles(no):
|
||||||
for result in generate_subtitles(path_mappings.path_replace(episode['path']),
|
for result in generate_subtitles(path_mappings.path_replace(episode['path']),
|
||||||
languages,
|
languages,
|
||||||
audio_language,
|
audio_language,
|
||||||
str(episode['scene_name']),
|
str(episode['sceneName']),
|
||||||
episode['title'],
|
episode['title'],
|
||||||
'series',
|
'series',
|
||||||
check_if_still_required=True):
|
check_if_still_required=True):
|
||||||
|
@ -112,7 +112,7 @@ def episode_download_subtitles(no, send_progress=False):
|
||||||
TableEpisodes.missing_subtitles,
|
TableEpisodes.missing_subtitles,
|
||||||
TableEpisodes.monitored,
|
TableEpisodes.monitored,
|
||||||
TableEpisodes.sonarrEpisodeId,
|
TableEpisodes.sonarrEpisodeId,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableShows.title,
|
TableShows.title,
|
||||||
TableShows.sonarrSeriesId,
|
TableShows.sonarrSeriesId,
|
||||||
|
@ -142,7 +142,7 @@ def episode_download_subtitles(no, send_progress=False):
|
||||||
value=0,
|
value=0,
|
||||||
count=1)
|
count=1)
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
audio_language_list = get_audio_profile_languages(episode['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
@ -161,7 +161,7 @@ def episode_download_subtitles(no, send_progress=False):
|
||||||
for result in generate_subtitles(path_mappings.path_replace(episode['path']),
|
for result in generate_subtitles(path_mappings.path_replace(episode['path']),
|
||||||
languages,
|
languages,
|
||||||
audio_language,
|
audio_language,
|
||||||
str(episode['scene_name']),
|
str(episode['sceneName']),
|
||||||
episode['title'],
|
episode['title'],
|
||||||
'series',
|
'series',
|
||||||
check_if_still_required=True):
|
check_if_still_required=True):
|
||||||
|
|
|
@ -23,7 +23,7 @@ def refine_from_db(path, video):
|
||||||
TableEpisodes.title.alias('episodeTitle'),
|
TableEpisodes.title.alias('episodeTitle'),
|
||||||
TableShows.year,
|
TableShows.year,
|
||||||
TableShows.tvdbId,
|
TableShows.tvdbId,
|
||||||
TableShows.alternateTitles,
|
TableShows.alternativeTitles,
|
||||||
TableEpisodes.format,
|
TableEpisodes.format,
|
||||||
TableEpisodes.resolution,
|
TableEpisodes.resolution,
|
||||||
TableEpisodes.video_codec,
|
TableEpisodes.video_codec,
|
||||||
|
@ -47,7 +47,7 @@ def refine_from_db(path, video):
|
||||||
video.year = int(data['year'])
|
video.year = int(data['year'])
|
||||||
|
|
||||||
video.series_tvdb_id = int(data['tvdbId'])
|
video.series_tvdb_id = int(data['tvdbId'])
|
||||||
video.alternative_series = ast.literal_eval(data['alternateTitles'])
|
video.alternative_series = ast.literal_eval(data['alternativeTitles'])
|
||||||
if data['imdbId'] and not video.series_imdb_id:
|
if data['imdbId'] and not video.series_imdb_id:
|
||||||
video.series_imdb_id = data['imdbId']
|
video.series_imdb_id = data['imdbId']
|
||||||
if not video.source:
|
if not video.source:
|
||||||
|
|
|
@ -26,8 +26,7 @@ from .download import generate_subtitles
|
||||||
|
|
||||||
def upgrade_subtitles():
|
def upgrade_subtitles():
|
||||||
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
days_to_upgrade_subs = settings.general.days_to_upgrade_subs
|
||||||
minimum_timestamp = ((datetime.now() - timedelta(days=int(days_to_upgrade_subs))) -
|
minimum_timestamp = (datetime.now() - timedelta(days=int(days_to_upgrade_subs)))
|
||||||
datetime(1970, 1, 1)).total_seconds()
|
|
||||||
|
|
||||||
if settings.general.getboolean('upgrade_manual'):
|
if settings.general.getboolean('upgrade_manual'):
|
||||||
query_actions = [1, 2, 3, 4, 6]
|
query_actions = [1, 2, 3, 4, 6]
|
||||||
|
@ -41,11 +40,11 @@ def upgrade_subtitles():
|
||||||
upgradable_episodes_conditions += get_exclusion_clause('series')
|
upgradable_episodes_conditions += get_exclusion_clause('series')
|
||||||
upgradable_episodes = TableHistory.select(TableHistory.video_path,
|
upgradable_episodes = TableHistory.select(TableHistory.video_path,
|
||||||
TableHistory.language,
|
TableHistory.language,
|
||||||
TableHistory.score,
|
fn.MAX(TableHistory.score).alias('score'),
|
||||||
TableShows.tags,
|
TableShows.tags,
|
||||||
TableShows.profileId,
|
TableShows.profileId,
|
||||||
TableEpisodes.audio_language,
|
TableEpisodes.audio_language,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableEpisodes.title,
|
TableEpisodes.title,
|
||||||
TableEpisodes.sonarrSeriesId,
|
TableEpisodes.sonarrSeriesId,
|
||||||
TableHistory.action,
|
TableHistory.action,
|
||||||
|
@ -56,11 +55,26 @@ def upgrade_subtitles():
|
||||||
TableEpisodes.season,
|
TableEpisodes.season,
|
||||||
TableEpisodes.episode,
|
TableEpisodes.episode,
|
||||||
TableShows.title.alias('seriesTitle'),
|
TableShows.title.alias('seriesTitle'),
|
||||||
TableShows.seriesType)\
|
TableShows.seriesType) \
|
||||||
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId)) \
|
||||||
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\
|
.join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId)) \
|
||||||
.where(reduce(operator.and_, upgradable_episodes_conditions))\
|
.where(reduce(operator.and_, upgradable_episodes_conditions)) \
|
||||||
.group_by(TableHistory.video_path, TableHistory.language)\
|
.group_by(TableHistory.video_path,
|
||||||
|
TableHistory.language,
|
||||||
|
TableShows.tags,
|
||||||
|
TableShows.profileId,
|
||||||
|
TableEpisodes.audio_language,
|
||||||
|
TableEpisodes.sceneName,
|
||||||
|
TableEpisodes.title,
|
||||||
|
TableEpisodes.sonarrSeriesId,
|
||||||
|
TableHistory.action,
|
||||||
|
TableHistory.subtitles_path,
|
||||||
|
TableEpisodes.sonarrEpisodeId,
|
||||||
|
TableEpisodes.monitored,
|
||||||
|
TableEpisodes.season,
|
||||||
|
TableEpisodes.episode,
|
||||||
|
TableShows.title.alias('seriesTitle'),
|
||||||
|
TableShows.seriesType) \
|
||||||
.dicts()
|
.dicts()
|
||||||
upgradable_episodes_not_perfect = []
|
upgradable_episodes_not_perfect = []
|
||||||
for upgradable_episode in upgradable_episodes:
|
for upgradable_episode in upgradable_episodes:
|
||||||
|
@ -90,7 +104,7 @@ def upgrade_subtitles():
|
||||||
upgradable_movies_conditions += get_exclusion_clause('movie')
|
upgradable_movies_conditions += get_exclusion_clause('movie')
|
||||||
upgradable_movies = TableHistoryMovie.select(TableHistoryMovie.video_path,
|
upgradable_movies = TableHistoryMovie.select(TableHistoryMovie.video_path,
|
||||||
TableHistoryMovie.language,
|
TableHistoryMovie.language,
|
||||||
TableHistoryMovie.score,
|
fn.MAX(TableHistoryMovie.score).alias('score'),
|
||||||
TableMovies.profileId,
|
TableMovies.profileId,
|
||||||
TableHistoryMovie.action,
|
TableHistoryMovie.action,
|
||||||
TableHistoryMovie.subtitles_path,
|
TableHistoryMovie.subtitles_path,
|
||||||
|
@ -100,10 +114,21 @@ def upgrade_subtitles():
|
||||||
TableMovies.monitored,
|
TableMovies.monitored,
|
||||||
TableMovies.tags,
|
TableMovies.tags,
|
||||||
TableMovies.radarrId,
|
TableMovies.radarrId,
|
||||||
TableMovies.title)\
|
TableMovies.title) \
|
||||||
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\
|
.join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId)) \
|
||||||
.where(reduce(operator.and_, upgradable_movies_conditions))\
|
.where(reduce(operator.and_, upgradable_movies_conditions)) \
|
||||||
.group_by(TableHistoryMovie.video_path, TableHistoryMovie.language)\
|
.group_by(TableHistoryMovie.video_path,
|
||||||
|
TableHistoryMovie.language,
|
||||||
|
TableMovies.profileId,
|
||||||
|
TableHistoryMovie.action,
|
||||||
|
TableHistoryMovie.subtitles_path,
|
||||||
|
TableMovies.audio_language,
|
||||||
|
TableMovies.sceneName,
|
||||||
|
TableMovies.monitored,
|
||||||
|
TableMovies.tags,
|
||||||
|
TableMovies.radarrId,
|
||||||
|
TableMovies.title
|
||||||
|
) \
|
||||||
.dicts()
|
.dicts()
|
||||||
upgradable_movies_not_perfect = []
|
upgradable_movies_not_perfect = []
|
||||||
for upgradable_movie in upgradable_movies:
|
for upgradable_movie in upgradable_movies:
|
||||||
|
@ -155,7 +180,7 @@ def upgrade_subtitles():
|
||||||
is_forced = "False"
|
is_forced = "False"
|
||||||
is_hi = "False"
|
is_hi = "False"
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
audio_language_list = get_audio_profile_languages(episode['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
@ -164,7 +189,7 @@ def upgrade_subtitles():
|
||||||
result = list(generate_subtitles(path_mappings.path_replace(episode['video_path']),
|
result = list(generate_subtitles(path_mappings.path_replace(episode['video_path']),
|
||||||
[(language, is_hi, is_forced)],
|
[(language, is_hi, is_forced)],
|
||||||
audio_language,
|
audio_language,
|
||||||
str(episode['scene_name']),
|
str(episode['sceneName']),
|
||||||
episode['seriesTitle'],
|
episode['seriesTitle'],
|
||||||
'series',
|
'series',
|
||||||
forced_minimum_score=int(episode['score']),
|
forced_minimum_score=int(episode['score']),
|
||||||
|
@ -218,7 +243,7 @@ def upgrade_subtitles():
|
||||||
is_forced = "False"
|
is_forced = "False"
|
||||||
is_hi = "False"
|
is_hi = "False"
|
||||||
|
|
||||||
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
|
audio_language_list = get_audio_profile_languages(movie['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
@ -249,7 +274,8 @@ def upgrade_subtitles():
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles_movie(movie['video_path'],
|
store_subtitles_movie(movie['video_path'],
|
||||||
path_mappings.path_replace_movie(movie['video_path']))
|
path_mappings.path_replace_movie(movie['video_path']))
|
||||||
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score, subs_id, subs_path)
|
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score, subs_id,
|
||||||
|
subs_path)
|
||||||
send_notifications_movie(movie['radarrId'], message)
|
send_notifications_movie(movie['radarrId'], message)
|
||||||
|
|
||||||
hide_progress(id='upgrade_movies_progress')
|
hide_progress(id='upgrade_movies_progress')
|
||||||
|
|
|
@ -20,7 +20,7 @@ from ..download import generate_subtitles
|
||||||
|
|
||||||
|
|
||||||
def _wanted_movie(movie):
|
def _wanted_movie(movie):
|
||||||
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
|
audio_language_list = get_audio_profile_languages(movie['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -20,7 +20,7 @@ from ..download import generate_subtitles
|
||||||
|
|
||||||
|
|
||||||
def _wanted_episode(episode):
|
def _wanted_episode(episode):
|
||||||
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
|
audio_language_list = get_audio_profile_languages(episode['audio_language'])
|
||||||
if len(audio_language_list) > 0:
|
if len(audio_language_list) > 0:
|
||||||
audio_language = audio_language_list[0]['name']
|
audio_language = audio_language_list[0]['name']
|
||||||
else:
|
else:
|
||||||
|
@ -47,7 +47,7 @@ def _wanted_episode(episode):
|
||||||
for result in generate_subtitles(path_mappings.path_replace(episode['path']),
|
for result in generate_subtitles(path_mappings.path_replace(episode['path']),
|
||||||
languages,
|
languages,
|
||||||
audio_language,
|
audio_language,
|
||||||
str(episode['scene_name']),
|
str(episode['sceneName']),
|
||||||
episode['title'],
|
episode['title'],
|
||||||
'series',
|
'series',
|
||||||
check_if_still_required=True):
|
check_if_still_required=True):
|
||||||
|
@ -56,9 +56,9 @@ def _wanted_episode(episode):
|
||||||
path = result[1]
|
path = result[1]
|
||||||
forced = result[5]
|
forced = result[5]
|
||||||
if result[8]:
|
if result[8]:
|
||||||
language_code = result[2] + ":hi"
|
language_code = f"{result[2]}:hi"
|
||||||
elif forced:
|
elif forced:
|
||||||
language_code = result[2] + ":forced"
|
language_code = f"{result[2]}:forced"
|
||||||
else:
|
else:
|
||||||
language_code = result[2]
|
language_code = result[2]
|
||||||
provider = result[3]
|
provider = result[3]
|
||||||
|
@ -79,7 +79,7 @@ def wanted_download_subtitles(sonarr_episode_id):
|
||||||
TableEpisodes.sonarrEpisodeId,
|
TableEpisodes.sonarrEpisodeId,
|
||||||
TableEpisodes.sonarrSeriesId,
|
TableEpisodes.sonarrSeriesId,
|
||||||
TableEpisodes.audio_language,
|
TableEpisodes.audio_language,
|
||||||
TableEpisodes.scene_name,
|
TableEpisodes.sceneName,
|
||||||
TableEpisodes.failedAttempts,
|
TableEpisodes.failedAttempts,
|
||||||
TableShows.title)\
|
TableShows.title)\
|
||||||
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
.join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
|
||||||
|
|
|
@ -47,27 +47,29 @@ def get_backup_files(fullpath=True):
|
||||||
|
|
||||||
def backup_to_zip():
|
def backup_to_zip():
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
database_backup_file = None
|
||||||
now_string = now.strftime("%Y.%m.%d_%H.%M.%S")
|
now_string = now.strftime("%Y.%m.%d_%H.%M.%S")
|
||||||
backup_filename = f"bazarr_backup_v{os.environ['BAZARR_VERSION']}_{now_string}.zip"
|
backup_filename = f"bazarr_backup_v{os.environ['BAZARR_VERSION']}_{now_string}.zip"
|
||||||
logging.debug(f'Backup filename will be: {backup_filename}')
|
logging.debug(f'Backup filename will be: {backup_filename}')
|
||||||
|
|
||||||
database_src_file = os.path.join(args.config_dir, 'db', 'bazarr.db')
|
if not settings.postgresql.getboolean('enabled'):
|
||||||
logging.debug(f'Database file path to backup is: {database_src_file}')
|
database_src_file = os.path.join(args.config_dir, 'db', 'bazarr.db')
|
||||||
|
logging.debug(f'Database file path to backup is: {database_src_file}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
database_src_con = sqlite3.connect(database_src_file)
|
database_src_con = sqlite3.connect(database_src_file)
|
||||||
|
|
||||||
database_backup_file = os.path.join(get_backup_path(), 'bazarr_temp.db')
|
database_backup_file = os.path.join(get_backup_path(), 'bazarr_temp.db')
|
||||||
database_backup_con = sqlite3.connect(database_backup_file)
|
database_backup_con = sqlite3.connect(database_backup_file)
|
||||||
|
|
||||||
with database_backup_con:
|
with database_backup_con:
|
||||||
database_src_con.backup(database_backup_con)
|
database_src_con.backup(database_backup_con)
|
||||||
|
|
||||||
database_backup_con.close()
|
database_backup_con.close()
|
||||||
database_src_con.close()
|
database_src_con.close()
|
||||||
except Exception:
|
except Exception:
|
||||||
database_backup_file = None
|
database_backup_file = None
|
||||||
logging.exception('Unable to backup database file.')
|
logging.exception('Unable to backup database file.')
|
||||||
|
|
||||||
config_file = os.path.join(args.config_dir, 'config', 'config.ini')
|
config_file = os.path.join(args.config_dir, 'config', 'config.ini')
|
||||||
logging.debug(f'Config file path to backup is: {config_file}')
|
logging.debug(f'Config file path to backup is: {config_file}')
|
||||||
|
@ -75,15 +77,14 @@ def backup_to_zip():
|
||||||
with ZipFile(os.path.join(get_backup_path(), backup_filename), 'w') as backupZip:
|
with ZipFile(os.path.join(get_backup_path(), backup_filename), 'w') as backupZip:
|
||||||
if database_backup_file:
|
if database_backup_file:
|
||||||
backupZip.write(database_backup_file, 'bazarr.db')
|
backupZip.write(database_backup_file, 'bazarr.db')
|
||||||
|
try:
|
||||||
|
os.remove(database_backup_file)
|
||||||
|
except OSError:
|
||||||
|
logging.exception(f'Unable to delete temporary database backup file: {database_backup_file}')
|
||||||
else:
|
else:
|
||||||
logging.debug('Database file is not included in backup. See previous exception')
|
logging.debug('Database file is not included in backup. See previous exception')
|
||||||
backupZip.write(config_file, 'config.ini')
|
backupZip.write(config_file, 'config.ini')
|
||||||
|
|
||||||
try:
|
|
||||||
os.remove(database_backup_file)
|
|
||||||
except OSError:
|
|
||||||
logging.exception(f'Unable to delete temporary database backup file: {database_backup_file}')
|
|
||||||
|
|
||||||
|
|
||||||
def restore_from_backup():
|
def restore_from_backup():
|
||||||
restore_config_path = os.path.join(get_restore_path(), 'config.ini')
|
restore_config_path = os.path.join(get_restore_path(), 'config.ini')
|
||||||
|
@ -97,30 +98,34 @@ def restore_from_backup():
|
||||||
os.remove(restore_config_path)
|
os.remove(restore_config_path)
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.exception(f'Unable to restore or delete config.ini to {dest_config_path}')
|
logging.exception(f'Unable to restore or delete config.ini to {dest_config_path}')
|
||||||
|
if not settings.postgresql.getboolean('enabled'):
|
||||||
try:
|
|
||||||
shutil.copy(restore_database_path, dest_database_path)
|
|
||||||
os.remove(restore_database_path)
|
|
||||||
except OSError:
|
|
||||||
logging.exception(f'Unable to restore or delete db to {dest_database_path}')
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
if os.path.isfile(dest_database_path + '-shm'):
|
shutil.copy(restore_database_path, dest_database_path)
|
||||||
os.remove(dest_database_path + '-shm')
|
os.remove(restore_database_path)
|
||||||
if os.path.isfile(dest_database_path + '-wal'):
|
|
||||||
os.remove(dest_database_path + '-wal')
|
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.exception('Unable to delete SHM and WAL file.')
|
logging.exception(f'Unable to restore or delete db to {dest_database_path}')
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
if os.path.isfile(f'{dest_database_path}-shm'):
|
||||||
|
os.remove(f'{dest_database_path}-shm')
|
||||||
|
if os.path.isfile(f'{dest_database_path}-wal'):
|
||||||
|
os.remove(f'{dest_database_path}-wal')
|
||||||
|
except OSError:
|
||||||
|
logging.exception('Unable to delete SHM and WAL file.')
|
||||||
|
try:
|
||||||
|
os.remove(restore_database_path)
|
||||||
|
except OSError:
|
||||||
|
logging.exception(f'Unable to delete {dest_database_path}')
|
||||||
|
|
||||||
logging.info('Backup restored successfully. Bazarr will restart.')
|
logging.info('Backup restored successfully. Bazarr will restart.')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
restart_file = io.open(os.path.join(args.config_dir, "bazarr.restart"), "w", encoding='UTF-8')
|
restart_file = io.open(os.path.join(args.config_dir, "bazarr.restart"), "w", encoding='UTF-8')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error('BAZARR Cannot create restart file: ' + repr(e))
|
logging.error(f'BAZARR Cannot create restart file: {repr(e)}')
|
||||||
else:
|
else:
|
||||||
logging.info('Bazarr is being restarted...')
|
logging.info('Bazarr is being restarted...')
|
||||||
restart_file.write(str(''))
|
restart_file.write('')
|
||||||
restart_file.close()
|
restart_file.close()
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
elif os.path.isfile(restore_config_path) or os.path.isfile(restore_database_path):
|
elif os.path.isfile(restore_config_path) or os.path.isfile(restore_database_path):
|
||||||
|
@ -134,11 +139,6 @@ def restore_from_backup():
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.exception(f'Unable to delete {dest_config_path}')
|
logging.exception(f'Unable to delete {dest_config_path}')
|
||||||
|
|
||||||
try:
|
|
||||||
os.remove(restore_database_path)
|
|
||||||
except OSError:
|
|
||||||
logging.exception(f'Unable to delete {dest_database_path}')
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_restore(filename):
|
def prepare_restore(filename):
|
||||||
src_zip_file_path = os.path.join(get_backup_path(), filename)
|
src_zip_file_path = os.path.join(get_backup_path(), filename)
|
||||||
|
|
Loading…
Reference in New Issue