mirror of https://github.com/morpheus65535/bazarr
Cache ffprobe results
This commit is contained in:
parent
72c9899f58
commit
dcbd7f004c
|
@ -721,7 +721,7 @@ class EpisodesSubtitles(Resource):
|
||||||
history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id,
|
history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id,
|
||||||
subs_path)
|
subs_path)
|
||||||
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
||||||
store_subtitles(path, episodePath)
|
store_subtitles(path, episodePath, 'episode', sonarrEpisodeId)
|
||||||
else:
|
else:
|
||||||
event_stream(type='episode', action='update', series=int(sonarrSeriesId), episode=int(sonarrEpisodeId))
|
event_stream(type='episode', action='update', series=int(sonarrSeriesId), episode=int(sonarrEpisodeId))
|
||||||
|
|
||||||
|
@ -777,7 +777,7 @@ class EpisodesSubtitles(Resource):
|
||||||
subtitles_path=subs_path)
|
subtitles_path=subs_path)
|
||||||
if not settings.general.getboolean('dont_notify_manual_actions'):
|
if not settings.general.getboolean('dont_notify_manual_actions'):
|
||||||
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
||||||
store_subtitles(path, episodePath)
|
store_subtitles(path, episodePath, 'episode', sonarrEpisodeId)
|
||||||
|
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
@ -927,7 +927,7 @@ class MoviesSubtitles(Resource):
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
history_log_movie(1, radarrId, message, path, language_code, provider, score, subs_id, subs_path)
|
history_log_movie(1, radarrId, message, path, language_code, provider, score, subs_id, subs_path)
|
||||||
send_notifications_movie(radarrId, message)
|
send_notifications_movie(radarrId, message)
|
||||||
store_subtitles_movie(path, moviePath)
|
store_subtitles_movie(path, moviePath, 'movie', radarrId)
|
||||||
else:
|
else:
|
||||||
event_stream(type='movie', action='update', movie=int(radarrId))
|
event_stream(type='movie', action='update', movie=int(radarrId))
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -982,7 +982,7 @@ class MoviesSubtitles(Resource):
|
||||||
history_log_movie(4, radarrId, message, path, language_code, provider, score, subtitles_path=subs_path)
|
history_log_movie(4, radarrId, message, path, language_code, provider, score, subtitles_path=subs_path)
|
||||||
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, 'movie', radarrId)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1109,7 +1109,7 @@ class ProviderMovies(Resource):
|
||||||
history_log_movie(2, radarrId, message, path, language_code, provider, score, subs_id, subs_path)
|
history_log_movie(2, radarrId, message, path, language_code, provider, score, subs_id, subs_path)
|
||||||
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, 'movie', radarrId)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1192,7 +1192,7 @@ class ProviderEpisodes(Resource):
|
||||||
subs_path)
|
subs_path)
|
||||||
if not settings.general.getboolean('dont_notify_manual_actions'):
|
if not settings.general.getboolean('dont_notify_manual_actions'):
|
||||||
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
send_notifications(sonarrSeriesId, sonarrEpisodeId, message)
|
||||||
store_subtitles(path, episodePath)
|
store_subtitles(path, episodePath, 'episode', sonarrEpisodeId)
|
||||||
return result, 201
|
return result, 201
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
@ -1654,9 +1654,9 @@ class Subtitles(Resource):
|
||||||
forced=forced, hi=hi)
|
forced=forced, hi=hi)
|
||||||
if result:
|
if result:
|
||||||
if media_type == 'episode':
|
if media_type == 'episode':
|
||||||
store_subtitles(path_mappings.path_replace_reverse(video_path), video_path)
|
store_subtitles(path_mappings.path_replace_reverse(video_path), video_path, 'episode', id)
|
||||||
else:
|
else:
|
||||||
store_subtitles_movie(path_mappings.path_replace_reverse_movie(video_path), video_path)
|
store_subtitles_movie(path_mappings.path_replace_reverse_movie(video_path), video_path, 'movie', id)
|
||||||
return '', 200
|
return '', 200
|
||||||
else:
|
else:
|
||||||
return '', 404
|
return '', 404
|
||||||
|
|
|
@ -108,6 +108,8 @@ def db_upgrade():
|
||||||
['table_episodes', 'audio_codec', 'text'],
|
['table_episodes', 'audio_codec', 'text'],
|
||||||
['table_episodes', 'episode_file_id', 'integer'],
|
['table_episodes', 'episode_file_id', 'integer'],
|
||||||
['table_episodes', 'audio_language', 'text'],
|
['table_episodes', 'audio_language', 'text'],
|
||||||
|
['table_episodes', 'file_size', 'integer', '0'],
|
||||||
|
['table_episodes', 'file_ffprobe', 'text'],
|
||||||
['table_movies', 'sortTitle', 'text'],
|
['table_movies', 'sortTitle', 'text'],
|
||||||
['table_movies', 'year', 'text'],
|
['table_movies', 'year', 'text'],
|
||||||
['table_movies', 'alternativeTitles', 'text'],
|
['table_movies', 'alternativeTitles', 'text'],
|
||||||
|
@ -120,6 +122,8 @@ def db_upgrade():
|
||||||
['table_movies', 'movie_file_id', 'integer'],
|
['table_movies', 'movie_file_id', 'integer'],
|
||||||
['table_movies', 'tags', 'text', '[]'],
|
['table_movies', 'tags', 'text', '[]'],
|
||||||
['table_movies', 'profileId', 'integer'],
|
['table_movies', 'profileId', 'integer'],
|
||||||
|
['table_movies', 'file_ffprobe', 'text'],
|
||||||
|
['table_movies', 'file_size', 'integer', '0'],
|
||||||
['table_history', 'video_path', 'text'],
|
['table_history', 'video_path', 'text'],
|
||||||
['table_history', 'language', 'text'],
|
['table_history', 'language', 'text'],
|
||||||
['table_history', 'provider', 'text'],
|
['table_history', 'provider', 'text'],
|
||||||
|
|
|
@ -10,21 +10,28 @@ from knowit import api
|
||||||
class EmbeddedSubsReader:
|
class EmbeddedSubsReader:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ffprobe = None
|
self.ffprobe = None
|
||||||
|
self.cache = None
|
||||||
|
self.data = None
|
||||||
|
|
||||||
|
def list_languages(self, file, original_path, record_type=None, record_id=None):
|
||||||
|
from utils import get_binary, cache_get_ffprobe, cache_save_ffprobe
|
||||||
|
|
||||||
|
self.cache = cache_get_ffprobe(original_path, record_type, record_id)
|
||||||
|
if self.cache['ffprobe'] is not None:
|
||||||
|
return self.cache['ffprobe']
|
||||||
|
|
||||||
def list_languages(self, file):
|
|
||||||
from utils import get_binary
|
|
||||||
self.ffprobe = get_binary("ffprobe")
|
self.ffprobe = get_binary("ffprobe")
|
||||||
|
|
||||||
subtitles_list = []
|
subtitles_list = []
|
||||||
if self.ffprobe:
|
if self.ffprobe:
|
||||||
api.initialize({'provider': 'ffmpeg', 'ffmpeg': self.ffprobe})
|
api.initialize({'provider': 'ffmpeg', 'ffmpeg': self.ffprobe})
|
||||||
data = api.know(file)
|
self.data = api.know(file)
|
||||||
|
|
||||||
traditional_chinese = ["cht", "tc", "traditional", "zht", "hant", "big5", u"繁", u"雙語"]
|
traditional_chinese = ["cht", "tc", "traditional", "zht", "hant", "big5", u"繁", u"雙語"]
|
||||||
brazilian_portuguese = ["pt-br", "pob", "pb", "brazilian", "brasil", "brazil"]
|
brazilian_portuguese = ["pt-br", "pob", "pb", "brazilian", "brasil", "brazil"]
|
||||||
|
|
||||||
if 'subtitle' in data:
|
if 'subtitle' in self.data:
|
||||||
for detected_language in data['subtitle']:
|
for detected_language in self.data['subtitle']:
|
||||||
if 'language' in detected_language:
|
if 'language' in detected_language:
|
||||||
language = detected_language['language'].alpha3
|
language = detected_language['language'].alpha3
|
||||||
if language == 'zho' and 'name' in detected_language:
|
if language == 'zho' and 'name' in detected_language:
|
||||||
|
@ -44,11 +51,11 @@ class EmbeddedSubsReader:
|
||||||
if os.path.splitext(file)[1] == '.mkv':
|
if os.path.splitext(file)[1] == '.mkv':
|
||||||
with open(file, 'rb') as f:
|
with open(file, 'rb') as f:
|
||||||
try:
|
try:
|
||||||
mkv = enzyme.MKV(f)
|
self.data = enzyme.MKV(f)
|
||||||
except MalformedMKVError:
|
except MalformedMKVError:
|
||||||
logging.error('BAZARR cannot analyze this MKV with our built-in MKV parser, you should install ffmpeg: ' + file)
|
logging.error('BAZARR cannot analyze this MKV with our built-in MKV parser, you should install ffmpeg: ' + file)
|
||||||
else:
|
else:
|
||||||
for subtitle_track in mkv.subtitle_tracks:
|
for subtitle_track in self.data.subtitle_tracks:
|
||||||
hearing_impaired = False
|
hearing_impaired = False
|
||||||
if subtitle_track.name:
|
if subtitle_track.name:
|
||||||
if 'sdh' in subtitle_track.name.lower():
|
if 'sdh' in subtitle_track.name.lower():
|
||||||
|
@ -56,6 +63,9 @@ class EmbeddedSubsReader:
|
||||||
subtitles_list.append([subtitle_track.language, subtitle_track.forced, hearing_impaired,
|
subtitles_list.append([subtitle_track.language, subtitle_track.forced, hearing_impaired,
|
||||||
subtitle_track.codec_id])
|
subtitle_track.codec_id])
|
||||||
|
|
||||||
|
if self.cache['type'] and self.cache['id']:
|
||||||
|
cache_save_ffprobe(original_path, self.cache['type'], self.cache['id'], subtitles_list)
|
||||||
|
|
||||||
return subtitles_list
|
return subtitles_list
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import os
|
||||||
import requests
|
import requests
|
||||||
import logging
|
import logging
|
||||||
from database import database, dict_converter, get_exclusion_clause
|
from database import database, dict_converter, get_exclusion_clause
|
||||||
|
from utils import cache_is_valid
|
||||||
from config import settings, url_sonarr
|
from config import settings, url_sonarr
|
||||||
from helper import path_mappings
|
from helper import path_mappings
|
||||||
from list_subtitles import store_subtitles, series_full_scan_subtitles
|
from list_subtitles import store_subtitles, series_full_scan_subtitles
|
||||||
|
@ -24,7 +24,7 @@ def sync_episodes():
|
||||||
apikey_sonarr = settings.sonarr.apikey
|
apikey_sonarr = settings.sonarr.apikey
|
||||||
|
|
||||||
# Get current episodes id in DB
|
# Get current episodes id in DB
|
||||||
current_episodes_db = database.execute("SELECT sonarrEpisodeId, path, sonarrSeriesId FROM table_episodes")
|
current_episodes_db = database.execute("SELECT sonarrEpisodeId FROM table_episodes")
|
||||||
|
|
||||||
current_episodes_db_list = [x['sonarrEpisodeId'] for x in current_episodes_db]
|
current_episodes_db_list = [x['sonarrEpisodeId'] for x in current_episodes_db]
|
||||||
|
|
||||||
|
@ -66,6 +66,11 @@ def sync_episodes():
|
||||||
else:
|
else:
|
||||||
sceneName = None
|
sceneName = None
|
||||||
|
|
||||||
|
if 'size' in episode['episodeFile']:
|
||||||
|
fileSize = int(episode['episodeFile']['size'])
|
||||||
|
else:
|
||||||
|
fileSize = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
format, resolution = episode['episodeFile']['quality']['quality']['name'].split('-')
|
format, resolution = episode['episodeFile']['quality']['quality']['name'].split('-')
|
||||||
except:
|
except:
|
||||||
|
@ -79,12 +84,14 @@ def sync_episodes():
|
||||||
if 'videoCodec' in episode['episodeFile']['mediaInfo']:
|
if 'videoCodec' in episode['episodeFile']['mediaInfo']:
|
||||||
videoCodec = episode['episodeFile']['mediaInfo']['videoCodec']
|
videoCodec = episode['episodeFile']['mediaInfo']['videoCodec']
|
||||||
videoCodec = SonarrFormatVideoCodec(videoCodec)
|
videoCodec = SonarrFormatVideoCodec(videoCodec)
|
||||||
else: videoCodec = None
|
else:
|
||||||
|
videoCodec = None
|
||||||
|
|
||||||
if 'audioCodec' in episode['episodeFile']['mediaInfo']:
|
if 'audioCodec' in episode['episodeFile']['mediaInfo']:
|
||||||
audioCodec = episode['episodeFile']['mediaInfo']['audioCodec']
|
audioCodec = episode['episodeFile']['mediaInfo']['audioCodec']
|
||||||
audioCodec = SonarrFormatAudioCodec(audioCodec)
|
audioCodec = SonarrFormatAudioCodec(audioCodec)
|
||||||
else: audioCodec = None
|
else:
|
||||||
|
audioCodec = None
|
||||||
else:
|
else:
|
||||||
videoCodec = None
|
videoCodec = None
|
||||||
audioCodec = None
|
audioCodec = None
|
||||||
|
@ -103,36 +110,34 @@ def sync_episodes():
|
||||||
# Add episodes in sonarr to current episode list
|
# Add episodes in sonarr to current episode list
|
||||||
current_episodes_sonarr.append(episode['id'])
|
current_episodes_sonarr.append(episode['id'])
|
||||||
|
|
||||||
|
info = {
|
||||||
|
'sonarrSeriesId': episode['seriesId'],
|
||||||
|
'sonarrEpisodeId': episode['id'],
|
||||||
|
'title': episode['title'],
|
||||||
|
'path': episode['episodeFile']['path'],
|
||||||
|
'season': episode['seasonNumber'],
|
||||||
|
'episode': episode['episodeNumber'],
|
||||||
|
'scene_name': sceneName,
|
||||||
|
'monitored': str(bool(episode['monitored'])),
|
||||||
|
'format': format,
|
||||||
|
'resolution': resolution,
|
||||||
|
'video_codec': videoCodec,
|
||||||
|
'audio_codec': audioCodec,
|
||||||
|
'episode_file_id': episode['episodeFile']['id'],
|
||||||
|
'audio_language': str(audio_language),
|
||||||
|
'file_size': fileSize,
|
||||||
|
}
|
||||||
|
|
||||||
if episode['id'] in current_episodes_db_list:
|
if episode['id'] in current_episodes_db_list:
|
||||||
episodes_to_update.append({'sonarrSeriesId': episode['seriesId'],
|
if not cache_is_valid(info['path'], info['file_size'], 'episode', info['sonarrEpisodeId']):
|
||||||
'sonarrEpisodeId': episode['id'],
|
logging.debug('Path and/or Size is not the same. Invalidating ffprobe cache data for: [%s:%s, %s].',
|
||||||
'title': episode['title'],
|
'episode', info['sonarrEpisodeId'], info['path'])
|
||||||
'path': episode['episodeFile']['path'],
|
info.update({'file_ffprobe': None})
|
||||||
'season': episode['seasonNumber'],
|
|
||||||
'episode': episode['episodeNumber'],
|
episodes_to_update.append(info)
|
||||||
'scene_name': sceneName,
|
|
||||||
'monitored': str(bool(episode['monitored'])),
|
|
||||||
'format': format,
|
|
||||||
'resolution': resolution,
|
|
||||||
'video_codec': videoCodec,
|
|
||||||
'audio_codec': audioCodec,
|
|
||||||
'episode_file_id': episode['episodeFile']['id'],
|
|
||||||
'audio_language': str(audio_language)})
|
|
||||||
else:
|
else:
|
||||||
episodes_to_add.append({'sonarrSeriesId': episode['seriesId'],
|
info.update({'file_ffprobe': None})
|
||||||
'sonarrEpisodeId': episode['id'],
|
episodes_to_add.append(info)
|
||||||
'title': episode['title'],
|
|
||||||
'path': episode['episodeFile']['path'],
|
|
||||||
'season': episode['seasonNumber'],
|
|
||||||
'episode': episode['episodeNumber'],
|
|
||||||
'scene_name': sceneName,
|
|
||||||
'monitored': str(bool(episode['monitored'])),
|
|
||||||
'format': format,
|
|
||||||
'resolution': resolution,
|
|
||||||
'video_codec': videoCodec,
|
|
||||||
'audio_codec': audioCodec,
|
|
||||||
'episode_file_id': episode['episodeFile']['id'],
|
|
||||||
'audio_language': str(audio_language)})
|
|
||||||
|
|
||||||
# Remove old episodes from DB
|
# Remove old episodes from DB
|
||||||
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))
|
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))
|
||||||
|
@ -148,7 +153,7 @@ def sync_episodes():
|
||||||
episode_in_db_list = []
|
episode_in_db_list = []
|
||||||
episodes_in_db = database.execute("SELECT sonarrSeriesId, sonarrEpisodeId, title, path, season, episode, "
|
episodes_in_db = database.execute("SELECT sonarrSeriesId, sonarrEpisodeId, title, path, season, episode, "
|
||||||
"scene_name, monitored, format, resolution, video_codec, audio_codec, "
|
"scene_name, monitored, format, resolution, video_codec, audio_codec, "
|
||||||
"episode_file_id, audio_language FROM table_episodes")
|
"episode_file_id, audio_language, file_ffprobe FROM table_episodes")
|
||||||
|
|
||||||
for item in episodes_in_db:
|
for item in episodes_in_db:
|
||||||
episode_in_db_list.append(item)
|
episode_in_db_list.append(item)
|
||||||
|
@ -180,7 +185,8 @@ def sync_episodes():
|
||||||
|
|
||||||
# Store subtitles for added or modified episodes
|
# Store subtitles for added or modified episodes
|
||||||
for i, altered_episode in enumerate(altered_episodes, 1):
|
for i, altered_episode in enumerate(altered_episodes, 1):
|
||||||
store_subtitles(altered_episode[1], path_mappings.path_replace(altered_episode[1]))
|
store_subtitles(altered_episode[1], path_mappings.path_replace(altered_episode[1]),
|
||||||
|
'episode', altered_episode[0])
|
||||||
|
|
||||||
logging.debug('BAZARR All episodes synced from Sonarr into database.')
|
logging.debug('BAZARR All episodes synced from Sonarr into database.')
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
||||||
|
|
||||||
from config import settings, url_radarr
|
from config import settings, url_radarr
|
||||||
from helper import path_mappings
|
from helper import path_mappings
|
||||||
from utils import get_radarr_version
|
from utils import get_radarr_version, cache_is_valid
|
||||||
from list_subtitles import store_subtitles_movie, movies_full_scan_subtitles
|
from list_subtitles import store_subtitles_movie, movies_full_scan_subtitles
|
||||||
|
|
||||||
from get_subtitle import movies_download_subtitles
|
from get_subtitle import movies_download_subtitles
|
||||||
|
@ -63,7 +63,7 @@ def update_movies():
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Get current movies in DB
|
# Get current movies in DB
|
||||||
current_movies_db = database.execute("SELECT tmdbId, path, radarrId FROM table_movies")
|
current_movies_db = database.execute("SELECT tmdbId FROM table_movies")
|
||||||
|
|
||||||
current_movies_db_list = [x['tmdbId'] for x in current_movies_db]
|
current_movies_db_list = [x['tmdbId'] for x in current_movies_db]
|
||||||
|
|
||||||
|
@ -102,6 +102,11 @@ def update_movies():
|
||||||
else:
|
else:
|
||||||
sceneName = None
|
sceneName = None
|
||||||
|
|
||||||
|
if 'size' in movie['movieFile']:
|
||||||
|
fileSize = movie['movieFile']['size']
|
||||||
|
else:
|
||||||
|
fileSize = 0
|
||||||
|
|
||||||
alternativeTitles = None
|
alternativeTitles = None
|
||||||
if radarr_version.startswith('0'):
|
if radarr_version.startswith('0'):
|
||||||
if 'alternativeTitles' in movie:
|
if 'alternativeTitles' in movie:
|
||||||
|
@ -110,8 +115,10 @@ def update_movies():
|
||||||
if 'alternateTitles' in movie:
|
if 'alternateTitles' in movie:
|
||||||
alternativeTitles = str([item['title'] for item in movie['alternateTitles']])
|
alternativeTitles = str([item['title'] for item in movie['alternateTitles']])
|
||||||
|
|
||||||
if 'imdbId' in movie: imdbId = movie['imdbId']
|
if 'imdbId' in movie:
|
||||||
else: imdbId = None
|
imdbId = movie['imdbId']
|
||||||
|
else:
|
||||||
|
imdbId = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
format, resolution = movie['movieFile']['quality']['quality']['name'].split('-')
|
format, resolution = movie['movieFile']['quality']['quality']['name'].split('-')
|
||||||
|
@ -168,11 +175,11 @@ def update_movies():
|
||||||
# Add movies in radarr to current movies list
|
# Add movies in radarr to current movies list
|
||||||
current_movies_radarr.append(str(movie['tmdbId']))
|
current_movies_radarr.append(str(movie['tmdbId']))
|
||||||
|
|
||||||
if str(movie['tmdbId']) in current_movies_db_list:
|
info = {
|
||||||
movies_to_update.append({'radarrId': int(movie["id"]),
|
'radarrId': int(movie['id']),
|
||||||
'title': movie["title"],
|
'title': movie['title'],
|
||||||
'path': movie["path"] + separator + movie['movieFile']['relativePath'],
|
'path': movie['path'] + separator + movie['movieFile']['relativePath'],
|
||||||
'tmdbId': str(movie["tmdbId"]),
|
'tmdbId': str(movie['tmdbId']),
|
||||||
'poster': poster,
|
'poster': poster,
|
||||||
'fanart': fanart,
|
'fanart': fanart,
|
||||||
'audio_language': str(audio_language),
|
'audio_language': str(audio_language),
|
||||||
|
@ -188,34 +195,22 @@ def update_movies():
|
||||||
'overview': overview,
|
'overview': overview,
|
||||||
'imdbId': imdbId,
|
'imdbId': imdbId,
|
||||||
'movie_file_id': int(movie['movieFile']['id']),
|
'movie_file_id': int(movie['movieFile']['id']),
|
||||||
'tags': str(tags)})
|
|
||||||
else:
|
|
||||||
movies_to_add.append({'radarrId': int(movie["id"]),
|
|
||||||
'title': movie["title"],
|
|
||||||
'path': movie["path"] + separator + movie['movieFile']['relativePath'],
|
|
||||||
'tmdbId': str(movie["tmdbId"]),
|
|
||||||
'subtitles': '[]',
|
|
||||||
'overview': overview,
|
|
||||||
'poster': poster,
|
|
||||||
'fanart': fanart,
|
|
||||||
'audio_language': str(audio_language),
|
|
||||||
'sceneName': sceneName,
|
|
||||||
'monitored': str(bool(movie['monitored'])),
|
|
||||||
'sortTitle': movie['sortTitle'],
|
|
||||||
'year': str(movie['year']),
|
|
||||||
'alternativeTitles': alternativeTitles,
|
|
||||||
'format': format,
|
|
||||||
'resolution': resolution,
|
|
||||||
'video_codec': videoCodec,
|
|
||||||
'audio_codec': audioCodec,
|
|
||||||
'imdbId': imdbId,
|
|
||||||
'movie_file_id': int(movie['movieFile']['id']),
|
|
||||||
'tags': str(tags),
|
'tags': str(tags),
|
||||||
'profileId': movie_default_profile})
|
'file_size': fileSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
if str(movie['tmdbId']) in current_movies_db_list:
|
||||||
|
if not cache_is_valid(info['path'], info['file_size'], 'movie', info['radarrId']):
|
||||||
|
logging.debug('Path and/or Size is not the same. Invalidating ffprobe cache data for: [%s:%s, %s].',
|
||||||
|
'movie', info['radarrId'], info['path'])
|
||||||
|
info.update({'file_ffprobe': None})
|
||||||
|
|
||||||
|
movies_to_update.append(info)
|
||||||
else:
|
else:
|
||||||
logging.error(
|
info.update({'file_ffprobe': None, 'profileId': movie_default_profile})
|
||||||
'BAZARR Radarr returned a movie without a file path: ' + movie["path"] + separator +
|
movies_to_add.append(info)
|
||||||
movie['movieFile']['relativePath'])
|
else:
|
||||||
|
logging.error('BAZARR Radarr returned a movie without a file path: ' + movie["path"] + separator + movie['movieFile']['relativePath'])
|
||||||
|
|
||||||
# Remove old movies from DB
|
# Remove old movies from DB
|
||||||
removed_movies = list(set(current_movies_db_list) - set(current_movies_radarr))
|
removed_movies = list(set(current_movies_db_list) - set(current_movies_radarr))
|
||||||
|
@ -261,7 +256,8 @@ def update_movies():
|
||||||
|
|
||||||
# Store subtitles for added or modified movies
|
# Store subtitles for added or modified movies
|
||||||
for i, altered_movie in enumerate(altered_movies, 1):
|
for i, altered_movie in enumerate(altered_movies, 1):
|
||||||
store_subtitles_movie(altered_movie[1], path_mappings.path_replace_movie(altered_movie[1]))
|
store_subtitles_movie(altered_movie[1], path_mappings.path_replace_movie(altered_movie[1]),
|
||||||
|
'movie', altered_movie[2])
|
||||||
|
|
||||||
logging.debug('BAZARR All movies synced from Radarr into database.')
|
logging.debug('BAZARR All movies synced from Radarr into database.')
|
||||||
|
|
||||||
|
|
|
@ -791,7 +791,8 @@ def series_download_subtitles(no):
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']),
|
||||||
|
'episode', episode['sonarrEpisodeId'])
|
||||||
history_log(1, no, episode['sonarrEpisodeId'], message, path, language_code, provider, score,
|
history_log(1, no, episode['sonarrEpisodeId'], message, path, language_code, provider, score,
|
||||||
subs_id, subs_path)
|
subs_id, subs_path)
|
||||||
send_notifications(no, episode['sonarrEpisodeId'], message)
|
send_notifications(no, episode['sonarrEpisodeId'], message)
|
||||||
|
@ -848,7 +849,8 @@ def episode_download_subtitles(no):
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']),
|
||||||
|
'episode', episode['sonarrEpisodeId'])
|
||||||
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
||||||
language_code, provider, score, subs_id, subs_path)
|
language_code, provider, score, subs_id, subs_path)
|
||||||
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
||||||
|
@ -908,7 +910,8 @@ def movies_download_subtitles(no):
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']),
|
||||||
|
'movie', movie['radarrId'])
|
||||||
history_log_movie(1, no, message, path, language_code, provider, score, subs_id, subs_path)
|
history_log_movie(1, no, message, path, language_code, provider, score, subs_id, subs_path)
|
||||||
send_notifications_movie(no, message)
|
send_notifications_movie(no, message)
|
||||||
else:
|
else:
|
||||||
|
@ -978,7 +981,8 @@ def wanted_download_subtitles(path, l, count_episodes):
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']),
|
||||||
|
'episode', episode['sonarrEpisodeId'])
|
||||||
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
||||||
language_code, provider, score, subs_id, subs_path)
|
language_code, provider, score, subs_id, subs_path)
|
||||||
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
||||||
|
@ -1046,7 +1050,8 @@ def wanted_download_subtitles_movie(path, l, count_movies):
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']),
|
||||||
|
'movie', movie['radarrId'])
|
||||||
history_log_movie(1, movie['radarrId'], message, path, language_code, provider, score,
|
history_log_movie(1, movie['radarrId'], message, path, language_code, provider, score,
|
||||||
subs_id, subs_path)
|
subs_id, subs_path)
|
||||||
send_notifications_movie(movie['radarrId'], message)
|
send_notifications_movie(movie['radarrId'], message)
|
||||||
|
@ -1347,7 +1352,8 @@ def upgrade_subtitles():
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
subs_path = result[7]
|
subs_path = result[7]
|
||||||
store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path']))
|
store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path']),
|
||||||
|
'episode', episode['sonarrEpisodeId'])
|
||||||
history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
|
||||||
language_code, provider, score, subs_id, subs_path)
|
language_code, provider, score, subs_id, subs_path)
|
||||||
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
|
||||||
|
@ -1406,8 +1412,8 @@ def upgrade_subtitles():
|
||||||
score = result[4]
|
score = result[4]
|
||||||
subs_id = result[6]
|
subs_id = result[6]
|
||||||
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']))
|
'movie', movie['radarrId'])
|
||||||
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)
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,14 @@ global hi_regex
|
||||||
hi_regex = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](?<!{\\an\d})')
|
hi_regex = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](?<!{\\an\d})')
|
||||||
|
|
||||||
|
|
||||||
def store_subtitles(original_path, reversed_path):
|
def store_subtitles(original_path, reversed_path, record_type=None, record_id=None):
|
||||||
logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
|
logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
|
||||||
actual_subtitles = []
|
actual_subtitles = []
|
||||||
if os.path.exists(reversed_path):
|
if os.path.exists(reversed_path):
|
||||||
if settings.general.getboolean('use_embedded_subs'):
|
if settings.general.getboolean('use_embedded_subs'):
|
||||||
logging.debug("BAZARR is trying to index embedded subtitles.")
|
logging.debug("BAZARR is trying to index embedded subtitles.")
|
||||||
try:
|
try:
|
||||||
subtitle_languages = embedded_subs_reader.list_languages(reversed_path)
|
subtitle_languages = embedded_subs_reader.list_languages(reversed_path, original_path, record_type, record_id)
|
||||||
for subtitle_language, subtitle_forced, subtitle_hi, subtitle_codec in subtitle_languages:
|
for subtitle_language, subtitle_forced, subtitle_hi, subtitle_codec in subtitle_languages:
|
||||||
try:
|
try:
|
||||||
if (settings.general.getboolean("ignore_pgs_subs") and subtitle_codec.lower() == "pgs") or \
|
if (settings.general.getboolean("ignore_pgs_subs") and subtitle_codec.lower() == "pgs") or \
|
||||||
|
@ -138,14 +138,14 @@ def store_subtitles(original_path, reversed_path):
|
||||||
return actual_subtitles
|
return actual_subtitles
|
||||||
|
|
||||||
|
|
||||||
def store_subtitles_movie(original_path, reversed_path):
|
def store_subtitles_movie(original_path, reversed_path, record_type=None, record_id=None):
|
||||||
logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
|
logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
|
||||||
actual_subtitles = []
|
actual_subtitles = []
|
||||||
if os.path.exists(reversed_path):
|
if os.path.exists(reversed_path):
|
||||||
if settings.general.getboolean('use_embedded_subs'):
|
if settings.general.getboolean('use_embedded_subs'):
|
||||||
logging.debug("BAZARR is trying to index embedded subtitles.")
|
logging.debug("BAZARR is trying to index embedded subtitles.")
|
||||||
try:
|
try:
|
||||||
subtitle_languages = embedded_subs_reader.list_languages(reversed_path)
|
subtitle_languages = embedded_subs_reader.list_languages(reversed_path, original_path, record_type, record_id)
|
||||||
for subtitle_language, subtitle_forced, subtitle_hi, subtitle_codec in subtitle_languages:
|
for subtitle_language, subtitle_forced, subtitle_hi, subtitle_codec in subtitle_languages:
|
||||||
try:
|
try:
|
||||||
if (settings.general.getboolean("ignore_pgs_subs") and subtitle_codec.lower() == "pgs") or \
|
if (settings.general.getboolean("ignore_pgs_subs") and subtitle_codec.lower() == "pgs") or \
|
||||||
|
@ -370,7 +370,6 @@ def list_missing_subtitles_movies(no=None, epno=None, send_event=True):
|
||||||
logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + movies_subtitles)
|
logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + movies_subtitles)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
use_embedded_subs = settings.general.getboolean('use_embedded_subs')
|
use_embedded_subs = settings.general.getboolean('use_embedded_subs')
|
||||||
|
|
||||||
for movie_subtitles in movies_subtitles:
|
for movie_subtitles in movies_subtitles:
|
||||||
|
@ -468,36 +467,38 @@ def list_missing_subtitles_movies(no=None, epno=None, send_event=True):
|
||||||
|
|
||||||
|
|
||||||
def series_full_scan_subtitles():
|
def series_full_scan_subtitles():
|
||||||
episodes = database.execute("SELECT path FROM table_episodes")
|
episodes = database.execute("SELECT sonarrEpisodeId, path FROM table_episodes")
|
||||||
|
|
||||||
for i, episode in enumerate(episodes, 1):
|
for i, episode in enumerate(episodes, 1):
|
||||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']),
|
||||||
|
'episode', episode['sonarrEpisodeId'])
|
||||||
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
def movies_full_scan_subtitles():
|
def movies_full_scan_subtitles():
|
||||||
movies = database.execute("SELECT path FROM table_movies")
|
movies = database.execute("SELECT radarrId, path FROM table_movies")
|
||||||
|
|
||||||
for i, movie in enumerate(movies, 1):
|
for i, movie in enumerate(movies, 1):
|
||||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']),
|
||||||
|
'movie', movie['radarrId'])
|
||||||
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
def series_scan_subtitles(no):
|
def series_scan_subtitles(no):
|
||||||
episodes = database.execute("SELECT path FROM table_episodes WHERE sonarrSeriesId=? ORDER BY sonarrEpisodeId",
|
episodes = database.execute("SELECT sonarrEpisodeId, path FROM table_episodes WHERE sonarrSeriesId=? ORDER BY sonarrEpisodeId",
|
||||||
(no,))
|
(no,))
|
||||||
|
|
||||||
for episode in episodes:
|
for episode in episodes:
|
||||||
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
|
store_subtitles(episode['path'], path_mappings.path_replace(episode['path']), 'episode', episode['sonarrEpisodeId'])
|
||||||
|
|
||||||
|
|
||||||
def movies_scan_subtitles(no):
|
def movies_scan_subtitles(no):
|
||||||
movies = database.execute("SELECT path FROM table_movies WHERE radarrId=? ORDER BY radarrId", (no,))
|
movies = database.execute("SELECT radarrId, path FROM table_movies WHERE radarrId=? ORDER BY radarrId", (no,))
|
||||||
|
|
||||||
for movie in movies:
|
for movie in movies:
|
||||||
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']))
|
store_subtitles_movie(movie['path'], path_mappings.path_replace_movie(movie['path']), 'movie', movie['radarrId'])
|
||||||
|
|
||||||
|
|
||||||
def get_external_subtitles_path(file, subtitle):
|
def get_external_subtitles_path(file, subtitle):
|
||||||
|
|
|
@ -299,26 +299,30 @@ def delete_subtitles(media_type, language, forced, hi, media_path, subtitles_pat
|
||||||
os.remove(path_mappings.path_replace(subtitles_path))
|
os.remove(path_mappings.path_replace(subtitles_path))
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
|
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
|
||||||
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path)
|
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path,
|
||||||
|
'episode', sonarr_episode_id)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
history_log(0, sonarr_series_id, sonarr_episode_id, result, language=language_log,
|
history_log(0, sonarr_series_id, sonarr_episode_id, result, language=language_log,
|
||||||
video_path=path_mappings.path_replace_reverse(media_path),
|
video_path=path_mappings.path_replace_reverse(media_path),
|
||||||
subtitles_path=path_mappings.path_replace_reverse(subtitles_path))
|
subtitles_path=path_mappings.path_replace_reverse(subtitles_path))
|
||||||
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path)
|
store_subtitles(path_mappings.path_replace_reverse(media_path), media_path,
|
||||||
|
'episode', sonarr_episode_id)
|
||||||
notify_sonarr(sonarr_series_id)
|
notify_sonarr(sonarr_series_id)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
os.remove(path_mappings.path_replace_movie(subtitles_path))
|
os.remove(path_mappings.path_replace_movie(subtitles_path))
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
|
logging.exception('BAZARR cannot delete subtitles file: ' + subtitles_path)
|
||||||
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path)
|
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path,
|
||||||
|
'movie', radarr_id)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
history_log_movie(0, radarr_id, result, language=language_log,
|
history_log_movie(0, radarr_id, result, language=language_log,
|
||||||
video_path=path_mappings.path_replace_reverse_movie(media_path),
|
video_path=path_mappings.path_replace_reverse_movie(media_path),
|
||||||
subtitles_path=path_mappings.path_replace_reverse_movie(subtitles_path))
|
subtitles_path=path_mappings.path_replace_reverse_movie(subtitles_path))
|
||||||
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path)
|
store_subtitles_movie(path_mappings.path_replace_reverse_movie(media_path), media_path,
|
||||||
|
'movie', radarr_id)
|
||||||
notify_radarr(radarr_id)
|
notify_radarr(radarr_id)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -399,7 +403,66 @@ def translate_subtitles_file(video_path, source_srt_file, to_lang, forced, hi):
|
||||||
|
|
||||||
return dest_srt_file
|
return dest_srt_file
|
||||||
|
|
||||||
|
|
||||||
def check_credentials(user, pw):
|
def check_credentials(user, pw):
|
||||||
username = settings.auth.username
|
username = settings.auth.username
|
||||||
password = settings.auth.password
|
password = settings.auth.password
|
||||||
return hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username
|
return hashlib.md5(pw.encode('utf-8')).hexdigest() == password and user == username
|
||||||
|
|
||||||
|
|
||||||
|
def cache_get_ffprobe(path, record_type=None, record_id=None):
|
||||||
|
record = {
|
||||||
|
'id': record_id,
|
||||||
|
'type': record_type,
|
||||||
|
'ffprobe': None
|
||||||
|
}
|
||||||
|
|
||||||
|
if record_type == 'episode':
|
||||||
|
item = database.execute("SELECT sonarrEpisodeId, file_ffprobe FROM table_episodes WHERE sonarrEpisodeId = ? AND path = ?",
|
||||||
|
(record_id, path,), only_one=True)
|
||||||
|
if item:
|
||||||
|
record.update({'id': item['sonarrEpisodeId'], 'type': 'episode'})
|
||||||
|
|
||||||
|
if item['file_ffprobe'] is not None:
|
||||||
|
record.update({'ffprobe': json.loads(item['file_ffprobe'])})
|
||||||
|
logging.debug('Returning cached results for: [%s:%s, %s]. (%s)', record['type'], record['id'], path, record['ffprobe'])
|
||||||
|
|
||||||
|
return record
|
||||||
|
|
||||||
|
if record_type == 'movie':
|
||||||
|
item = database.execute("SELECT radarrId, file_ffprobe FROM table_movies WHERE radarrId = ? AND path = ?",
|
||||||
|
(record_id, path,), only_one=True)
|
||||||
|
if item:
|
||||||
|
record.update({'id': item['radarrId'], 'type': 'movie'})
|
||||||
|
|
||||||
|
if item['file_ffprobe'] is not None:
|
||||||
|
record.update({'ffprobe': json.loads(item['file_ffprobe'])})
|
||||||
|
logging.debug('Returning cached results for: [%s:%s, %s]. (%s)', record['type'], record['id'], path, record['ffprobe'])
|
||||||
|
|
||||||
|
return record
|
||||||
|
|
||||||
|
|
||||||
|
def cache_save_ffprobe(path, record_type, record_id, ffprobe):
|
||||||
|
if record_type == 'movie':
|
||||||
|
database.execute("UPDATE table_movies SET file_ffprobe = ? WHERE path = ? AND radarrId = ?",
|
||||||
|
(json.dumps(ffprobe), path, record_id))
|
||||||
|
|
||||||
|
if record_type == 'episode':
|
||||||
|
database.execute("UPDATE table_episodes SET file_ffprobe = ? WHERE path = ? AND sonarrEpisodeId = ?",
|
||||||
|
(json.dumps(ffprobe), path, record_id))
|
||||||
|
|
||||||
|
logging.debug('Saving ffprobe records [%s:%s, %s]. (%s)', record_type, record_id, path, ffprobe)
|
||||||
|
|
||||||
|
|
||||||
|
def cache_is_valid(path, file_size, record_type, record_id):
|
||||||
|
if record_type == 'movie':
|
||||||
|
item = database.execute("SELECT path, file_size FROM table_movies WHERE radarrId = ?",
|
||||||
|
(record_id,), only_one=True)
|
||||||
|
else:
|
||||||
|
item = database.execute("SELECT path, file_size FROM table_episodes WHERE sonarrEpisodeId = ?",
|
||||||
|
(record_id,), only_one=True)
|
||||||
|
|
||||||
|
if item:
|
||||||
|
return str(item['path']) == str(path) and int(item['file_size']) == int(file_size)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
Loading…
Reference in New Issue