You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
402 lines
16 KiB
402 lines
16 KiB
# coding=utf-8
|
|
|
|
import os
|
|
import ast
|
|
import sqlite3
|
|
import logging
|
|
import json
|
|
|
|
from sqlite3worker import Sqlite3Worker
|
|
|
|
from get_args import args
|
|
from helper import path_mappings
|
|
from config import settings
|
|
|
|
global profile_id_list
|
|
profile_id_list = []
|
|
|
|
|
|
def db_init():
|
|
if not os.path.exists(os.path.join(args.config_dir, 'db', 'bazarr.db')):
|
|
# Get SQL script from file
|
|
fd = open(os.path.join(os.path.dirname(__file__), 'create_db.sql'), 'r')
|
|
script = fd.read()
|
|
# Close SQL script file
|
|
fd.close()
|
|
# Open database connection
|
|
db = sqlite3.connect(os.path.join(args.config_dir, 'db', 'bazarr.db'), timeout=30)
|
|
c = db.cursor()
|
|
# Execute script and commit change to database
|
|
c.executescript(script)
|
|
# Close database connection
|
|
db.close()
|
|
logging.info('BAZARR Database created successfully')
|
|
|
|
|
|
database = Sqlite3Worker(os.path.join(args.config_dir, 'db', 'bazarr.db'), max_queue_size=256, as_dict=True)
|
|
|
|
|
|
class SqliteDictConverter:
|
|
def __init__(self):
|
|
self.keys_insert = tuple()
|
|
self.keys_update = tuple()
|
|
self.values = tuple()
|
|
self.question_marks = tuple()
|
|
|
|
def convert(self, values_dict):
|
|
if type(values_dict) is dict:
|
|
self.keys_insert = tuple()
|
|
self.keys_update = tuple()
|
|
self.values = tuple()
|
|
self.question_marks = tuple()
|
|
|
|
temp_keys = list()
|
|
temp_values = list()
|
|
for item in values_dict.items():
|
|
temp_keys.append(item[0])
|
|
temp_values.append(item[1])
|
|
self.keys_insert = ','.join(temp_keys)
|
|
self.keys_update = ','.join([k + '=?' for k in temp_keys])
|
|
self.values = tuple(temp_values)
|
|
self.question_marks = ','.join(list('?'*len(values_dict)))
|
|
return self
|
|
else:
|
|
pass
|
|
|
|
|
|
dict_converter = SqliteDictConverter()
|
|
|
|
|
|
class SqliteDictPathMapper:
|
|
def __init__(self):
|
|
pass
|
|
|
|
def path_replace(self, values_dict):
|
|
if type(values_dict) is list:
|
|
for item in values_dict:
|
|
item['path'] = path_mappings.path_replace(item['path'])
|
|
elif type(values_dict) is dict:
|
|
values_dict['path'] = path_mappings.path_replace(values_dict['path'])
|
|
else:
|
|
return path_mappings.path_replace(values_dict)
|
|
|
|
def path_replace_movie(self, values_dict):
|
|
if type(values_dict) is list:
|
|
for item in values_dict:
|
|
item['path'] = path_mappings.path_replace_movie(item['path'])
|
|
elif type(values_dict) is dict:
|
|
values_dict['path'] = path_mappings.path_replace_movie(values_dict['path'])
|
|
else:
|
|
return path_mappings.path_replace_movie(values_dict)
|
|
|
|
|
|
dict_mapper = SqliteDictPathMapper()
|
|
|
|
|
|
def db_upgrade():
|
|
columnToAdd = [
|
|
['table_shows', 'year', 'text'],
|
|
['table_shows', 'alternateTitles', 'text'],
|
|
['table_shows', 'forced', 'text', 'False'],
|
|
['table_shows', 'tags', 'text', '[]'],
|
|
['table_shows', 'seriesType', 'text', ''],
|
|
['table_shows', 'imdbId', 'text', ''],
|
|
['table_shows', 'profileId', 'integer'],
|
|
['table_episodes', 'format', 'text'],
|
|
['table_episodes', 'resolution', 'text'],
|
|
['table_episodes', 'video_codec', 'text'],
|
|
['table_episodes', 'audio_codec', 'text'],
|
|
['table_episodes', 'episode_file_id', 'integer'],
|
|
['table_episodes', 'audio_language', 'text'],
|
|
['table_movies', 'sortTitle', 'text'],
|
|
['table_movies', 'year', 'text'],
|
|
['table_movies', 'alternativeTitles', 'text'],
|
|
['table_movies', 'format', 'text'],
|
|
['table_movies', 'resolution', 'text'],
|
|
['table_movies', 'video_codec', 'text'],
|
|
['table_movies', 'audio_codec', 'text'],
|
|
['table_movies', 'imdbId', 'text'],
|
|
['table_movies', 'forced', 'text', 'False'],
|
|
['table_movies', 'movie_file_id', 'integer'],
|
|
['table_movies', 'tags', 'text', '[]'],
|
|
['table_movies', 'profileId', 'integer'],
|
|
['table_history', 'video_path', 'text'],
|
|
['table_history', 'language', 'text'],
|
|
['table_history', 'provider', 'text'],
|
|
['table_history', 'score', 'text'],
|
|
['table_history', 'subs_id', 'text'],
|
|
['table_history', 'subtitles_path', 'text'],
|
|
['table_history_movie', 'video_path', 'text'],
|
|
['table_history_movie', 'language', 'text'],
|
|
['table_history_movie', 'provider', 'text'],
|
|
['table_history_movie', 'score', 'text'],
|
|
['table_history_movie', 'subs_id', 'text'],
|
|
['table_history_movie', 'subtitles_path', 'text']
|
|
]
|
|
|
|
for column in columnToAdd:
|
|
try:
|
|
# Check if column already exist in table
|
|
columns_dict = database.execute('''PRAGMA table_info('{0}')'''.format(column[0]))
|
|
columns_names_list = [x['name'] for x in columns_dict]
|
|
if column[1] in columns_names_list:
|
|
continue
|
|
|
|
# Creating the missing column
|
|
if len(column) == 3:
|
|
database.execute('''ALTER TABLE {0} ADD COLUMN "{1}" "{2}"'''.format(column[0], column[1], column[2]))
|
|
else:
|
|
database.execute('''ALTER TABLE {0} ADD COLUMN "{1}" "{2}" DEFAULT "{3}"'''.format(column[0], column[1], column[2], column[3]))
|
|
logging.debug('BAZARR Database upgrade process added column {0} to table {1}.'.format(column[1], column[0]))
|
|
except:
|
|
pass
|
|
|
|
# Create blacklist tables
|
|
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist (sonarr_series_id integer, sonarr_episode_id integer, "
|
|
"timestamp integer, provider text, subs_id text, language text)")
|
|
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist_movie (radarr_id integer, timestamp integer, "
|
|
"provider text, subs_id text, language text)")
|
|
|
|
# Create languages profiles table and populate it
|
|
lang_table_content = database.execute("SELECT * FROM table_languages_profiles")
|
|
if isinstance(lang_table_content, list):
|
|
lang_table_exist = True
|
|
else:
|
|
lang_table_exist = False
|
|
database.execute("CREATE TABLE IF NOT EXISTS table_languages_profiles ("
|
|
"profileId INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, "
|
|
"cutoff INTEGER, items TEXT NOT NULL)")
|
|
|
|
if not lang_table_exist:
|
|
series_default = []
|
|
try:
|
|
for language in ast.literal_eval(settings.general.serie_default_language):
|
|
if settings.general.serie_default_forced == 'Both':
|
|
series_default.append([language, 'True', settings.general.serie_default_hi])
|
|
series_default.append([language, 'False', settings.general.serie_default_hi])
|
|
else:
|
|
series_default.append([language, settings.general.serie_default_forced,
|
|
settings.general.serie_default_hi])
|
|
except ValueError:
|
|
pass
|
|
|
|
movies_default = []
|
|
try:
|
|
for language in ast.literal_eval(settings.general.movie_default_language):
|
|
if settings.general.movie_default_forced == 'Both':
|
|
movies_default.append([language, 'True', settings.general.movie_default_hi])
|
|
movies_default.append([language, 'False', settings.general.movie_default_hi])
|
|
else:
|
|
movies_default.append([language, settings.general.movie_default_forced,
|
|
settings.general.movie_default_hi])
|
|
except ValueError:
|
|
pass
|
|
|
|
profiles_to_create = database.execute("SELECT DISTINCT languages, hearing_impaired, forced "
|
|
"FROM (SELECT languages, hearing_impaired, forced FROM table_shows "
|
|
"UNION ALL SELECT languages, hearing_impaired, forced FROM table_movies) "
|
|
"a WHERE languages NOT null and languages NOT IN ('None', '[]')")
|
|
|
|
for profile in profiles_to_create:
|
|
profile_items = []
|
|
languages_list = ast.literal_eval(profile['languages'])
|
|
for i, language in enumerate(languages_list, 1):
|
|
if profile['forced'] == 'Both':
|
|
profile_items.append({'id': i, 'language': language, 'forced': 'True',
|
|
'hi': profile['hearing_impaired'], 'audio_exclude': 'False'})
|
|
profile_items.append({'id': i, 'language': language, 'forced': 'False',
|
|
'hi': profile['hearing_impaired'], 'audio_exclude': 'False'})
|
|
else:
|
|
profile_items.append({'id': i, 'language': language, 'forced': profile['forced'],
|
|
'hi': profile['hearing_impaired'], 'audio_exclude': 'False'})
|
|
|
|
# Create profiles
|
|
new_profile_name = profile['languages'] + ' (' + profile['hearing_impaired'] + '/' + profile['forced'] + ')'
|
|
database.execute("INSERT INTO table_languages_profiles (name, cutoff, items) VALUES("
|
|
"?,null,?)", (new_profile_name, json.dumps(profile_items),))
|
|
created_profile_id = database.execute("SELECT profileId FROM table_languages_profiles WHERE name = ?",
|
|
(new_profile_name,), only_one=True)['profileId']
|
|
# Assign profiles to series and movies
|
|
database.execute("UPDATE table_shows SET profileId = ? WHERE languages = ? AND hearing_impaired = ? AND "
|
|
"forced = ?", (created_profile_id, profile['languages'], profile['hearing_impaired'],
|
|
profile['forced']))
|
|
database.execute("UPDATE table_movies SET profileId = ? WHERE languages = ? AND hearing_impaired = ? AND "
|
|
"forced = ?", (created_profile_id, profile['languages'], profile['hearing_impaired'],
|
|
profile['forced']))
|
|
|
|
# Save new defaults
|
|
profile_items_list = []
|
|
for item in profile_items:
|
|
profile_items_list.append([item['language'], item['forced'], item['hi']])
|
|
try:
|
|
if created_profile_id and profile_items_list == series_default:
|
|
settings.general.serie_default_profile = str(created_profile_id)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
if created_profile_id and profile_items_list == movies_default:
|
|
settings.general.movie_default_profile = str(created_profile_id)
|
|
except:
|
|
pass
|
|
|
|
# null languages, forced and hearing_impaired for all series and movies
|
|
database.execute("UPDATE table_shows SET languages = null, forced = null, hearing_impaired = null")
|
|
database.execute("UPDATE table_movies SET languages = null, forced = null, hearing_impaired = null")
|
|
|
|
# Force series, episodes and movies sync with Sonarr to get all the audio track from video files
|
|
# Set environment variable that is going to be use during the init process to run sync once Bazarr is ready.
|
|
os.environ['BAZARR_AUDIO_PROFILES_MIGRATION'] = '1'
|
|
|
|
|
|
def get_exclusion_clause(type):
|
|
where_clause = ''
|
|
if type == 'series':
|
|
tagsList = ast.literal_eval(settings.sonarr.excluded_tags)
|
|
for tag in tagsList:
|
|
where_clause += ' AND table_shows.tags NOT LIKE "%\'' + tag + '\'%"'
|
|
else:
|
|
tagsList = ast.literal_eval(settings.radarr.excluded_tags)
|
|
for tag in tagsList:
|
|
where_clause += ' AND table_movies.tags NOT LIKE "%\'' + tag + '\'%"'
|
|
|
|
if type == 'series':
|
|
monitoredOnly = settings.sonarr.getboolean('only_monitored')
|
|
if monitoredOnly:
|
|
where_clause += ' AND table_episodes.monitored = "True"'
|
|
else:
|
|
monitoredOnly = settings.radarr.getboolean('only_monitored')
|
|
if monitoredOnly:
|
|
where_clause += ' AND table_movies.monitored = "True"'
|
|
|
|
if type == 'series':
|
|
typesList = ast.literal_eval(settings.sonarr.excluded_series_types)
|
|
for type in typesList:
|
|
where_clause += ' AND table_shows.seriesType != "' + type + '"'
|
|
|
|
return where_clause
|
|
|
|
|
|
def update_profile_id_list():
|
|
global profile_id_list
|
|
profile_id_list = database.execute("SELECT profileId, name, cutoff, items FROM table_languages_profiles")
|
|
|
|
|
|
def get_profiles_list(profile_id=None):
|
|
if not len(profile_id_list):
|
|
update_profile_id_list()
|
|
|
|
if profile_id:
|
|
for profile in profile_id_list:
|
|
if profile['profileId'] == profile_id:
|
|
return profile
|
|
else:
|
|
return profile_id_list
|
|
|
|
|
|
def get_desired_languages(profile_id):
|
|
languages = []
|
|
|
|
if not len(profile_id_list):
|
|
update_profile_id_list()
|
|
|
|
if profile_id:
|
|
for profile in profile_id_list:
|
|
profileId, name, cutoff, items = profile.values()
|
|
if profileId == int(profile_id):
|
|
items_list = ast.literal_eval(items)
|
|
languages = [x['language'] for x in items_list]
|
|
break
|
|
|
|
return languages
|
|
|
|
|
|
def get_profile_id_name(profile_id):
|
|
name_from_id = None
|
|
|
|
if not len(profile_id_list):
|
|
update_profile_id_list()
|
|
|
|
if profile_id:
|
|
for profile in profile_id_list:
|
|
profileId, name, cutoff, items = profile.values()
|
|
if profileId == int(profile_id):
|
|
name_from_id = name
|
|
break
|
|
|
|
return name_from_id
|
|
|
|
|
|
def get_profile_cutoff(profile_id):
|
|
cutoff_language = None
|
|
|
|
if not len(profile_id_list):
|
|
update_profile_id_list()
|
|
|
|
if profile_id:
|
|
cutoff_language = []
|
|
for profile in profile_id_list:
|
|
profileId, name, cutoff, items = profile.values()
|
|
if cutoff:
|
|
if profileId == int(profile_id):
|
|
for item in ast.literal_eval(items):
|
|
if item['id'] == cutoff:
|
|
return [item]
|
|
elif cutoff == 65535:
|
|
cutoff_language.append(item)
|
|
|
|
if not len(cutoff_language):
|
|
cutoff_language = None
|
|
|
|
return cutoff_language
|
|
|
|
|
|
def get_audio_profile_languages(series_id=None, episode_id=None, movie_id=None):
|
|
from get_languages import alpha2_from_language, alpha3_from_language
|
|
audio_languages = []
|
|
|
|
if series_id:
|
|
audio_languages_list_str = database.execute("SELECT audio_language FROM table_shows WHERE sonarrSeriesId=?",
|
|
(series_id,), only_one=True)['audio_language']
|
|
try:
|
|
audio_languages_list = ast.literal_eval(audio_languages_list_str)
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
for language in audio_languages_list:
|
|
audio_languages.append(
|
|
{"name": language,
|
|
"code2": alpha2_from_language(language) or None,
|
|
"code3": alpha3_from_language(language) or None}
|
|
)
|
|
elif episode_id:
|
|
audio_languages_list_str = database.execute("SELECT audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
|
|
(episode_id,), only_one=True)['audio_language']
|
|
try:
|
|
audio_languages_list = ast.literal_eval(audio_languages_list_str)
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
for language in audio_languages_list:
|
|
audio_languages.append(
|
|
{"name": language,
|
|
"code2": alpha2_from_language(language) or None,
|
|
"code3": alpha3_from_language(language) or None}
|
|
)
|
|
elif movie_id:
|
|
audio_languages_list_str = database.execute("SELECT audio_language FROM table_movies WHERE radarrId=?",
|
|
(movie_id,), only_one=True)['audio_language']
|
|
try:
|
|
audio_languages_list = ast.literal_eval(audio_languages_list_str)
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
for language in audio_languages_list:
|
|
audio_languages.append(
|
|
{"name": language,
|
|
"code2": alpha2_from_language(language) or None,
|
|
"code3": alpha3_from_language(language) or None}
|
|
)
|
|
|
|
return audio_languages
|