Languages profiles (#1232)

Implementing the languages profiles functionality.
This commit is contained in:
morpheus65535 2021-01-18 23:49:51 -05:00 committed by GitHub
parent 240a3759cf
commit 22cd45bc41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1540 additions and 1123 deletions

View File

@ -1,15 +1,12 @@
# coding=utf-8 # coding=utf-8
import os
import ast import ast
from datetime import timedelta from datetime import timedelta
import datetime
from dateutil import rrule from dateutil import rrule
import pretty import pretty
import time import time
from operator import itemgetter from operator import itemgetter
import platform import platform
import io
import re import re
import json import json
@ -18,13 +15,13 @@ from config import settings, base_url, save_settings
from init import * from init import *
import logging import logging
from database import database, get_exclusion_clause from database import database, get_exclusion_clause, get_profiles_list, get_desired_languages, get_profile_id_name, \
get_audio_profile_languages, update_profile_id_list
from helper import path_mappings from helper import path_mappings
from get_languages import language_from_alpha3, language_from_alpha2, alpha2_from_alpha3, alpha2_from_language, \ from get_languages import language_from_alpha2, alpha3_from_alpha2
alpha3_from_language, alpha3_from_alpha2 from get_subtitle import download_subtitle, series_download_subtitles, manual_search, manual_download_subtitle, \
from get_subtitle import download_subtitle, series_download_subtitles, movies_download_subtitles, \ manual_upload_subtitle, wanted_search_missing_subtitles_series, wanted_search_missing_subtitles_movies, \
manual_search, manual_download_subtitle, manual_upload_subtitle, wanted_search_missing_subtitles_series, \ episode_download_subtitles, movies_download_subtitles
wanted_search_missing_subtitles_movies, episode_download_subtitles, movies_download_subtitles
from notifier import send_notifications, send_notifications_movie from notifier import send_notifications, send_notifications_movie
from list_subtitles import store_subtitles, store_subtitles_movie, series_scan_subtitles, movies_scan_subtitles, \ from list_subtitles import store_subtitles, store_subtitles_movie, series_scan_subtitles, movies_scan_subtitles, \
list_missing_subtitles, list_missing_subtitles_movies list_missing_subtitles, list_missing_subtitles_movies
@ -128,6 +125,12 @@ class Languages(Resource):
return jsonify(result) return jsonify(result)
class LanguagesProfiles(Resource):
@authenticate
def get(self):
return jsonify(data=get_profiles_list())
class Notifications(Resource): class Notifications(Resource):
@authenticate @authenticate
def get(self): def get(self):
@ -182,6 +185,40 @@ class ResetProviders(Resource):
class SaveSettings(Resource): class SaveSettings(Resource):
@authenticate @authenticate
def post(self): def post(self):
languages_profiles = request.form.get('languages_profiles')
if languages_profiles:
existing_ids = database.execute('SELECT profileId FROM table_languages_profiles')
existing = [x['profileId'] for x in existing_ids]
for item in json.loads(languages_profiles):
if item['profileId'] in existing:
# Update existing profiles
database.execute('UPDATE table_languages_profiles SET name = ?, cutoff = ?, items = ? '
'WHERE profileId = ?', (item['name'],
item['cutoff'] if item['cutoff'] != '' else None,
item['items'],
item['profileId']))
existing.remove(item['profileId'])
else:
# Add new profiles
database.execute('INSERT INTO table_languages_profiles (profileId, name, cutoff, items) '
'VALUES (?, ?, ?, ?)', (item['profileId'],
item['name'],
item['cutoff'] if item['cutoff'] != '' else None,
item['items']))
for profileId in existing:
# Unassign this profileId from series and movies
database.execute('UPDATE table_shows SET profileId = null WHERE profileId = ?', (profileId,))
database.execute('UPDATE table_movies SET profileId = null WHERE profileId = ?', (profileId,))
# Remove deleted profiles
database.execute('DELETE FROM table_languages_profiles WHERE profileId = ?', (profileId,))
update_profile_id_list()
if settings.general.getboolean('use_sonarr'):
scheduler.add_job(list_missing_subtitles, kwargs={'send_event': False})
if settings.general.getboolean('use_radarr'):
scheduler.add_job(list_missing_subtitles_movies, kwargs={'send_event': False})
save_settings(zip(request.form.keys(), request.form.listvalues())) save_settings(zip(request.form.keys(), request.form.listvalues()))
return '', 200 return '', 200
@ -283,10 +320,6 @@ class Series(Resource):
if seriesId: if seriesId:
result = database.execute("SELECT * FROM table_shows WHERE sonarrSeriesId=? ORDER BY sortTitle ASC LIMIT ? " result = database.execute("SELECT * FROM table_shows WHERE sonarrSeriesId=? ORDER BY sortTitle ASC LIMIT ? "
"OFFSET ?", (seriesId, length, start)) "OFFSET ?", (seriesId, length, start))
desired_languages = database.execute("SELECT languages FROM table_shows WHERE sonarrSeriesId=?",
(seriesId,), only_one=True)['languages']
if desired_languages == "None":
desired_languages = '[]'
else: else:
result = database.execute("SELECT * FROM table_shows ORDER BY sortTitle ASC LIMIT ? OFFSET ?", (length, start)) result = database.execute("SELECT * FROM table_shows ORDER BY sortTitle ASC LIMIT ? OFFSET ?", (length, start))
for item in result: for item in result:
@ -294,11 +327,10 @@ class Series(Resource):
item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])}) item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])})
# Parse audio language # Parse audio language
item.update({"audio_language": {"name": item['audio_language'], item.update({"audio_language": get_audio_profile_languages(series_id=item['sonarrSeriesId'])})
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse desired languages # Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None': if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])}) item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']): for i, subs in enumerate(item['languages']):
@ -306,6 +338,9 @@ class Series(Resource):
"code2": subs, "code2": subs,
"code3": alpha3_from_alpha2(subs)} "code3": alpha3_from_alpha2(subs)}
# Parse profileId
item['profileId'] = {"id": item['profileId'], "name": get_profile_id_name(item['profileId'])}
# Parse alternate titles # Parse alternate titles
if item['alternateTitles']: if item['alternateTitles']:
item.update({"alternateTitles": ast.literal_eval(item['alternateTitles'])}) item.update({"alternateTitles": ast.literal_eval(item['alternateTitles'])})
@ -343,42 +378,20 @@ class Series(Resource):
item.update({"episodeFileCount": episodeFileCount}) item.update({"episodeFileCount": episodeFileCount})
# Add the series desired subtitles language code2 # Add the series desired subtitles language code2
try: item.update({"desired_languages": get_desired_languages(item['profileId']['id'])})
item.update({"desired_languages": desired_languages})
except NameError:
pass
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result) return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
@authenticate @authenticate
def post(self): def post(self):
seriesId = request.args.get('seriesid') seriesId = request.args.get('seriesid')
lang = request.form.getlist('languages') languages_profile = request.form.get('languages')
if len(lang) > 0:
pass
else:
lang = 'None'
single_language = settings.general.getboolean('single_language') if languages_profile == 'None':
if single_language: languages_profile = None
if str(lang) == "['None']":
lang = 'None'
else:
lang = str(lang)
else:
if str(lang) == "['']":
lang = '[]'
hi = request.form.get('hi') database.execute("UPDATE table_shows SET profileId=? WHERE sonarrSeriesId=?", (languages_profile, seriesId))
forced = request.form.get('forced')
if hi == "on":
hi = "True"
else:
hi = "False"
result = database.execute("UPDATE table_shows SET languages=?, hearing_impaired=?, forced=? WHERE "
"sonarrSeriesId=?", (str(lang), hi, forced, seriesId))
list_missing_subtitles(no=seriesId) list_missing_subtitles(no=seriesId)
@ -392,7 +405,7 @@ class SeriesEditor(Resource):
def get(self, **kwargs): def get(self, **kwargs):
draw = request.args.get('draw') draw = request.args.get('draw')
result = database.execute("SELECT sonarrSeriesId, title, languages, hearing_impaired, forced, audio_language " result = database.execute("SELECT sonarrSeriesId, title, audio_language, profileId "
"FROM table_shows ORDER BY sortTitle") "FROM table_shows ORDER BY sortTitle")
row_count = len(result) row_count = len(result)
@ -402,11 +415,10 @@ class SeriesEditor(Resource):
item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])}) item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])})
# Parse audio language # Parse audio language
item.update({"audio_language": {"name": item['audio_language'], item.update({"audio_language": get_audio_profile_languages(series_id=item['sonarrSeriesId'])})
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse desired languages # Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None': if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])}) item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']): for i, subs in enumerate(item['languages']):
@ -414,43 +426,30 @@ class SeriesEditor(Resource):
"code2": subs, "code2": subs,
"code3": alpha3_from_alpha2(subs)} "code3": alpha3_from_alpha2(subs)}
# Parse profileId
item['profileId'] = {"id": item['profileId'], "name": get_profile_id_name(item['profileId'])}
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result) return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
class SeriesEditSave(Resource): class SeriesEditSave(Resource):
@authenticate @authenticate
def post(self): def post(self):
lang = request.form.getlist('languages[]') lang = request.form.get('languages')
hi = request.form.getlist('hi[]')
forced = request.form.getlist('forced[]')
if lang == ['None']: if lang == 'None':
lang = 'None' lang = None
seriesIdList = [] seriesIdList = []
seriesidLangList = [] seriesidLangList = []
seriesidHiList = []
seriesidForcedList = []
for item in request.form.getlist('seriesid[]'): for item in request.form.getlist('seriesid[]'):
seriesid = item.lstrip('row_') seriesid = item.lstrip('row_')
seriesIdList.append(seriesid) seriesIdList.append(seriesid)
if len(lang): seriesidLangList.append([lang, seriesid])
seriesidLangList.append([str(lang), seriesid])
if len(hi):
seriesidHiList.append([hi[0], seriesid])
if len(forced):
seriesidForcedList.append([forced[0], seriesid])
try: try:
if len(lang): database.execute("UPDATE table_shows SET profileId=? WHERE sonarrSeriesId=?", seriesidLangList,
database.execute("UPDATE table_shows SET languages=? WHERE sonarrSeriesId=?", seriesidLangList, execute_many=True)
execute_many=True)
if len(hi):
database.execute("UPDATE table_shows SET hearing_impaired=? WHERE sonarrSeriesId=?", seriesidHiList,
execute_many=True)
if len(forced):
database.execute("UPDATE table_shows SET forced=? WHERE sonarrSeriesId=?", seriesidForcedList,
execute_many=True)
except: except:
pass pass
else: else:
@ -476,27 +475,22 @@ class Episodes(Resource):
(seriesId,), only_one=True)['count'] (seriesId,), only_one=True)['count']
if episodeId: if episodeId:
result = database.execute("SELECT * FROM table_episodes WHERE sonarrEpisodeId=?", (episodeId,)) result = database.execute("SELECT * FROM table_episodes WHERE sonarrEpisodeId=?", (episodeId,))
desired_languages = database.execute("SELECT languages FROM table_shows WHERE sonarrSeriesId=?",
(seriesId,), only_one=True)['languages']
if desired_languages == "None":
desired_languages = '[]'
elif seriesId: elif seriesId:
result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=? ORDER BY season DESC, " result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=? ORDER BY season DESC, "
"episode DESC", (seriesId,)) "episode DESC", (seriesId,))
desired_languages = database.execute("SELECT languages FROM table_shows WHERE sonarrSeriesId=?",
(seriesId,), only_one=True)['languages']
if desired_languages == "None":
desired_languages = '[]'
else: else:
return "Series ID not provided", 400 return "Series ID not provided", 400
profileId = database.execute("SELECT profileId FROM table_shows WHERE sonarrSeriesId = ?", (seriesId,),
only_one=True)['profileId']
desired_languages = str(get_desired_languages(profileId))
for item in result: for item in result:
# Add Datatables rowId # Add Datatables rowId
item.update({"DT_RowId": 'row_' + str(item['sonarrEpisodeId'])}) item.update({"DT_RowId": 'row_' + str(item['sonarrEpisodeId'])})
# Parse audio language # Parse audio language
item.update({"audio_language": {"name": item['audio_language'], item.update({"audio_language": get_audio_profile_languages(episode_id=item['sonarrEpisodeId'])})
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse subtitles # Parse subtitles
if item['subtitles']: if item['subtitles']:
@ -616,8 +610,12 @@ class EpisodesSubtitlesDownload(Resource):
title = request.form.get('title') title = request.form.get('title')
providers_list = get_providers() providers_list = get_providers()
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
audio_language = database.execute("SELECT audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
(sonarrEpisodeId,), only_one=True)['audio_language'] audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId)
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
try: try:
result = download_subtitle(episodePath, language, audio_language, hi, forced, providers_list, providers_auth, sceneName, result = download_subtitle(episodePath, language, audio_language, hi, forced, providers_list, providers_auth, sceneName,
@ -659,14 +657,12 @@ class EpisodesSubtitlesManualSearch(Resource):
sceneName = request.form.get('sceneName') sceneName = request.form.get('sceneName')
if sceneName == "null": if sceneName == "null":
sceneName = "None" sceneName = "None"
language = request.form.get('language') profileId = request.form.get('profileId')
hi = request.form.get('hi').capitalize()
forced = request.form.get('forced').capitalize()
title = request.form.get('title') title = request.form.get('title')
providers_list = get_providers() providers_list = get_providers()
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
data = manual_search(episodePath, language, hi, forced, providers_list, providers_auth, sceneName, title, data = manual_search(episodePath, profileId, providers_list, providers_auth, sceneName, title,
'series') 'series')
if not data: if not data:
data = [] data = []
@ -690,8 +686,12 @@ class EpisodesSubtitlesManualDownload(Resource):
sonarrEpisodeId = request.form.get('sonarrEpisodeId') sonarrEpisodeId = request.form.get('sonarrEpisodeId')
title = request.form.get('title') title = request.form.get('title')
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
audio_language = database.execute("SELECT audio_language FROM table_episodes WHERE sonarrEpisodeId=?",
(sonarrEpisodeId,), only_one=True)['audio_language'] audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId)
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
try: try:
result = manual_download_subtitle(episodePath, language, audio_language, hi, forced, subtitle, result = manual_download_subtitle(episodePath, language, audio_language, hi, forced, subtitle,
@ -887,10 +887,6 @@ class Movies(Resource):
if moviesId: if moviesId:
result = database.execute("SELECT * FROM table_movies WHERE radarrId=? ORDER BY sortTitle ASC LIMIT ? " result = database.execute("SELECT * FROM table_movies WHERE radarrId=? ORDER BY sortTitle ASC LIMIT ? "
"OFFSET ?", (moviesId, length, start)) "OFFSET ?", (moviesId, length, start))
desired_languages = database.execute("SELECT languages FROM table_movies WHERE radarrId=?",
(moviesId,), only_one=True)['languages']
if desired_languages == "None":
desired_languages = '[]'
else: else:
result = database.execute("SELECT * FROM table_movies ORDER BY sortTitle ASC LIMIT ? OFFSET ?", result = database.execute("SELECT * FROM table_movies ORDER BY sortTitle ASC LIMIT ? OFFSET ?",
(length, start)) (length, start))
@ -899,11 +895,10 @@ class Movies(Resource):
item.update({"DT_RowId": 'row_' + str(item['radarrId'])}) item.update({"DT_RowId": 'row_' + str(item['radarrId'])})
# Parse audio language # Parse audio language
item.update({"audio_language": {"name": item['audio_language'], item.update({"audio_language": get_audio_profile_languages(movie_id=item['radarrId'])})
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse desired languages # Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None': if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])}) item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']): for i, subs in enumerate(item['languages']):
@ -911,6 +906,9 @@ class Movies(Resource):
"code2": subs, "code2": subs,
"code3": alpha3_from_alpha2(subs)} "code3": alpha3_from_alpha2(subs)}
# Parse profileId
item['profileId'] = {"id": item['profileId'], "name": get_profile_id_name(item['profileId'])}
# Parse alternate titles # Parse alternate titles
if item['alternativeTitles']: if item['alternativeTitles']:
item.update({"alternativeTitles": ast.literal_eval(item['alternativeTitles'])}) item.update({"alternativeTitles": ast.literal_eval(item['alternativeTitles'])})
@ -975,42 +973,20 @@ class Movies(Resource):
item.update({"exist": os.path.isfile(mapped_path)}) item.update({"exist": os.path.isfile(mapped_path)})
# Add the movie desired subtitles language code2 # Add the movie desired subtitles language code2
try: item.update({"desired_languages": get_desired_languages(item['profileId']['id'])})
item.update({"desired_languages": desired_languages})
except NameError:
pass
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result) return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
@authenticate @authenticate
def post(self): def post(self):
radarrId = request.args.get('radarrid') radarrId = request.args.get('radarrid')
lang = request.form.getlist('languages') languages_profile = request.form.get('languages')
if len(lang) > 0:
pass
else:
lang = 'None'
single_language = settings.general.getboolean('single_language') if languages_profile == 'None':
if single_language: languages_profile = None
if str(lang) == "['None']":
lang = 'None'
else:
lang = str(lang)
else:
if str(lang) == "['']":
lang = '[]'
hi = request.form.get('hi') database.execute("UPDATE table_movies SET profileId=? WHERE radarrId=?", (languages_profile, radarrId))
forced = request.form.get('forced')
if hi == "on":
hi = "True"
else:
hi = "False"
result = database.execute("UPDATE table_movies SET languages=?, hearing_impaired=?, forced=? WHERE "
"radarrId=?", (str(lang), hi, forced, radarrId))
list_missing_subtitles_movies(no=radarrId) list_missing_subtitles_movies(no=radarrId)
@ -1024,7 +1000,7 @@ class MoviesEditor(Resource):
def get(self): def get(self):
draw = request.args.get('draw') draw = request.args.get('draw')
result = database.execute("SELECT radarrId, title, languages, hearing_impaired, forced, audio_language " result = database.execute("SELECT radarrId, title, audio_language, profileId "
"FROM table_movies ORDER BY sortTitle") "FROM table_movies ORDER BY sortTitle")
row_count = len(result) row_count = len(result)
@ -1034,11 +1010,10 @@ class MoviesEditor(Resource):
item.update({"DT_RowId": 'row_' + str(item['radarrId'])}) item.update({"DT_RowId": 'row_' + str(item['radarrId'])})
# Parse audio language # Parse audio language
item.update({"audio_language": {"name": item['audio_language'], item.update({"audio_language": get_audio_profile_languages(movie_id=item['radarrId'])})
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
# Parse desired languages # Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None': if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])}) item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']): for i, subs in enumerate(item['languages']):
@ -1046,42 +1021,29 @@ class MoviesEditor(Resource):
"code2": subs, "code2": subs,
"code3": alpha3_from_alpha2(subs)} "code3": alpha3_from_alpha2(subs)}
# Parse profileId
item['profileId'] = {"id": item['profileId'], "name": get_profile_id_name(item['profileId'])}
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result) return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
class MoviesEditSave(Resource): class MoviesEditSave(Resource):
@authenticate @authenticate
def post(self): def post(self):
lang = request.form.getlist('languages[]') lang = request.form.get('languages')
hi = request.form.getlist('hi[]')
forced = request.form.getlist('forced[]')
if lang == ['None']: if lang == 'None':
lang = 'None' lang = None
radarrIdList = [] radarrIdList = []
radarrIdLangList = [] radarrIdLangList = []
radarrIdHiList = []
radarrIdForcedList = []
for item in request.form.getlist('radarrid[]'): for item in request.form.getlist('radarrid[]'):
radarrid = item.lstrip('row_') radarrid = item.lstrip('row_')
radarrIdList.append(radarrid) radarrIdList.append(radarrid)
if len(lang): radarrIdLangList.append([lang, radarrid])
radarrIdLangList.append([str(lang), radarrid])
if len(hi):
radarrIdHiList.append([hi[0], radarrid])
if len(forced):
radarrIdForcedList.append([forced[0], radarrid])
try: try:
if len(lang): database.execute("UPDATE table_movies SET profileId=? WHERE radarrId=?", radarrIdLangList,
database.execute("UPDATE table_movies SET languages=? WHERE radarrId=?", radarrIdLangList, execute_many=True)
execute_many=True)
if len(hi):
database.execute("UPDATE table_movies SET hearing_impaired=? WHERE radarrId=?", radarrIdHiList,
execute_many=True)
if len(forced):
database.execute("UPDATE table_movies SET forced=? WHERE radarrId=?", radarrIdForcedList,
execute_many=True)
except: except:
pass pass
else: else:
@ -1131,8 +1093,12 @@ class MovieSubtitlesDownload(Resource):
title = request.form.get('title') title = request.form.get('title')
providers_list = get_providers() providers_list = get_providers()
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
audio_language = database.execute("SELECT audio_language FROM table_movies WHERE radarrId=?", (radarrId,),
only_one=True)['audio_language'] audio_language_list = get_audio_profile_languages(movie_id=radarrId)
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
try: try:
result = download_subtitle(moviePath, language, audio_language, hi, forced, providers_list, result = download_subtitle(moviePath, language, audio_language, hi, forced, providers_list,
@ -1174,14 +1140,12 @@ class MovieSubtitlesManualSearch(Resource):
sceneName = request.form.get('sceneName') sceneName = request.form.get('sceneName')
if sceneName == "null": if sceneName == "null":
sceneName = "None" sceneName = "None"
language = request.form.get('language') profileId = request.form.get('profileId')
hi = request.form.get('hi').capitalize()
forced = request.form.get('forced').capitalize()
title = request.form.get('title') title = request.form.get('title')
providers_list = get_providers() providers_list = get_providers()
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
data = manual_search(moviePath, language, hi, forced, providers_list, providers_auth, sceneName, title, data = manual_search(moviePath, profileId, providers_list, providers_auth, sceneName, title,
'movie') 'movie')
if not data: if not data:
data = [] data = []
@ -1204,8 +1168,12 @@ class MovieSubtitlesManualDownload(Resource):
radarrId = request.form.get('radarrId') radarrId = request.form.get('radarrId')
title = request.form.get('title') title = request.form.get('title')
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
audio_language = database.execute("SELECT audio_language FROM table_movies WHERE radarrId=?", (radarrId,),
only_one=True)['audio_language'] audio_language_list = get_audio_profile_languages(movie_id=radarrId)
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
try: try:
result = manual_download_subtitle(moviePath, language, audio_language, hi, forced, subtitle, result = manual_download_subtitle(moviePath, language, audio_language, hi, forced, subtitle,
@ -1412,8 +1380,7 @@ class HistorySeries(Resource):
"table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId INNER JOIN table_shows on " "table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId INNER JOIN table_shows on "
"table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE action IN (" + "table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE action IN (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not null" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not null" +
get_exclusion_clause('series') + " GROUP BY table_history.video_path, table_history.language", get_exclusion_clause('series') + " GROUP BY table_history.video_path", (minimum_timestamp,))
(minimum_timestamp,))
for upgradable_episode in upgradable_episodes: for upgradable_episode in upgradable_episodes:
if upgradable_episode['timestamp'] > minimum_timestamp: if upgradable_episode['timestamp'] > minimum_timestamp:
@ -1433,16 +1400,16 @@ class HistorySeries(Resource):
"table_episodes.title as episodeTitle, table_history.timestamp, table_history.subs_id, " "table_episodes.title as episodeTitle, table_history.timestamp, table_history.subs_id, "
"table_history.description, table_history.sonarrSeriesId, table_episodes.path, " "table_history.description, table_history.sonarrSeriesId, table_episodes.path, "
"table_history.language, table_history.score, table_shows.tags, table_history.action, " "table_history.language, table_history.score, table_shows.tags, table_history.action, "
"table_history.subtitles_path, table_history.sonarrEpisodeId, table_history.provider " "table_history.subtitles_path, table_history.sonarrEpisodeId, table_history.provider, "
"FROM table_history LEFT JOIN table_shows on table_shows.sonarrSeriesId = " "table_shows.seriesType FROM table_history LEFT JOIN table_shows on "
"table_history.sonarrSeriesId LEFT JOIN table_episodes on " "table_shows.sonarrSeriesId = table_history.sonarrSeriesId LEFT JOIN table_episodes on "
"table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE " "table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
"table_episodes.title is not NULL ORDER BY timestamp DESC LIMIT ? OFFSET ?", "table_episodes.title is not NULL ORDER BY timestamp DESC LIMIT ? OFFSET ?",
(length, start)) (length, start))
for item in data: for item in data:
# Mark episode as upgradable or not # Mark episode as upgradable or not
if {"video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score']), "tags": str(item['tags']), "monitored": str(item['monitored'])} in upgradable_episodes_not_perfect: if {"video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score']), "tags": str(item['tags']), "monitored": str(item['monitored']), "seriesType": str(item['seriesType'])} in upgradable_episodes_not_perfect:
item.update({"upgradable": True}) item.update({"upgradable": True})
else: else:
item.update({"upgradable": False}) item.update({"upgradable": False})
@ -1516,8 +1483,8 @@ class HistoryMovies(Resource):
upgradable_movies = database.execute( upgradable_movies = database.execute(
"SELECT video_path, MAX(timestamp) as timestamp, score, tags, monitored FROM table_history_movie " "SELECT video_path, MAX(timestamp) as timestamp, score, tags, monitored FROM table_history_movie "
"INNER JOIN table_movies on table_movies.radarrId=table_history_movie.radarrId WHERE action IN (" + "INNER JOIN table_movies on table_movies.radarrId=table_history_movie.radarrId WHERE action IN (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" +
get_exclusion_clause('movie') + " GROUP BY video_path, language", (minimum_timestamp,)) get_exclusion_clause('movie') + " GROUP BY video_path", (minimum_timestamp,))
for upgradable_movie in upgradable_movies: for upgradable_movie in upgradable_movies:
if upgradable_movie['timestamp'] > minimum_timestamp: if upgradable_movie['timestamp'] > minimum_timestamp:
@ -1661,7 +1628,7 @@ class WantedSeries(Resource):
data = database.execute("SELECT table_shows.title as seriesTitle, table_episodes.monitored, " data = database.execute("SELECT table_shows.title as seriesTitle, table_episodes.monitored, "
"table_episodes.season || 'x' || table_episodes.episode as episode_number, " "table_episodes.season || 'x' || table_episodes.episode as episode_number, "
"table_episodes.title as episodeTitle, table_episodes.missing_subtitles, " "table_episodes.title as episodeTitle, table_episodes.missing_subtitles, "
"table_episodes.sonarrSeriesId, table_episodes.path, table_shows.hearing_impaired, " "table_episodes.sonarrSeriesId, table_episodes.path, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, " "table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_episodes.failedAttempts, table_shows.seriesType FROM table_episodes INNER JOIN " "table_episodes.failedAttempts, table_shows.seriesType FROM table_episodes INNER JOIN "
"table_shows on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE " "table_shows on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE "
@ -1707,7 +1674,7 @@ class WantedMovies(Resource):
data_count = database.execute("SELECT tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" + data_count = database.execute("SELECT tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" +
get_exclusion_clause('movie')) get_exclusion_clause('movie'))
row_count = len(data_count) row_count = len(data_count)
data = database.execute("SELECT title, missing_subtitles, radarrId, path, hearing_impaired, sceneName, " data = database.execute("SELECT title, missing_subtitles, radarrId, path, sceneName, "
"failedAttempts, tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" + "failedAttempts, tags, monitored FROM table_movies WHERE missing_subtitles != '[]'" +
get_exclusion_clause('movie') + " ORDER BY _rowid_ DESC LIMIT " + length + " OFFSET " + get_exclusion_clause('movie') + " ORDER BY _rowid_ DESC LIMIT " + length + " OFFSET " +
start) start)
@ -2002,6 +1969,7 @@ api.add_resource(BadgesSeries, '/badges_series')
api.add_resource(BadgesMovies, '/badges_movies') api.add_resource(BadgesMovies, '/badges_movies')
api.add_resource(BadgesProviders, '/badges_providers') api.add_resource(BadgesProviders, '/badges_providers')
api.add_resource(Languages, '/languages') api.add_resource(Languages, '/languages')
api.add_resource(LanguagesProfiles, '/languages_profiles')
api.add_resource(Notifications, '/notifications') api.add_resource(Notifications, '/notifications')
api.add_resource(Search, '/search_json') api.add_resource(Search, '/search_json')

View File

@ -1,4 +1,5 @@
#!/bin/env python # coding=utf-8
from flask import Flask, redirect, render_template, request, url_for from flask import Flask, redirect, render_template, request, url_for
from flask_debugtoolbar import DebugToolbarExtension from flask_debugtoolbar import DebugToolbarExtension
from flask_socketio import SocketIO from flask_socketio import SocketIO

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import os import os
import logging import logging
import json import json

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import hashlib import hashlib
import os import os
@ -30,13 +31,9 @@ defaults = {
'use_radarr': 'False', 'use_radarr': 'False',
'path_mappings_movie': '[]', 'path_mappings_movie': '[]',
'serie_default_enabled': 'False', 'serie_default_enabled': 'False',
'serie_default_language': '[]', 'serie_default_profile': '',
'serie_default_hi': 'False',
'serie_default_forced': 'False',
'movie_default_enabled': 'False', 'movie_default_enabled': 'False',
'movie_default_language': '[]', 'movie_default_profile': '',
'movie_default_hi': 'False',
'movie_default_forced': 'False',
'page_size': '25', 'page_size': '25',
'page_size_manual_search': '10', 'page_size_manual_search': '10',
'minimum_score_movie': '70', 'minimum_score_movie': '70',

View File

@ -1,7 +1,10 @@
# coding=utf-8
import os import os
import ast import ast
import sqlite3 import sqlite3
import logging import logging
import json
from sqlite3worker import Sqlite3Worker from sqlite3worker import Sqlite3Worker
@ -9,6 +12,9 @@ from get_args import args
from helper import path_mappings from helper import path_mappings
from config import settings from config import settings
global profile_id_list
profile_id_list = []
def db_init(): def db_init():
if not os.path.exists(os.path.join(args.config_dir, 'db', 'bazarr.db')): if not os.path.exists(os.path.join(args.config_dir, 'db', 'bazarr.db')):
@ -95,6 +101,7 @@ def db_upgrade():
['table_shows', 'tags', 'text', '[]'], ['table_shows', 'tags', 'text', '[]'],
['table_shows', 'seriesType', 'text', ''], ['table_shows', 'seriesType', 'text', ''],
['table_shows', 'imdbId', 'text', ''], ['table_shows', 'imdbId', 'text', ''],
['table_shows', 'profileId', 'integer'],
['table_episodes', 'format', 'text'], ['table_episodes', 'format', 'text'],
['table_episodes', 'resolution', 'text'], ['table_episodes', 'resolution', 'text'],
['table_episodes', 'video_codec', 'text'], ['table_episodes', 'video_codec', 'text'],
@ -112,6 +119,7 @@ def db_upgrade():
['table_movies', 'forced', 'text', 'False'], ['table_movies', 'forced', 'text', 'False'],
['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_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'],
@ -143,20 +151,61 @@ def db_upgrade():
except: except:
pass pass
# Fix null languages, hearing-impaired and forced for series and movies.
database.execute("UPDATE table_shows SET languages = '[]' WHERE languages is null")
database.execute("UPDATE table_shows SET hearing_impaired = 'False' WHERE hearing_impaired is null")
database.execute("UPDATE table_shows SET forced = 'False' WHERE forced is null")
database.execute("UPDATE table_movies SET languages = '[]' WHERE languages is null")
database.execute("UPDATE table_movies SET hearing_impaired = 'False' WHERE hearing_impaired is null")
database.execute("UPDATE table_movies SET forced = 'False' WHERE forced is null")
# Create blacklist tables # Create blacklist tables
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist (sonarr_series_id integer, sonarr_episode_id integer, " 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)") "timestamp integer, provider text, subs_id text, language text)")
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist_movie (radarr_id integer, timestamp integer, " database.execute("CREATE TABLE IF NOT EXISTS table_blacklist_movie (radarr_id integer, timestamp integer, "
"provider text, subs_id text, language text)") "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:
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']))
# 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): def get_exclusion_clause(type):
where_clause = '' where_clause = ''
@ -184,3 +233,115 @@ def get_exclusion_clause(type):
where_clause += ' AND table_shows.seriesType != "' + type + '"' where_clause += ' AND table_shows.seriesType != "' + type + '"'
return where_clause 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']
audio_languages_list = ast.literal_eval(audio_languages_list_str)
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']
audio_languages_list = ast.literal_eval(audio_languages_list_str)
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']
audio_languages_list = ast.literal_eval(audio_languages_list_str)
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

View File

@ -1,3 +1,5 @@
# coding=utf-8
import enzyme import enzyme
from enzyme.exceptions import MalformedMKVError from enzyme.exceptions import MalformedMKVError
import logging import logging

View File

@ -1,3 +1,5 @@
# coding=utf-8
import json import json
from app import socketio from app import socketio

View File

@ -1,3 +1,5 @@
# coding=utf-8
import os import os
import requests import requests
import logging import logging

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import os import os
import argparse import argparse

View File

@ -1,11 +1,12 @@
# coding=utf-8 # coding=utf-8
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 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 list_missing_subtitles, store_subtitles, series_full_scan_subtitles from list_subtitles import store_subtitles, series_full_scan_subtitles
from get_subtitle import episode_download_subtitles from get_subtitle import episode_download_subtitles
from event_handler import event_stream from event_handler import event_stream
@ -85,12 +86,12 @@ def sync_episodes():
videoCodec = None videoCodec = None
audioCodec = None audioCodec = None
audio_language = None audio_language = []
if 'language' in episode['episodeFile'] and len(episode['episodeFile']['language']): if 'language' in episode['episodeFile'] and len(episode['episodeFile']['language']):
item = episode['episodeFile']['language'] item = episode['episodeFile']['language']
if isinstance(item, dict): if isinstance(item, dict):
if 'name' in item: if 'name' in item:
audio_language = item['name'] audio_language.append(item['name'])
else: else:
audio_language = database.execute("SELECT audio_language FROM table_shows WHERE " audio_language = database.execute("SELECT audio_language FROM table_shows WHERE "
"sonarrSeriesId=?", (episode['seriesId'],), "sonarrSeriesId=?", (episode['seriesId'],),
@ -113,7 +114,7 @@ def sync_episodes():
'video_codec': videoCodec, 'video_codec': videoCodec,
'audio_codec': audioCodec, 'audio_codec': audioCodec,
'episode_file_id': episode['episodeFile']['id'], 'episode_file_id': episode['episodeFile']['id'],
'audio_language': audio_language}) 'audio_language': str(audio_language)})
else: else:
episodes_to_add.append({'sonarrSeriesId': episode['seriesId'], episodes_to_add.append({'sonarrSeriesId': episode['seriesId'],
'sonarrEpisodeId': episode['id'], 'sonarrEpisodeId': episode['id'],
@ -128,7 +129,7 @@ def sync_episodes():
'video_codec': videoCodec, 'video_codec': videoCodec,
'audio_codec': audioCodec, 'audio_codec': audioCodec,
'episode_file_id': episode['episodeFile']['id'], 'episode_file_id': episode['episodeFile']['id'],
'audio_language': audio_language}) '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))

View File

@ -1,7 +1,6 @@
# coding=utf-8 # coding=utf-8
import pycountry import pycountry
import ast
from subzero.language import Language from subzero.language import Language
from database import database from database import database
@ -76,48 +75,5 @@ def get_language_set():
return language_set return language_set
def clean_desired_languages():
from list_subtitles import list_missing_subtitles, list_missing_subtitles_movies
enabled_languages = []
enabled_languages_temp = database.execute("SELECT code2 FROM table_settings_languages WHERE enabled=1")
for language in enabled_languages_temp:
enabled_languages.append(language['code2'])
series_languages = database.execute("SELECT sonarrSeriesId, languages FROM table_shows")
movies_languages = database.execute("SELECT radarrId, languages FROM table_movies")
for item in series_languages:
if item['languages'] != 'None':
try:
languages_list = ast.literal_eval(item['languages'])
except:
pass
else:
cleaned_languages_list = []
for language in languages_list:
if language in enabled_languages:
cleaned_languages_list.append(language)
if cleaned_languages_list != languages_list:
database.execute("UPDATE table_shows SET languages=? WHERE sonarrSeriesId=?",
(str(cleaned_languages_list), item['sonarrSeriesId']))
list_missing_subtitles(no=item['sonarrSeriesId'])
for item in movies_languages:
if item['languages'] != 'None':
try:
languages_list = ast.literal_eval(item['languages'])
except:
pass
else:
cleaned_languages_list = []
for language in languages_list:
if language in enabled_languages:
cleaned_languages_list.append(language)
if cleaned_languages_list != languages_list:
database.execute("UPDATE table_movies SET languages=? WHERE radarrId=?",
(str(cleaned_languages_list), item['radarrId']))
list_missing_subtitles_movies(no=item['radarrId'])
if __name__ == '__main__': if __name__ == '__main__':
load_language_in_db() load_language_in_db()

View File

@ -7,7 +7,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
from list_subtitles import store_subtitles_movie, list_missing_subtitles_movies, 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
from database import database, dict_converter, get_exclusion_clause from database import database, dict_converter, get_exclusion_clause
@ -26,13 +26,11 @@ def update_movies():
movie_default_enabled = settings.general.getboolean('movie_default_enabled') movie_default_enabled = settings.general.getboolean('movie_default_enabled')
if movie_default_enabled is True: if movie_default_enabled is True:
movie_default_language = settings.general.movie_default_language movie_default_profile = settings.general.movie_default_profile
movie_default_hi = settings.general.movie_default_hi if movie_default_profile == '':
movie_default_forced = settings.general.movie_default_forced movie_default_profile = None
else: else:
movie_default_language = '[]' movie_default_profile = None
movie_default_hi = 'False'
movie_default_forced = 'False'
if apikey_radarr is None: if apikey_radarr is None:
pass pass
@ -146,13 +144,14 @@ def update_movies():
videoCodec = None videoCodec = None
audioCodec = None audioCodec = None
audio_language = None audio_language = []
if radarr_version.startswith('0'): if radarr_version.startswith('0'):
if 'mediaInfo' in movie['movieFile']: if 'mediaInfo' in movie['movieFile']:
if 'audioLanguages' in movie['movieFile']['mediaInfo']: if 'audioLanguages' in movie['movieFile']['mediaInfo']:
audio_language_list = movie['movieFile']['mediaInfo']['audioLanguages'].split('/') audio_languages_list = movie['movieFile']['mediaInfo']['audioLanguages'].split('/')
if len(audio_language_list): if len(audio_languages_list):
audio_language = audio_language_list[0].strip() for audio_language_list in audio_languages_list:
audio_language.append(audio_language_list.strip())
if not audio_language: if not audio_language:
audio_language = profile_id_to_language(movie['qualityProfileId'], audio_profiles) audio_language = profile_id_to_language(movie['qualityProfileId'], audio_profiles)
else: else:
@ -160,8 +159,7 @@ def update_movies():
for item in movie['movieFile']['languages']: for item in movie['movieFile']['languages']:
if isinstance(item, dict): if isinstance(item, dict):
if 'name' in item: if 'name' in item:
audio_language = item['name'] audio_language.append(item['name'])
break
tags = [d['label'] for d in tagsDict if d['id'] in movie['tags']] tags = [d['label'] for d in tagsDict if d['id'] in movie['tags']]
@ -175,7 +173,7 @@ def update_movies():
'tmdbId': str(movie["tmdbId"]), 'tmdbId': str(movie["tmdbId"]),
'poster': poster, 'poster': poster,
'fanart': fanart, 'fanart': fanart,
'audio_language': audio_language, 'audio_language': str(audio_language),
'sceneName': sceneName, 'sceneName': sceneName,
'monitored': str(bool(movie['monitored'])), 'monitored': str(bool(movie['monitored'])),
'year': str(movie['year']), 'year': str(movie['year']),
@ -194,13 +192,11 @@ def update_movies():
'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"]),
'languages': movie_default_language,
'subtitles': '[]', 'subtitles': '[]',
'hearing_impaired': movie_default_hi,
'overview': overview, 'overview': overview,
'poster': poster, 'poster': poster,
'fanart': fanart, 'fanart': fanart,
'audio_language': audio_language, 'audio_language': str(audio_language),
'sceneName': sceneName, 'sceneName': sceneName,
'monitored': str(bool(movie['monitored'])), 'monitored': str(bool(movie['monitored'])),
'sortTitle': movie['sortTitle'], 'sortTitle': movie['sortTitle'],
@ -211,9 +207,9 @@ def update_movies():
'video_codec': videoCodec, 'video_codec': videoCodec,
'audio_codec': audioCodec, 'audio_codec': audioCodec,
'imdbId': imdbId, 'imdbId': imdbId,
'forced': movie_default_forced,
'movie_file_id': int(movie['movieFile']['id']), 'movie_file_id': int(movie['movieFile']['id']),
'tags': str(tags)}) 'tags': str(tags),
'profileId': movie_default_profile})
else: else:
logging.error( logging.error(
'BAZARR Radarr returned a movie without a file path: ' + movie["path"] + separator + 'BAZARR Radarr returned a movie without a file path: ' + movie["path"] + separator +
@ -315,8 +311,10 @@ def get_profile_list():
def profile_id_to_language(id, profiles): def profile_id_to_language(id, profiles):
for profile in profiles: for profile in profiles:
profiles_to_return = []
if id == profile[0]: if id == profile[0]:
return profile[1] profiles_to_return.append(profile[1])
return profiles_to_return
def RadarrFormatAudioCodec(audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures): def RadarrFormatAudioCodec(audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures):

View File

@ -22,13 +22,11 @@ def update_series():
serie_default_enabled = settings.general.getboolean('serie_default_enabled') serie_default_enabled = settings.general.getboolean('serie_default_enabled')
if serie_default_enabled is True: if serie_default_enabled is True:
serie_default_language = settings.general.serie_default_language serie_default_profile = settings.general.serie_default_profile
serie_default_hi = settings.general.serie_default_hi if serie_default_profile == '':
serie_default_forced = settings.general.serie_default_forced serie_default_profile = None
else: else:
serie_default_language = '[]' serie_default_profile = None
serie_default_hi = 'False'
serie_default_forced = 'False'
audio_profiles = get_profile_list() audio_profiles = get_profile_list()
tagsDict = get_tags() tagsDict = get_tags()
@ -76,6 +74,7 @@ def update_series():
if show['alternateTitles'] is not None: if show['alternateTitles'] is not None:
alternate_titles = str([item['title'] for item in show['alternateTitles']]) alternate_titles = str([item['title'] for item in show['alternateTitles']])
audio_language = []
if sonarr_version.startswith('2'): if sonarr_version.startswith('2'):
audio_language = profile_id_to_language(show['qualityProfileId'], audio_profiles) audio_language = profile_id_to_language(show['qualityProfileId'], audio_profiles)
else: else:
@ -96,7 +95,7 @@ def update_series():
'overview': overview, 'overview': overview,
'poster': poster, 'poster': poster,
'fanart': fanart, 'fanart': fanart,
'audio_language': audio_language, 'audio_language': str(audio_language),
'sortTitle': show['sortTitle'], 'sortTitle': show['sortTitle'],
'year': str(show['year']), 'year': str(show['year']),
'alternateTitles': alternate_titles, 'alternateTitles': alternate_titles,
@ -107,20 +106,18 @@ def update_series():
series_to_add.append({'title': show["title"], series_to_add.append({'title': show["title"],
'path': show["path"], 'path': show["path"],
'tvdbId': show["tvdbId"], 'tvdbId': show["tvdbId"],
'languages': serie_default_language,
'hearing_impaired': serie_default_hi,
'sonarrSeriesId': show["id"], 'sonarrSeriesId': show["id"],
'overview': overview, 'overview': overview,
'poster': poster, 'poster': poster,
'fanart': fanart, 'fanart': fanart,
'audio_language': audio_language, 'audio_language': str(audio_language),
'sortTitle': show['sortTitle'], 'sortTitle': show['sortTitle'],
'year': str(show['year']), 'year': str(show['year']),
'alternateTitles': alternate_titles, 'alternateTitles': alternate_titles,
'forced': serie_default_forced,
'tags': str(tags), 'tags': str(tags),
'seriesType': show['seriesType'], 'seriesType': show['seriesType'],
'imdbId': imdbId}) 'imdbId': imdbId,
'profileId': serie_default_profile})
# Remove old series from DB # Remove old series from DB
removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr)) removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr))
@ -197,9 +194,11 @@ def get_profile_list():
def profile_id_to_language(id_, profiles): def profile_id_to_language(id_, profiles):
profiles_to_return = []
for profile in profiles: for profile in profiles:
if id_ == profile[0]: if id_ == profile[0]:
return profile[1] profiles_to_return.append(profile[1])
return profiles_to_return
def get_tags(): def get_tags():

View File

@ -30,7 +30,8 @@ from get_providers import get_providers, get_providers_auth, provider_throttle,
from knowit import api from knowit import api
from subsyncer import subsync from subsyncer import subsync
from guessit import guessit from guessit import guessit
from database import database, dict_mapper, get_exclusion_clause from database import database, dict_mapper, get_exclusion_clause, get_profiles_list, get_audio_profile_languages, \
get_desired_languages
from analytics import track_event from analytics import track_event
from locale import getpreferredencoding from locale import getpreferredencoding
@ -304,44 +305,48 @@ def download_subtitle(path, language, audio_language, hi, forced, providers, pro
logging.debug('BAZARR Ended searching Subtitles for file: ' + path) logging.debug('BAZARR Ended searching Subtitles for file: ' + path)
def manual_search(path, language, hi, forced, providers, providers_auth, sceneName, title, media_type): def manual_search(path, profileId, providers, providers_auth, sceneName, title, media_type):
logging.debug('BAZARR Manually searching subtitles for this file: ' + path) logging.debug('BAZARR Manually searching subtitles for this file: ' + path)
final_subtitles = [] final_subtitles = []
initial_hi = True if hi == "True" else False initial_language_set = set()
if hi == "True":
hi = "force HI"
else:
hi = "force non-HI"
language_set = set() language_set = set()
if forced == "True": # where [3] is items list of dict(id, lang, forced, hi)
providers_auth['podnapisi']['only_foreign'] = True language_items = ast.literal_eval(get_profiles_list(profile_id=int(profileId))['items'])
providers_auth['subscene']['only_foreign'] = True
providers_auth['opensubtitles']['only_foreign'] = True for language in language_items:
else: lang_id, lang, forced, hi, audio_exclude = language.values()
providers_auth['podnapisi']['only_foreign'] = False
providers_auth['subscene']['only_foreign'] = False
providers_auth['opensubtitles']['only_foreign'] = False
for lang in ast.literal_eval(language):
lang = alpha3_from_alpha2(lang) lang = alpha3_from_alpha2(lang)
if lang == 'pob': if lang == 'pob':
lang_obj = Language('por', 'BR') lang_obj = Language('por', 'BR')
if forced == "True":
lang_obj = Language.rebuild(lang_obj, forced=True)
else: else:
lang_obj = Language(lang) lang_obj = Language(lang)
if forced == "True":
lang_obj = Language.rebuild(lang_obj, forced=True)
language_set.add(lang_obj) if forced == "True":
lang_obj = Language.rebuild(lang_obj, forced=True)
if forced != "True": providers_auth['podnapisi']['also_foreign'] = True
lang_obj_hi = Language.rebuild(lang_obj, hi=True) providers_auth['opensubtitles']['also_foreign'] = True
language_set.add(lang_obj_hi)
if hi == "True":
lang_obj = Language.rebuild(lang_obj, hi=True)
initial_language_set.add(lang_obj)
language_set = initial_language_set.copy()
for language in language_set.copy():
lang_obj_for_hi = language
if not language.forced and not language.hi:
lang_obj_hi = Language.rebuild(lang_obj_for_hi, hi=True)
elif not language.forced and language.hi:
lang_obj_hi = Language.rebuild(lang_obj_for_hi, hi=False)
else:
continue
language_set.add(lang_obj_hi)
minimum_score = settings.general.minimum_score minimum_score = settings.general.minimum_score
minimum_score_movie = settings.general.minimum_score_movie minimum_score_movie = settings.general.minimum_score_movie
@ -365,6 +370,22 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa
blacklist=get_blacklist(media_type=media_type), blacklist=get_blacklist(media_type=media_type),
throttle_callback=provider_throttle, throttle_callback=provider_throttle,
language_hook=None) # fixme language_hook=None) # fixme
if 'subscene' in providers:
subscene_language_set = set()
for language in language_set:
if language.forced:
subscene_language_set.add(language)
if len(subscene_language_set):
providers_auth['subscene']['only_foreign'] = True
subtitles_subscene = list_all_subtitles([video], subscene_language_set,
providers=['subscene'],
provider_configs=providers_auth,
blacklist=get_blacklist(media_type=media_type),
throttle_callback=provider_throttle,
language_hook=None) # fixme
providers_auth['subscene']['only_foreign'] = False
subtitles[video] += subtitles_subscene[video]
else: else:
subtitles = [] subtitles = []
logging.info("BAZARR All providers are throttled") logging.info("BAZARR All providers are throttled")
@ -390,6 +411,20 @@ def manual_search(path, language, hi, forced, providers, providers_auth, sceneNa
logging.debug(u"BAZARR Skipping %s, because it doesn't match our series/episode", s) logging.debug(u"BAZARR Skipping %s, because it doesn't match our series/episode", s)
continue continue
initial_hi_match = False
for language in initial_language_set:
if s.language.basename == language.basename and \
s.language.forced == language.forced and \
s.language.hi == language.hi:
initial_hi = language.hi
initial_hi_match = True
break
if not initial_hi_match:
initial_hi = None
if initial_hi_match:
matches.add('hearing_impaired')
score, score_without_hash = compute_score(matches, s, video, hearing_impaired=initial_hi) score, score_without_hash = compute_score(matches, s, video, hearing_impaired=initial_hi)
if 'hash' not in matches: if 'hash' not in matches:
not_matched = scores - matches not_matched = scores - matches
@ -680,21 +715,14 @@ def manual_upload_subtitle(path, language, forced, title, scene_name, media_type
def series_download_subtitles(no): def series_download_subtitles(no):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, " episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, " "table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_shows.seriesType, table_episodes.audio_language FROM table_episodes " "table_shows.seriesType, table_episodes.audio_language, table_shows.title "
"INNER JOIN table_shows on table_shows.sonarrSeriesId = " "FROM table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_episodes.sonarrSeriesId WHERE table_episodes.sonarrSeriesId=? and " "table_episodes.sonarrSeriesId WHERE table_episodes.sonarrSeriesId=? and "
"missing_subtitles!='[]'" + get_exclusion_clause('series'), (no,)) "missing_subtitles!='[]'" + get_exclusion_clause('series'), (no,))
if not episodes_details: if not episodes_details:
logging.debug("BAZARR no episode for that sonarrSeriesId can be found in database:", str(no)) logging.debug("BAZARR no episode for that sonarrSeriesId can be found in database:", str(no))
return return
series_details = database.execute(
"SELECT hearing_impaired, title, forced FROM table_shows WHERE sonarrSeriesId=?",
(no,), only_one=True)
if not series_details:
logging.debug("BAZARR no series with that sonarrSeriesId can be found in database:", str(no))
return
providers_list = get_providers() providers_list = get_providers()
providers_auth = get_providers_auth() providers_auth = get_providers_auth()
@ -704,15 +732,21 @@ def series_download_subtitles(no):
if providers_list: if providers_list:
for language in ast.literal_eval(episode['missing_subtitles']): for language in ast.literal_eval(episode['missing_subtitles']):
if language is not None: if language is not None:
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
result = download_subtitle(path_mappings.path_replace(episode['path']), result = download_subtitle(path_mappings.path_replace(episode['path']),
str(alpha3_from_alpha2(language.split(':')[0])), str(alpha3_from_alpha2(language.split(':')[0])),
episode['audio_language'], audio_language,
series_details['hearing_impaired'], "True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False", "True" if language.endswith(':forced') else "False",
providers_list, providers_list,
providers_auth, providers_auth,
str(episode['scene_name']), str(episode['scene_name']),
series_details['title'], episode['title'],
'series') 'series')
if result is not None: if result is not None:
message = result[0] message = result[0]
@ -740,10 +774,9 @@ def series_download_subtitles(no):
def episode_download_subtitles(no): def episode_download_subtitles(no):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, " episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, " "table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_shows.hearing_impaired, table_shows.title, table_shows.sonarrSeriesId, " "table_shows.title, table_shows.sonarrSeriesId, table_episodes.audio_language, "
"table_shows.forced, table_episodes.audio_language, table_shows.seriesType FROM " "table_shows.seriesType FROM table_episodes LEFT JOIN table_shows on "
"table_episodes LEFT JOIN table_shows on table_episodes.sonarrSeriesId = " "table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId WHERE sonarrEpisodeId=?" +
"table_shows.sonarrSeriesId WHERE sonarrEpisodeId=?" +
get_exclusion_clause('series'), (no,)) get_exclusion_clause('series'), (no,))
if not episodes_details: if not episodes_details:
logging.debug("BAZARR no episode with that sonarrEpisodeId can be found in database:", str(no)) logging.debug("BAZARR no episode with that sonarrEpisodeId can be found in database:", str(no))
@ -756,10 +789,16 @@ def episode_download_subtitles(no):
if providers_list: if providers_list:
for language in ast.literal_eval(episode['missing_subtitles']): for language in ast.literal_eval(episode['missing_subtitles']):
if language is not None: if language is not None:
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
result = download_subtitle(path_mappings.path_replace(episode['path']), result = download_subtitle(path_mappings.path_replace(episode['path']),
str(alpha3_from_alpha2(language.split(':')[0])), str(alpha3_from_alpha2(language.split(':')[0])),
episode['audio_language'], audio_language,
episode['hearing_impaired'], "True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False", "True" if language.endswith(':forced') else "False",
providers_list, providers_list,
providers_auth, providers_auth,
@ -791,7 +830,7 @@ def episode_download_subtitles(no):
def movies_download_subtitles(no): def movies_download_subtitles(no):
movies = database.execute( movies = database.execute(
"SELECT path, missing_subtitles, audio_language, radarrId, sceneName, hearing_impaired, title, forced, tags, " "SELECT path, missing_subtitles, audio_language, radarrId, sceneName, title, tags, "
"monitored FROM table_movies WHERE radarrId=?" + get_exclusion_clause('movie'), (no,)) "monitored FROM table_movies WHERE radarrId=?" + get_exclusion_clause('movie'), (no,))
if not len(movies): if not len(movies):
logging.debug("BAZARR no movie with that radarrId can be found in database:", str(no)) logging.debug("BAZARR no movie with that radarrId can be found in database:", str(no))
@ -810,10 +849,16 @@ def movies_download_subtitles(no):
for i, language in enumerate(ast.literal_eval(movie['missing_subtitles']), 1): for i, language in enumerate(ast.literal_eval(movie['missing_subtitles']), 1):
if providers_list: if providers_list:
if language is not None: if language is not None:
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
result = download_subtitle(path_mappings.path_replace_movie(movie['path']), result = download_subtitle(path_mappings.path_replace_movie(movie['path']),
str(alpha3_from_alpha2(language.split(':')[0])), str(alpha3_from_alpha2(language.split(':')[0])),
movie['audio_language'], audio_language,
movie['hearing_impaired'], "True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False", "True" if language.endswith(':forced') else "False",
providers_list, providers_list,
providers_auth, providers_auth,
@ -845,8 +890,8 @@ def movies_download_subtitles(no):
def wanted_download_subtitles(path, l, count_episodes): def wanted_download_subtitles(path, l, count_episodes):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, " episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, "
"table_episodes.sonarrEpisodeId, table_episodes.sonarrSeriesId, " "table_episodes.sonarrEpisodeId, table_episodes.sonarrSeriesId, "
"table_shows.hearing_impaired, table_episodes.audio_language, table_episodes.scene_name," "table_episodes.audio_language, table_episodes.scene_name,"
"table_episodes.failedAttempts, table_shows.title, table_shows.forced " "table_episodes.failedAttempts, table_shows.title "
"FROM table_episodes LEFT JOIN table_shows on " "FROM table_episodes LEFT JOIN table_shows on "
"table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId " "table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId "
"WHERE table_episodes.path=? and table_episodes.missing_subtitles!='[]'", "WHERE table_episodes.path=? and table_episodes.missing_subtitles!='[]'",
@ -874,10 +919,16 @@ def wanted_download_subtitles(path, l, count_episodes):
for i in range(len(attempt)): for i in range(len(attempt)):
if attempt[i][0] == language: if attempt[i][0] == language:
if search_active(attempt[i][1]): if search_active(attempt[i][1]):
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
result = download_subtitle(path_mappings.path_replace(episode['path']), result = download_subtitle(path_mappings.path_replace(episode['path']),
str(alpha3_from_alpha2(language.split(':')[0])), str(alpha3_from_alpha2(language.split(':')[0])),
episode['audio_language'], audio_language,
episode['hearing_impaired'], "True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False", "True" if language.endswith(':forced') else "False",
providers_list, providers_list,
providers_auth, providers_auth,
@ -910,8 +961,8 @@ def wanted_download_subtitles(path, l, count_episodes):
def wanted_download_subtitles_movie(path, l, count_movies): def wanted_download_subtitles_movie(path, l, count_movies):
movies_details = database.execute( movies_details = database.execute(
"SELECT path, missing_subtitles, radarrId, hearing_impaired, audio_language, sceneName, " "SELECT path, missing_subtitles, radarrId, audio_language, sceneName, "
"failedAttempts, title, forced FROM table_movies WHERE path = ? " "failedAttempts, title FROM table_movies WHERE path = ? "
"AND missing_subtitles != '[]'", (path_mappings.path_replace_reverse_movie(path),)) "AND missing_subtitles != '[]'", (path_mappings.path_replace_reverse_movie(path),))
providers_list = get_providers() providers_list = get_providers()
@ -936,10 +987,16 @@ def wanted_download_subtitles_movie(path, l, count_movies):
for i in range(len(attempt)): for i in range(len(attempt)):
if attempt[i][0] == language: if attempt[i][0] == language:
if search_active(attempt[i][1]) is True: if search_active(attempt[i][1]) is True:
audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
result = download_subtitle(path_mappings.path_replace_movie(movie['path']), result = download_subtitle(path_mappings.path_replace_movie(movie['path']),
str(alpha3_from_alpha2(language.split(':')[0])), str(alpha3_from_alpha2(language.split(':')[0])),
movie['audio_language'], audio_language,
movie['hearing_impaired'], "True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False", "True" if language.endswith(':forced') else "False",
providers_list, providers_list,
providers_auth, providers_auth,
@ -1144,17 +1201,17 @@ def upgrade_subtitles():
if settings.general.getboolean('use_sonarr'): if settings.general.getboolean('use_sonarr'):
upgradable_episodes = database.execute("SELECT table_history.video_path, table_history.language, " upgradable_episodes = database.execute("SELECT table_history.video_path, table_history.language, "
"table_history.score, table_shows.hearing_impaired, " "table_history.score, table_shows.tags, table_shows.profileId, "
"table_episodes.audio_language, table_episodes.scene_name, table_episodes.title," "table_episodes.audio_language, table_episodes.scene_name, "
"table_episodes.sonarrSeriesId, table_episodes.sonarrEpisodeId," "table_episodes.title, table_episodes.sonarrSeriesId, "
"MAX(table_history.timestamp) as timestamp, table_episodes.monitored, " "table_episodes.sonarrEpisodeId, MAX(table_history.timestamp) "
"table_shows.languages, table_shows.forced, table_shows.tags, " "as timestamp, table_episodes.monitored, table_shows.seriesType FROM "
"table_shows.seriesType FROM table_history INNER JOIN table_shows on " "table_history INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_shows.sonarrSeriesId = table_history.sonarrSeriesId INNER JOIN " "table_history.sonarrSeriesId INNER JOIN table_episodes on "
"table_episodes on table_episodes.sonarrEpisodeId = " "table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
"table_history.sonarrEpisodeId WHERE action IN " "action IN (" + ','.join(map(str, query_actions)) +
"(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score" ") AND timestamp > ? AND score is not null" +
" is not null" + get_exclusion_clause('series') + " GROUP BY " get_exclusion_clause('series') + " GROUP BY "
"table_history.video_path, table_history.language", "table_history.video_path, table_history.language",
(minimum_timestamp,)) (minimum_timestamp,))
upgradable_episodes_not_perfect = [] upgradable_episodes_not_perfect = []
@ -1177,13 +1234,13 @@ def upgrade_subtitles():
if settings.general.getboolean('use_radarr'): if settings.general.getboolean('use_radarr'):
upgradable_movies = database.execute("SELECT table_history_movie.video_path, table_history_movie.language, " upgradable_movies = database.execute("SELECT table_history_movie.video_path, table_history_movie.language, "
"table_history_movie.score, table_movies.hearing_impaired, " "table_history_movie.score, table_movies.profileId, "
"table_movies.audio_language, table_movies.sceneName, table_movies.title, " "table_movies.audio_language, table_movies.sceneName, table_movies.title, "
"table_movies.radarrId, MAX(table_history_movie.timestamp) as timestamp, " "table_movies.radarrId, MAX(table_history_movie.timestamp) as timestamp, "
"table_movies.languages, table_movies.forced, table_movies.tags, " "table_movies.tags, table_movies.monitored FROM table_history_movie INNER "
"table_movies.monitored FROM table_history_movie INNER JOIN table_movies " "JOIN table_movies on table_movies.radarrId = "
"on table_movies.radarrId = table_history_movie.radarrId WHERE action IN " "table_history_movie.radarrId WHERE action IN (" +
"(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score " ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score "
"is not null" + get_exclusion_clause('movie') + " GROUP BY " "is not null" + get_exclusion_clause('movie') + " GROUP BY "
"table_history_movie.video_path, table_history_movie.language", "table_history_movie.video_path, table_history_movie.language",
(minimum_timestamp,)) (minimum_timestamp,))
@ -1210,110 +1267,113 @@ def upgrade_subtitles():
if settings.general.getboolean('use_sonarr'): if settings.general.getboolean('use_sonarr'):
for i, episode in enumerate(episodes_to_upgrade, 1): for i, episode in enumerate(episodes_to_upgrade, 1):
if episode['languages'] in [None, 'None', '[]']:
continue
providers = get_providers() providers = get_providers()
if not providers: if not providers:
logging.info("BAZARR All providers are throttled") logging.info("BAZARR All providers are throttled")
return return
if episode['languages']: if episode['language'].endswith('forced'):
desired_languages = ast.literal_eval(str(episode['languages'])) language = episode['language'].split(':')[0]
if episode['forced'] == "True": is_forced = True
forced_languages = [l + ":forced" for l in desired_languages] is_hi = False
elif episode['forced'] == "Both": elif episode['language'].endswith('hi'):
forced_languages = [l + ":forced" for l in desired_languages] + desired_languages language = episode['language'].split(':')[0]
is_forced = False
is_hi = True
else:
language = episode['language'].split(':')[0]
is_forced = False
is_hi = False
audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
if len(audio_language_list) > 0:
audio_language = audio_language_list[0]['name']
else:
audio_language = 'None'
result = download_subtitle(path_mappings.path_replace(episode['video_path']),
str(alpha3_from_alpha2(language)),
audio_language,
is_hi,
is_forced,
providers_list,
providers_auth,
str(episode['scene_name']),
episode['title'],
'series',
forced_minimum_score=int(episode['score']),
is_upgrade=True)
if result is not None:
message = result[0]
path = result[1]
forced = result[5]
if result[8]:
language_code = result[2] + ":hi"
elif forced:
language_code = result[2] + ":forced"
else: else:
forced_languages = desired_languages language_code = result[2]
provider = result[3]
if episode['language'] in forced_languages: score = result[4]
if episode['language'].endswith('forced'): subs_id = result[6]
language = episode['language'].split(':')[0] subs_path = result[7]
is_forced = "True" store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path']))
else: history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
language = episode['language'] language_code, provider, score, subs_id, subs_path)
is_forced = "False" send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
result = download_subtitle(path_mappings.path_replace(episode['video_path']),
str(alpha3_from_alpha2(language)),
episode['audio_language'],
episode['hearing_impaired'],
is_forced,
providers_list,
providers_auth,
str(episode['scene_name']),
episode['title'],
'series',
forced_minimum_score=int(episode['score']),
is_upgrade=True)
if result is not None:
message = result[0]
path = result[1]
forced = result[5]
if result[8]:
language_code = result[2] + ":hi"
elif forced:
language_code = result[2] + ":forced"
else:
language_code = result[2]
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles(episode['video_path'], path_mappings.path_replace(episode['video_path']))
history_log(3, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
language_code, provider, score, subs_id, subs_path)
send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
if settings.general.getboolean('use_radarr'): if settings.general.getboolean('use_radarr'):
for i, movie in enumerate(movies_to_upgrade, 1): for i, movie in enumerate(movies_to_upgrade, 1):
if movie['languages'] in [None, 'None', '[]']:
continue
providers = get_providers() providers = get_providers()
if not providers: if not providers:
logging.info("BAZARR All providers are throttled") logging.info("BAZARR All providers are throttled")
return return
if movie['languages']: if not providers:
desired_languages = ast.literal_eval(str(movie['languages'])) logging.info("BAZARR All providers are throttled")
if movie['forced'] == "True": return
forced_languages = [l + ":forced" for l in desired_languages] if episode['language'].endswith('forced'):
elif movie['forced'] == "Both": language = episode['language'].split(':')[0]
forced_languages = [l + ":forced" for l in desired_languages] + desired_languages is_forced = True
else: is_hi = False
forced_languages = desired_languages elif episode['language'].endswith('hi'):
language = episode['language'].split(':')[0]
is_forced = False
is_hi = True
else:
language = episode['language'].split(':')[0]
is_forced = False
is_hi = False
if movie['language'] in forced_languages: audio_language_list = get_audio_profile_languages(movie_id=movie['radarrId'])
if movie['language'].endswith('forced'): if len(audio_language_list) > 0:
language = movie['language'].split(':')[0] audio_language = audio_language_list[0]['name']
is_forced = "True" else:
else: audio_language = 'None'
language = movie['language']
is_forced = "False"
result = download_subtitle(path_mappings.path_replace_movie(movie['video_path']), result = download_subtitle(path_mappings.path_replace_movie(movie['video_path']),
str(alpha3_from_alpha2(language)), str(alpha3_from_alpha2(language)),
movie['audio_language'], audio_language,
movie['hearing_impaired'], is_hi,
is_forced, is_forced,
providers_list, providers_list,
providers_auth, providers_auth,
str(movie['sceneName']), str(movie['sceneName']),
movie['title'], movie['title'],
'movie', 'movie',
forced_minimum_score=int(movie['score']), forced_minimum_score=int(movie['score']),
is_upgrade=True) is_upgrade=True)
if result is not None: if result is not None:
message = result[0] message = result[0]
path = result[1] path = result[1]
forced = result[5] forced = result[5]
language_code = result[2] + ":forced" if forced else result[2] language_code = result[2] + ":forced" if forced else result[2]
provider = result[3] provider = result[3]
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']))
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)
def postprocessing(command, path): def postprocessing(command, path):

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import ast import ast
import os import os
import re import re

View File

@ -3,8 +3,6 @@
import os import os
import io import io
import rarfile import rarfile
import json
import hashlib
import sys import sys
import subprocess import subprocess

View File

@ -9,8 +9,8 @@ from guess_language import guess_language
from subliminal_patch import core, search_external_subtitles from subliminal_patch import core, search_external_subtitles
from subzero.language import Language from subzero.language import Language
from database import database from database import database, get_profiles_list, get_profile_cutoff
from get_languages import alpha2_from_alpha3, get_language_set from get_languages import alpha2_from_alpha3, language_from_alpha2, get_language_set
from config import settings from config import settings
from helper import path_mappings, get_subtitle_destination_folder from helper import path_mappings, get_subtitle_destination_folder
@ -203,157 +203,212 @@ def store_subtitles_movie(original_path, reversed_path):
def list_missing_subtitles(no=None, epno=None, send_event=True): def list_missing_subtitles(no=None, epno=None, send_event=True):
if no is not None: if epno is not None:
episodes_subtitles_clause = " WHERE table_episodes.sonarrSeriesId=" + str(no)
elif epno is not None:
episodes_subtitles_clause = " WHERE table_episodes.sonarrEpisodeId=" + str(epno) episodes_subtitles_clause = " WHERE table_episodes.sonarrEpisodeId=" + str(epno)
elif no is not None:
episodes_subtitles_clause = " WHERE table_episodes.sonarrSeriesId=" + str(no)
else: else:
episodes_subtitles_clause = "" episodes_subtitles_clause = ""
episodes_subtitles = database.execute("SELECT table_shows.sonarrSeriesId, table_episodes.sonarrEpisodeId, " episodes_subtitles = database.execute("SELECT table_shows.sonarrSeriesId, table_episodes.sonarrEpisodeId, "
"table_episodes.subtitles, table_shows.languages, table_shows.forced, " "table_episodes.subtitles, table_shows.profileId, "
"table_shows.hearing_impaired FROM table_episodes LEFT JOIN table_shows " "table_episodes.audio_language FROM table_episodes "
"on table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId" + "LEFT JOIN table_shows on table_episodes.sonarrSeriesId = "
episodes_subtitles_clause) "table_shows.sonarrSeriesId" + episodes_subtitles_clause)
if isinstance(episodes_subtitles, str): if isinstance(episodes_subtitles, str):
logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + episodes_subtitles) logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + episodes_subtitles)
return return
missing_subtitles_global = []
use_embedded_subs = settings.general.getboolean('use_embedded_subs') use_embedded_subs = settings.general.getboolean('use_embedded_subs')
for episode_subtitles in episodes_subtitles: for episode_subtitles in episodes_subtitles:
actual_subtitles_temp = [] missing_subtitles_text = '[]'
desired_subtitles_temp = [] if episode_subtitles['profileId']:
actual_subtitles = [] # get desired subtitles
desired_subtitles = [] desired_subtitles_temp = get_profiles_list(profile_id=episode_subtitles['profileId'])
missing_subtitles = [] desired_subtitles_list = []
if episode_subtitles['subtitles'] is not None: if desired_subtitles_temp:
if use_embedded_subs: for language in ast.literal_eval(desired_subtitles_temp['items']):
actual_subtitles = ast.literal_eval(episode_subtitles['subtitles']) if language['audio_exclude'] == "True":
else: if language_from_alpha2(language['language']) in ast.literal_eval(episode_subtitles['audio_language']):
actual_subtitles_temp = ast.literal_eval(episode_subtitles['subtitles']) continue
for subtitle in actual_subtitles_temp: desired_subtitles_list.append([language['language'], language['forced'], language['hi']])
if subtitle[1] is not None:
actual_subtitles.append(subtitle)
if episode_subtitles['languages'] is not None:
desired_subtitles = ast.literal_eval(episode_subtitles['languages'])
if desired_subtitles:
desired_subtitles_enum = enumerate(desired_subtitles)
else:
desired_subtitles_enum = None
if episode_subtitles['hearing_impaired'] == "True" and desired_subtitles is not None: # get existing subtitles
for i, desired_subtitle in desired_subtitles_enum: actual_subtitles_list = []
desired_subtitles[i] = desired_subtitle + ":hi" if episode_subtitles['subtitles'] is not None:
elif episode_subtitles['forced'] == "True" and desired_subtitles is not None: if use_embedded_subs:
for i, desired_subtitle in desired_subtitles_enum: actual_subtitles_temp = ast.literal_eval(episode_subtitles['subtitles'])
desired_subtitles[i] = desired_subtitle + ":forced"
elif episode_subtitles['forced'] == "Both" and desired_subtitles is not None:
for desired_subtitle in desired_subtitles:
desired_subtitles_temp.append(desired_subtitle)
desired_subtitles_temp.append(desired_subtitle + ":forced")
desired_subtitles = desired_subtitles_temp
actual_subtitles_list = []
if desired_subtitles is None:
missing_subtitles_global.append(tuple(['[]', episode_subtitles['sonarrEpisodeId'],
episode_subtitles['sonarrSeriesId']]))
else:
for item in actual_subtitles:
if item[0] == "pt-BR":
actual_subtitles_list.append("pb")
elif item[0] == "pt-BR:forced":
actual_subtitles_list.append("pb:forced")
else: else:
actual_subtitles_list.append(item[0]) actual_subtitles_temp = [x for x in ast.literal_eval(episode_subtitles['subtitles']) if x[1]]
missing_subtitles = list(set(desired_subtitles) - set(actual_subtitles_list))
hi_subs_to_remove = [] for subtitles in actual_subtitles_temp:
for item in missing_subtitles: subtitles = subtitles[0].split(':')
if item + ':hi' in actual_subtitles_list: lang = subtitles[0]
hi_subs_to_remove.append(item) forced = False
missing_subtitles = list(set(missing_subtitles) - set(hi_subs_to_remove)) hi = False
missing_subtitles_global.append(tuple([str(missing_subtitles), episode_subtitles['sonarrEpisodeId'], if len(subtitles) > 1:
episode_subtitles['sonarrSeriesId']])) if subtitles[1] == 'forced':
forced = True
hi = False
elif subtitles[1] == 'hi':
forced = False
hi = True
actual_subtitles_list.append([lang, str(forced), str(hi)])
# check if cutoff is reached and skip any further check
cutoff_met = False
cutoff_temp_list = get_profile_cutoff(profile_id=episode_subtitles['profileId'])
if cutoff_temp_list:
for cutoff_temp in cutoff_temp_list:
cutoff_language = [cutoff_temp['language'], cutoff_temp['forced'], cutoff_temp['hi']]
if cutoff_language in actual_subtitles_list:
cutoff_met = True
missing_subtitles_text = str([])
elif cutoff_language and [cutoff_language[0], 'True', 'False'] in actual_subtitles_list:
cutoff_met = True
missing_subtitles_text = str([])
elif cutoff_language and [cutoff_language[0], 'False', 'True'] in actual_subtitles_list:
cutoff_met = True
missing_subtitles_text = str([])
if not cutoff_met:
# if cutoff isn't met or None, we continue
# get difference between desired and existing subtitles
missing_subtitles_list = []
for item in desired_subtitles_list:
if item not in actual_subtitles_list:
missing_subtitles_list.append(item)
# remove missing that have forced or hi subtitles for this language in existing
for item in actual_subtitles_list:
if item[1] == 'True' or item[2] == 'True':
try:
missing_subtitles_list.remove([item[0], 'False', 'False'])
except ValueError:
pass
# make the missing languages list looks like expected
missing_subtitles_output_list = []
for item in missing_subtitles_list:
lang = item[0]
if item[1] == 'True':
lang += ':forced'
elif item[2] == 'True':
lang += ':hi'
missing_subtitles_output_list.append(lang)
missing_subtitles_text = str(missing_subtitles_output_list)
for missing_subtitles_item in missing_subtitles_global:
database.execute("UPDATE table_episodes SET missing_subtitles=? WHERE sonarrEpisodeId=?", database.execute("UPDATE table_episodes SET missing_subtitles=? WHERE sonarrEpisodeId=?",
(missing_subtitles_item[0], missing_subtitles_item[1])) (missing_subtitles_text, episode_subtitles['sonarrEpisodeId']))
if send_event: if send_event:
event_stream(type='episode', action='update', series=missing_subtitles_item[2], event_stream(type='episode', action='update', series=episode_subtitles['sonarrSeriesId'],
episode=missing_subtitles_item[1]) episode=episode_subtitles['sonarrEpisodeId'])
event_stream(type='badges_series') event_stream(type='badges_series')
def list_missing_subtitles_movies(no=None, send_event=True): def list_missing_subtitles_movies(no=None, epno=None, send_event=True):
if no is not None: if no is not None:
movies_subtitles_clause = " WHERE radarrId=" + str(no) movies_subtitles_clause = " WHERE radarrId=" + str(no)
else: else:
movies_subtitles_clause = "" movies_subtitles_clause = ""
movies_subtitles = database.execute("SELECT radarrId, subtitles, languages, forced, hearing_impaired FROM " movies_subtitles = database.execute("SELECT radarrId, subtitles, profileId, audio_language FROM table_movies" +
"table_movies" + movies_subtitles_clause) movies_subtitles_clause)
if isinstance(movies_subtitles, str): if isinstance(movies_subtitles, str):
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
missing_subtitles_global = []
use_embedded_subs = settings.general.getboolean('use_embedded_subs')
for movie_subtitles in movies_subtitles:
actual_subtitles_temp = []
desired_subtitles_temp = []
actual_subtitles = []
desired_subtitles = []
missing_subtitles = []
if movie_subtitles['subtitles'] is not None:
if use_embedded_subs:
actual_subtitles = ast.literal_eval(movie_subtitles['subtitles'])
else:
actual_subtitles_temp = ast.literal_eval(movie_subtitles['subtitles'])
for subtitle in actual_subtitles_temp:
if subtitle[1] is not None:
actual_subtitles.append(subtitle)
if movie_subtitles['languages'] is not None:
desired_subtitles = ast.literal_eval(movie_subtitles['languages'])
if desired_subtitles:
desired_subtitles_enum = enumerate(desired_subtitles)
else:
desired_subtitles_enum = None
if movie_subtitles['hearing_impaired'] == "True" and desired_subtitles is not None:
for i, desired_subtitle in desired_subtitles_enum: use_embedded_subs = settings.general.getboolean('use_embedded_subs')
desired_subtitles[i] = desired_subtitle + ":hi"
elif movie_subtitles['forced'] == "True" and desired_subtitles is not None: for movie_subtitles in movies_subtitles:
for i, desired_subtitle in desired_subtitles_enum: missing_subtitles_text = '[]'
desired_subtitles[i] = desired_subtitle + ":forced" if movie_subtitles['profileId']:
elif movie_subtitles['forced'] == "Both" and desired_subtitles is not None: # get desired subtitles
for desired_subtitle in desired_subtitles: desired_subtitles_temp = get_profiles_list(profile_id=movie_subtitles['profileId'])
desired_subtitles_temp.append(desired_subtitle) desired_subtitles_list = []
desired_subtitles_temp.append(desired_subtitle + ":forced") if desired_subtitles_temp:
desired_subtitles = desired_subtitles_temp for language in ast.literal_eval(desired_subtitles_temp['items']):
actual_subtitles_list = [] if language['audio_exclude'] == "True":
if desired_subtitles is None: if language_from_alpha2(language['language']) in ast.literal_eval(movie_subtitles['audio_language']):
missing_subtitles_global.append(tuple(['[]', movie_subtitles['radarrId']])) continue
else: desired_subtitles_list.append([language['language'], language['forced'], language['hi']])
for item in actual_subtitles:
if item[0] == "pt-BR": # get existing subtitles
actual_subtitles_list.append("pb") actual_subtitles_list = []
elif item[0] == "pt-BR:forced": if movie_subtitles['subtitles'] is not None:
actual_subtitles_list.append("pb:forced") if use_embedded_subs:
actual_subtitles_temp = ast.literal_eval(movie_subtitles['subtitles'])
else: else:
actual_subtitles_list.append(item[0]) actual_subtitles_temp = [x for x in ast.literal_eval(movie_subtitles['subtitles']) if x[1]]
missing_subtitles = list(set(desired_subtitles) - set(actual_subtitles_list))
hi_subs_to_remove = [] for subtitles in actual_subtitles_temp:
for item in missing_subtitles: subtitles = subtitles[0].split(':')
if item + ':hi' in actual_subtitles_list: lang = subtitles[0]
hi_subs_to_remove.append(item) forced = False
missing_subtitles = list(set(missing_subtitles) - set(hi_subs_to_remove)) hi = False
missing_subtitles_global.append(tuple([str(missing_subtitles), movie_subtitles['radarrId']])) if len(subtitles) > 1:
if subtitles[1] == 'forced':
for missing_subtitles_item in missing_subtitles_global: forced = True
hi = False
elif subtitles[1] == 'hi':
forced = False
hi = True
actual_subtitles_list.append([lang, str(forced), str(hi)])
# check if cutoff is reached and skip any further check
cutoff_met = False
cutoff_temp_list = get_profile_cutoff(profile_id=movie_subtitles['profileId'])
if cutoff_temp_list:
for cutoff_temp in cutoff_temp_list:
cutoff_language = [cutoff_temp['language'], cutoff_temp['forced'], cutoff_temp['hi']]
if cutoff_language in actual_subtitles_list:
cutoff_met = True
missing_subtitles_text = str([])
elif cutoff_language and [cutoff_language[0], 'True', 'False'] in actual_subtitles_list:
cutoff_met = True
missing_subtitles_text = str([])
elif cutoff_language and [cutoff_language[0], 'False', 'True'] in actual_subtitles_list:
cutoff_met = True
missing_subtitles_text = str([])
if not cutoff_met:
# get difference between desired and existing subtitles
missing_subtitles_list = []
for item in desired_subtitles_list:
if item not in actual_subtitles_list:
missing_subtitles_list.append(item)
# remove missing that have forced or hi subtitles for this language in existing
for item in actual_subtitles_list:
if item[1] == 'True' or item[2] == 'True':
try:
missing_subtitles_list.remove([item[0], 'False', 'False'])
except ValueError:
pass
# make the missing languages list looks like expected
missing_subtitles_output_list = []
for item in missing_subtitles_list:
lang = item[0]
if item[1] == 'True':
lang += ':forced'
elif item[2] == 'True':
lang += ':hi'
missing_subtitles_output_list.append(lang)
missing_subtitles_text = str(missing_subtitles_output_list)
database.execute("UPDATE table_movies SET missing_subtitles=? WHERE radarrId=?", database.execute("UPDATE table_movies SET missing_subtitles=? WHERE radarrId=?",
(missing_subtitles_item[0], missing_subtitles_item[1])) (missing_subtitles_text, movie_subtitles['radarrId']))
if send_event: if send_event:
event_stream(type='movie', action='update', movie=missing_subtitles_item[1]) event_stream(type='movie', action='update', movie=movie_subtitles['radarrId'])
event_stream(type='badges_movies') event_stream(type='badges_movies')

View File

@ -1,32 +1,29 @@
# coding=utf-8 # coding=utf-8
bazarr_version = '0.9.0.8' bazarr_version = '0.9.1'
import os import os
os.environ["BAZARR_VERSION"] = bazarr_version os.environ["BAZARR_VERSION"] = bazarr_version
import gc import gc
import sys
import libs import libs
import hashlib import hashlib
import apprise import apprise
import requests
import calendar import calendar
from get_args import args from get_args import args
from logger import empty_log from logger import empty_log
from config import settings, url_sonarr, url_radarr, url_radarr_short, url_sonarr_short, base_url, configure_proxy_func from config import settings, url_sonarr, url_radarr, configure_proxy_func
from init import * from init import *
from database import database, dict_mapper from database import database
from notifier import update_notifier from notifier import update_notifier
from urllib.parse import unquote from urllib.parse import unquote
from get_languages import load_language_in_db, language_from_alpha3, language_from_alpha2, alpha2_from_alpha3, \ from get_languages import load_language_in_db, language_from_alpha2, alpha3_from_alpha2
alpha3_from_alpha2
from flask import make_response, request, redirect, abort, render_template, Response, session, flash, url_for, \ from flask import make_response, request, redirect, abort, render_template, Response, session, flash, url_for, \
send_file, stream_with_context send_file, stream_with_context

View File

@ -4,7 +4,8 @@ from get_episodes import sync_episodes, update_all_episodes
from get_movies import update_movies, update_all_movies from get_movies import update_movies, update_all_movies
from get_series import update_series from get_series import update_series
from config import settings from config import settings
from get_subtitle import wanted_search_missing_subtitles_series, wanted_search_missing_subtitles_movies, upgrade_subtitles from get_subtitle import wanted_search_missing_subtitles_series, wanted_search_missing_subtitles_movies, \
upgrade_subtitles
from utils import cache_maintenance from utils import cache_maintenance
from get_args import args from get_args import args
if not args.no_update: if not args.no_update:
@ -21,6 +22,7 @@ from calendar import day_name
import pretty import pretty
from random import randrange from random import randrange
from event_handler import event_stream from event_handler import event_stream
import os
class Scheduler: class Scheduler:
@ -62,10 +64,10 @@ class Scheduler:
if args.no_tasks: if args.no_tasks:
self.__no_task() self.__no_task()
def add_job(self, job, name=None, max_instances=1, coalesce=True, args=None): def add_job(self, job, name=None, max_instances=1, coalesce=True, args=None, kwargs=None):
self.aps_scheduler.add_job( self.aps_scheduler.add_job(
job, DateTrigger(run_date=datetime.now()), name=name, id=name, max_instances=max_instances, job, DateTrigger(run_date=datetime.now()), name=name, id=name, max_instances=max_instances,
coalesce=coalesce, args=args) coalesce=coalesce, args=args, kwargs=kwargs)
def execute_job_now(self, taskid): def execute_job_now(self, taskid):
self.aps_scheduler.modify_job(taskid, next_run_time=datetime.now()) self.aps_scheduler.modify_job(taskid, next_run_time=datetime.now())
@ -252,3 +254,12 @@ class Scheduler:
scheduler = Scheduler() scheduler = Scheduler()
# Force the execution of the sync process with Sonarr and Radarr after migration to v0.9.1
if 'BAZARR_AUDIO_PROFILES_MIGRATION' in os.environ:
if settings.general.getboolean('use_sonarr'):
scheduler.aps_scheduler.modify_job('update_series', next_run_time=datetime.now())
scheduler.aps_scheduler.modify_job('sync_episodes', next_run_time=datetime.now())
if settings.general.getboolean('use_radarr'):
scheduler.aps_scheduler.modify_job('update_movies', next_run_time=datetime.now())
del os.environ['BAZARR_AUDIO_PROFILES_MIGRATION']

View File

@ -1,3 +1,5 @@
# coding=utf-8
import warnings import warnings
import logging import logging
import os import os

View File

@ -823,9 +823,8 @@ def get_subtitle_path(video_path, language=None, extension='.srt', forced_tag=Fa
if forced_tag: if forced_tag:
tags.append("forced") tags.append("forced")
# fixme when we'll be ready to add .hi to filename when saving a subtitles elif hi_tag:
# elif hi_tag: tags.append("hi")
# tags.append("hi")
if language: if language:
subtitle_root += '.' + str(language.basename) subtitle_root += '.' + str(language.basename)

View File

@ -269,11 +269,11 @@
class="fas fa-cogs"></i><span class="hide-menu"> Settings</span></a> class="fas fa-cogs"></i><span class="hide-menu"> Settings</span></a>
<ul aria-expanded="false" class="collapse"> <ul aria-expanded="false" class="collapse">
<li><a href="{{ url_for('settingsgeneral') }}"> General</a></li> <li><a href="{{ url_for('settingsgeneral') }}"> General</a></li>
<li><a href="{{ url_for('settingssonarr') }}"> Sonarr</a></li>
<li><a href="{{ url_for('settingsradarr') }}"> Radarr</a></li>
<li><a href="{{ url_for('settingssubtitles') }}"> Subtitles</a></li>
<li><a href="{{ url_for('settingslanguages') }}"> Languages</a></li> <li><a href="{{ url_for('settingslanguages') }}"> Languages</a></li>
<li><a href="{{ url_for('settingsproviders') }}"> Providers</a></li> <li><a href="{{ url_for('settingsproviders') }}"> Providers</a></li>
<li><a href="{{ url_for('settingssubtitles') }}"> Subtitles</a></li>
<li><a href="{{ url_for('settingssonarr') }}"> Sonarr</a></li>
<li><a href="{{ url_for('settingsradarr') }}"> Radarr</a></li>
<li><a href="{{ url_for('settingsnotifications') }}"> Notifications</a></li> <li><a href="{{ url_for('settingsnotifications') }}"> Notifications</a></li>
<li><a href="{{ url_for('settingsscheduler') }}"> Scheduler</a></li> <li><a href="{{ url_for('settingsscheduler') }}"> Scheduler</a></li>
</ul> </ul>

View File

@ -87,7 +87,9 @@
title="None" data-html="true"></i> title="None" data-html="true"></i>
</div> </div>
<div class="row"> <div class="row">
<h5><span id="seriesAudioLanguage" class="badge badge-secondary"></span></h5> <h5><span id="seriesAudioLanguage"></span></h5>
</div>
<div class="row">
<h5><span id="seriesMappedPath" class="badge badge-secondary"></span></h5> <h5><span id="seriesMappedPath" class="badge badge-secondary"></span></h5>
<h5><span id="seriesFileCount" class="badge badge-secondary"></span></h5> <h5><span id="seriesFileCount" class="badge badge-secondary"></span></h5>
<h5><span id="seriesType" class="badge badge-secondary"></span></h5> <h5><span id="seriesType" class="badge badge-secondary"></span></h5>
@ -95,7 +97,7 @@
title="None" data-html="true">Tags</span></h5> title="None" data-html="true">Tags</span></h5>
</div> </div>
<div class="row"> <div class="row">
<h5><span id="seriesSubtitlesLanguages"></span></h5> <h5><span id="seriesSubtitlesLanguagesProfile" class="badge badge-secondary"></span></h5>
</div> </div>
<div class="row"> <div class="row">
<h5><span id="seriesHearingImpaired" class="badge badge-secondary"></span></h5> <h5><span id="seriesHearingImpaired" class="badge badge-secondary"></span></h5>
@ -117,7 +119,7 @@
<th></th> <th></th>
<th>Episode</th> <th>Episode</th>
<th>Title</th> <th>Title</th>
<th>Audio Language</th> <th>Audio Languages</th>
<th>Existing Subtitles</th> <th>Existing Subtitles</th>
<th>Missing Subtitles</th> <th>Missing Subtitles</th>
<th>Manual Search</th> <th>Manual Search</th>
@ -287,42 +289,19 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
Audio Profile Audio Profile Languages
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<span id="edit_audio_language_span"></span> <span id="edit_audio_language_span"></span>
</div> </div>
</div> </div>
<br>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
Subtitles Language(s) Languages Profile
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_languages_select" name="languages" multiple <select class="selectpicker" id="edit_languages_select" name="languages"></select>
data-live-search="true"></select>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Hearing-Impaired
</div>
<div class="form-group col-sm-1 pl-sm-0">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="hi_checkbox" name="hi">
<span class="custom-control-label" for="hi_checkbox"></span>
</label>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Forced
</div>
<div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_forced_select" name="forced">
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@ -564,6 +543,7 @@
episodesDetailsRefresh(); episodesDetailsRefresh();
getLanguages(); getLanguages();
getEnabledLanguages(); getEnabledLanguages();
getLanguagesProfiles();
var collapsedGroups = {}; var collapsedGroups = {};
@ -636,7 +616,16 @@
} }
}, },
{ {
data: 'audio_language.name' data: 'audio_language',
render: function (data) {
var audio_languages = '';
data.forEach(appendFunc);
return audio_languages;
function appendFunc(value) {
audio_languages = audio_languages + '<span class="badge badge-secondary">' + value.name + '</span> ';
}
}
}, },
{ {
data: null, data: null,
@ -686,7 +675,7 @@
var advtag = ''; var advtag = '';
} }
languages = languages + '<a href="" class="get_subtitle badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + advtag + '" data-episodepath="' + data.mapped_path + '" data-scenename="' + data.scene_name + '" data-title="' + data.title + '" data-language="' + value.code3 + '" data-hi="' + seriesDetails.hearing_impaired + '" data-forced="' + value.forced + '" data-sonarrepisodeid=' + data.sonarrEpisodeId + '>' + value.code2 + advtag + ' <i class="fas fa-search"></i></a> '; languages = languages + '<a href="" class="get_subtitle badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + advtag + '" data-episodepath="' + data.mapped_path + '" data-scenename="' + data.scene_name + '" data-title="' + data.title + '" data-language="' + value.code3 + '" data-hi="' + value.hi + '" data-forced="' + value.forced + '" data-sonarrepisodeid=' + data.sonarrEpisodeId + '>' + value.code2 + advtag + ' <i class="fas fa-search"></i></a> ';
} }
} }
}, },
@ -694,7 +683,7 @@
data: null, data: null,
render: function (data) { render: function (data) {
if (data.desired_languages !== '[]') { if (data.desired_languages !== '[]') {
return '<a href="" class="manual_search badge badge-secondary" data-season=' + data.season + ' data-episode=' + data.episode + ' data-episode_title="' + data.title + '" data-episodePath="' + data.mapped_path + '" data-sceneName="' + data.scene_name + '" data-language="' + data.desired_languages + '" data-sonarrEpisodeId=' + data.sonarrEpisodeId + '><i class="fas fa-user"></i></a>'; return '<a href="" class="manual_search badge badge-secondary" data-season=' + data.season + ' data-episode=' + data.episode + ' data-episode_title="' + data.title + '" data-episodePath="' + data.mapped_path + '" data-sceneName="' + data.scene_name + '" data-sonarrEpisodeId=' + data.sonarrEpisodeId + '><i class="fas fa-user"></i></a>';
} else { } else {
return '' return ''
} }
@ -704,7 +693,7 @@
data: null, data: null,
render: function (data) { render: function (data) {
if (data.desired_languages !== '[]') { if (data.desired_languages !== '[]') {
return '<a href="" class="upload_subtitle badge badge-secondary" data-episodePath="' + data.mapped_path + '" data-sceneName"' + data.scene_name + '" data-sonarrSeriesId="' + seriesDetails['sonarrSeriesId'] + '" data-sonarrEpisodeId="' + data.sonarrEpisodeId + '" data-season="' + data.season + '" data-episode="' + data.episode + '" data-episode_title="' + data.title + '" data-audio_language="' + data.audio_language.name + '"><i class="fas fa-cloud-upload-alt"></i></a>'; return '<a href="" class="upload_subtitle badge badge-secondary" data-episodePath="' + data.mapped_path + '" data-sceneName"' + data.scene_name + '" data-sonarrSeriesId="' + seriesDetails['sonarrSeriesId'] + '" data-sonarrEpisodeId="' + data.sonarrEpisodeId + '" data-season="' + data.season + '" data-episode="' + data.episode + '" data-episode_title="' + data.title + '" data-audio_language=\'' + JSON.stringify(data.audio_language) + '\'><i class="fas fa-cloud-upload-alt"></i></a>';
} else { } else {
return '' return ''
} }
@ -783,9 +772,7 @@
episodePath = $(this).attr("data-episodePath"); episodePath = $(this).attr("data-episodePath");
sceneName = $(this).attr("data-sceneName"); sceneName = $(this).attr("data-sceneName");
language = $(this).attr("data-language"); profileId = seriesDetails['profileId'].id;
hi = seriesDetails['hearing_impaired'];
forced = seriesDetails['forced'];
sonarrSeriesId = seriesDetails['sonarrSeriesId']; sonarrSeriesId = seriesDetails['sonarrSeriesId'];
sonarrEpisodeId = $(this).attr("data-sonarrEpisodeId"); sonarrEpisodeId = $(this).attr("data-sonarrEpisodeId");
var languages = Array.from(seriesDetails['languages']); var languages = Array.from(seriesDetails['languages']);
@ -795,9 +782,7 @@
const values = { const values = {
episodePath: episodePath, episodePath: episodePath,
sceneName: sceneName, sceneName: sceneName,
language: language, profileId: profileId,
hi: hi,
forced: forced,
sonarrSeriesId: sonarrSeriesId, sonarrSeriesId: sonarrSeriesId,
sonarrEpisodeId: sonarrEpisodeId, sonarrEpisodeId: sonarrEpisodeId,
title: seriesDetails['title'] title: seriesDetails['title']
@ -906,7 +891,7 @@
data: null, data: null,
searchable: false, searchable: false,
render: function (data) { render: function (data) {
return '<a href="" class="manual_download badge badge-secondary" data-episodePath="' + episodePath + '" data-sceneName="' + sceneName + '" data-sonarrEpisodeId=' + sonarrEpisodeId + ' data-subtitle="' + data.subtitle + '" data-provider="' + data.provider + '" data-language="' + data.language + '" data-forced="' + forced + '"><i class="fas fa-download" style="margin-right:0px" ></i></a>'; return '<a href="" class="manual_download badge badge-secondary" data-episodePath="' + episodePath + '" data-sceneName="' + sceneName + '" data-sonarrEpisodeId=' + sonarrEpisodeId + ' data-subtitle="' + data.subtitle + '" data-provider="' + data.provider + '" data-language="' + data.language + '" data-forced="' + data.forced + '"><i class="fas fa-download" style="margin-right:0px" ></i></a>';
} }
} }
] ]
@ -956,7 +941,7 @@
$('#upload_sonarrSeriesId').val($(this).data("sonarrseriesid")); $('#upload_sonarrSeriesId').val($(this).data("sonarrseriesid"));
$('#upload_sonarrEpisodeId').val($(this).data("sonarrepisodeid")); $('#upload_sonarrEpisodeId').val($(this).data("sonarrepisodeid"));
$('#upload_title').val($(this).data("episode_title")); $('#upload_title').val($(this).data("episode_title"));
$('#upload_audioLanguage').val($(this).data("audio_language")); $('#upload_audioLanguage').val(($(this).data("audio_language").length) ? $(this).data("audio_language")[0].name : 'None');
$('#manual_language_select').empty(); $('#manual_language_select').empty();
$.each(enabledLanguages, function (i, item) { $.each(enabledLanguages, function (i, item) {
@ -1340,26 +1325,20 @@
$('#edit_button').on('click', function (e) { $('#edit_button').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$("#edit_series_title_span").html(seriesDetails['title']); $("#edit_series_title_span").html(seriesDetails['title']);
$("#edit_audio_language_span").text(seriesDetails['audio_language']['name']); $("#edit_audio_language_span").empty();
$.each(seriesDetails['audio_language'], function (i, item) {
$("#edit_audio_language_span").append('<div class="badge badge-secondary">' + item['name'] + '</div> ');
})
$('#edit_sonarrSeriesId').val(seriesDetails['sonarrSeriesId']); $('#edit_sonarrSeriesId').val(seriesDetails['sonarrSeriesId']);
$('#edit_languages_select').empty(); $('#edit_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') { $('#edit_languages_select').append('<option value="None">None</option>');
$('#edit_languages_select').selectpicker({maxOptions: 1}); $.each(languagesProfiles, function (i, item) {
$('#edit_languages_select').append('<option value="None">None</option>'); $('#edit_languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
}
$.each(enabledLanguages, function (i, item) {
$('#edit_languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>');
}); });
$("#edit_languages_select").selectpicker("refresh"); $("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array(); $('#edit_languages_select').selectpicker('val', ((seriesDetails['profileId'].id) ? seriesDetails['profileId'].id : 'None'));
$.each(Array.from(seriesDetails['languages']), function (i, item) {
selected_languages.push(item.code2);
});
$('#edit_languages_select').selectpicker('val', selected_languages);
$('#hi_checkbox').prop('checked', (seriesDetails['hearing_impaired'] === 'True'));
$('#edit_forced_select').val(seriesDetails['forced']).change();
$('#editModal') $('#editModal')
.modal({ .modal({
@ -1826,30 +1805,23 @@
$('#seriesTags').hide(); $('#seriesTags').hide();
} }
$('#seriesAudioLanguage').text(seriesDetails['audio_language']['name']); $("#seriesAudioLanguage").empty();
$.each(seriesDetails['audio_language'], function (i, item) {
$("#seriesAudioLanguage").append('<div class="badge badge-secondary"><i class="fa fa-music"></i> ' + item['name'] + '</div> ');
})
$('#seriesMappedPath').text(seriesDetails['mapped_path']); $('#seriesMappedPath').text(seriesDetails['mapped_path']);
$('#seriesMappedPath').attr("data-original-title", seriesDetails['mapped_path']); $('#seriesMappedPath').attr("data-original-title", seriesDetails['mapped_path']);
$('#seriesFileCount').text(seriesDetails['episodeFileCount'] + ' files'); $('#seriesFileCount').text(seriesDetails['episodeFileCount'] + ' files');
$('#seriesType').text(seriesDetails['seriesType']); $('#seriesType').text(seriesDetails['seriesType']);
var languages = ''; $('#seriesSubtitlesLanguagesProfile').text(seriesDetails['profileId'].name);
if (seriesDetails['languages'] && seriesDetails['languages'] !== 'None') {
seriesDetails['languages'].forEach(appendFunc);
}
function appendFunc(value) {
languages = languages + '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + '">' + value.code2 + '</span> ';
}
$('#seriesSubtitlesLanguages').html(languages);
$('#seriesHearingImpaired').text('Hearing-Impaired: ' + seriesDetails['hearing_impaired']);
$('#seriesForced').text('Forced: ' + seriesDetails['forced']);
$('#seriesDescription').text(seriesDetails['overview']); $('#seriesDescription').text(seriesDetails['overview']);
if (seriesDetails['desired_languages'] == '[]') { if (seriesDetails['profileId'].id) {
$('#search_button').hide();
} else {
$('#search_button').show(); $('#search_button').show();
} else {
$('#search_button').hide();
} }
$('[data-toggle="tooltip"]').tooltip({html: true}); $('[data-toggle="tooltip"]').tooltip({html: true});
@ -1873,5 +1845,14 @@
} }
}); });
} }
function getLanguagesProfiles() {
$.ajax({
url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) {
languagesProfiles = data['data'];
}
});
}
</script> </script>
{% endblock tail %} {% endblock tail %}

View File

@ -94,17 +94,15 @@
<i class="far fa-clone" id="moviealternativeTitles" data-toggle="tooltip" data-placement="right" title="None" data-html="true"></i> <i class="far fa-clone" id="moviealternativeTitles" data-toggle="tooltip" data-placement="right" title="None" data-html="true"></i>
</div> </div>
<div class="row"> <div class="row">
<h5><span id="movieAudioLanguage" class="badge badge-secondary"></span></h5> <h5><span id="movieAudioLanguage"></span></h5>
</div>
<div class="row">
<h5><span id="movieMappedPath" data-toggle="tooltip" data-placement="right" title="None" class="badge badge-secondary"></span></h5> <h5><span id="movieMappedPath" data-toggle="tooltip" data-placement="right" title="None" class="badge badge-secondary"></span></h5>
<h5><span id="movieTags" class="badge badge-secondary" data-toggle="tooltip" data-placement="right" <h5><span id="movieTags" class="badge badge-secondary" data-toggle="tooltip" data-placement="right"
title="None" data-html="true">Tags</span></h5> title="None" data-html="true">Tags</span></h5>
</div> </div>
<div class="row"> <div class="row">
<h5><span id="movieSubtitlesLanguages"></span></h5> <h5><span id="movieSubtitlesLanguagesProfile" class="badge badge-secondary"></span></h5>
</div>
<div class="row">
<h5><span id="movieHearingImpaired" class="badge badge-secondary"></span></h5>
<h5><span id="movieForced" class="badge badge-secondary"></span></h5>
</div> </div>
<div class="row"> <div class="row">
<span id="movieDescription"></span> <span id="movieDescription"></span>
@ -244,33 +242,10 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
Subtitles Language(s) Languages Profile
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_languages_select" name="languages" multiple data-live-search="true"></select> <select class="selectpicker" id="edit_languages_select" name="languages"></select>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Hearing-Impaired
</div>
<div class="form-group col-sm-1 pl-sm-0">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="hi_checkbox" name="hi">
<span class="custom-control-label" for="hi_checkbox"></span>
</label>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Forced
</div>
<div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_forced_select" name="forced">
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@ -511,6 +486,7 @@
movieDetailsRefresh(); movieDetailsRefresh();
getLanguages(); getLanguages();
getEnabledLanguages(); getEnabledLanguages();
getLanguagesProfiles();
//test //test
$('#movieSubtitles').on('click', '.remove_subtitles', function(e){ $('#movieSubtitles').on('click', '.remove_subtitles', function(e){
@ -544,8 +520,8 @@
moviePath: movieDetails['mapped_path'], moviePath: movieDetails['mapped_path'],
sceneName: movieDetails['sceneName'], sceneName: movieDetails['sceneName'],
language: $(this).attr("data-language"), language: $(this).attr("data-language"),
hi: movieDetails['hearing_impaired'], hi: $(this).attr("data-hi"),
forced:$(this).attr("data-forced"), forced: $(this).attr("data-forced"),
radarrId: movieDetails['radarrId'], radarrId: movieDetails['radarrId'],
title: movieDetails['title'] title: movieDetails['title']
}; };
@ -569,9 +545,7 @@
moviePath = movieDetails['mapped_path']; moviePath = movieDetails['mapped_path'];
sceneName = movieDetails['sceneName']; sceneName = movieDetails['sceneName'];
language = movieDetails['desired_languages']; profileId = movieDetails['profileId'].id;
hi = movieDetails['hearing_impaired'];
forced = movieDetails['forced'];
radarrId = movieDetails['radarrId']; radarrId = movieDetails['radarrId'];
var languages = Array.from(movieDetails['languages']); var languages = Array.from(movieDetails['languages']);
var is_pb = languages.includes('pb'); var is_pb = languages.includes('pb');
@ -580,9 +554,7 @@
const values = { const values = {
moviePath: moviePath, moviePath: moviePath,
sceneName: sceneName, sceneName: sceneName,
language: language, profileId: profileId,
hi: hi,
forced: forced,
radarrId: radarrId, radarrId: radarrId,
title: movieDetails['title'] title: movieDetails['title']
}; };
@ -683,7 +655,7 @@
{ data: null, { data: null,
searchable: false, searchable: false,
render: function ( data ) { render: function ( data ) {
return '<a href="" class="manual_download badge badge-secondary" data-moviePath="'+moviePath+'" data-sceneName="'+sceneName+'" data-subtitle="'+data.subtitle+'" data-provider="'+data.provider+'" data-language="'+data.language+'" data-forced="'+forced+'"><i class="fas fa-download" style="margin-right:0px" ></i></a>'; return '<a href="" class="manual_download badge badge-secondary" data-moviePath="'+moviePath+'" data-sceneName="'+sceneName+'" data-subtitle="'+data.subtitle+'" data-provider="'+data.provider+'" data-language="'+data.language+'" data-forced="'+data.forced+'"><i class="fas fa-download" style="margin-right:0px" ></i></a>';
} }
} }
] ]
@ -730,7 +702,7 @@
$('#upload_sceneName').val(movieDetails['sceneName']); $('#upload_sceneName').val(movieDetails['sceneName']);
$('#upload_radarrId').val(movieDetails['radarrId']); $('#upload_radarrId').val(movieDetails['radarrId']);
$('#upload_title').val(movieDetails['title']); $('#upload_title').val(movieDetails['title']);
$('#upload_audioLanguage').val(movieDetails['audio_language']['name']); $('#upload_audioLanguage').val((movieDetails['audio_language'].length) ? movieDetails['audio_language'][0].name : 'None');
$('#manual_language_select').empty(); $('#manual_language_select').empty();
$.each(enabledLanguages, function (i, item) { $.each(enabledLanguages, function (i, item) {
@ -794,26 +766,20 @@
$('#edit_button').on('click', function(e){ $('#edit_button').on('click', function(e){
e.preventDefault(); e.preventDefault();
$("#edit_movie_title_span").html(movieDetails['title']); $("#edit_movie_title_span").html(movieDetails['title']);
$("#edit_audio_language_span").text(movieDetails['audio_language']['name']); $("#edit_audio_language_span").empty();
$('#edit_radarrId').val(movieDetails['radarrId']); $.each(movieDetails['audio_language'], function (i, item) {
$("#edit_audio_language_span").append('<div class="badge badge-secondary">' + item['name'] + '</div> ');
})
$('#edit_radarrId').val(movieDetails['radarrId']);
$('#edit_languages_select').empty(); $('#edit_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') { $('#edit_languages_select').append('<option value="None">None</option>');
$('#edit_languages_select').selectpicker({maxOptions: 1}); $.each(languagesProfiles, function (i, item) {
$('#edit_languages_select').append('<option value="None">None</option>'); $('#edit_languages_select').append('<option value="'+item.profileId+'">'+item.name+'</option>');
}
$.each(enabledLanguages, function (i, item) {
$('#edit_languages_select').append('<option value="'+item.code2+'">'+item.name+'</option>');
}); });
$("#edit_languages_select").selectpicker("refresh"); $("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array(); $('#edit_languages_select').selectpicker('val', ((movieDetails['profileId'].id) ? movieDetails['profileId'].id : 'None'));
$.each(Array.from(movieDetails['languages']), function (i, item) {
selected_languages.push(item.code2);
});
$('#edit_languages_select').selectpicker('val', selected_languages);
$('#hi_checkbox').prop('checked', (movieDetails['hearing_impaired'] === 'True'));
$('#edit_forced_select').val(movieDetails['forced']).change();
$('#editModal') $('#editModal')
.modal({ .modal({
@ -1223,22 +1189,15 @@
$('#movieTags').hide(); $('#movieTags').hide();
} }
$('#movieAudioLanguage').text(movieDetails['audio_language']['name']); $("#movieAudioLanguage").empty();
$('#movieMappedPath').text(movieDetails['mapped_path']); $.each(movieDetails['audio_language'], function (i, item) {
$("#movieAudioLanguage").append('<div class="badge badge-secondary"><i class="fa fa-music"></i> ' + item['name'] + '</div> ');
})
$('#movieMappedPath').text(movieDetails['mapped_path']);
$('#movieMappedPath').attr("data-original-title", movieDetails['mapped_path']); $('#movieMappedPath').attr("data-original-title", movieDetails['mapped_path']);
var languages = ''; $('#movieSubtitlesLanguagesProfile').text(movieDetails['profileId'].name);
if (movieDetails['languages'] && movieDetails['languages'] !== 'None') {
movieDetails['languages'].forEach(appendFunc);
}
function appendFunc(value) {
languages += '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + '">' + value.code2 + '</span> ';
}
$('#movieSubtitlesLanguages').html(languages);
$('#movieHearingImpaired').text('Hearing-Impaired: ' + movieDetails['hearing_impaired']);
$('#movieForced').text('Forced: ' + movieDetails['forced']);
$('#movieDescription').text(movieDetails['overview']); $('#movieDescription').text(movieDetails['overview']);
var missing_languages = ''; var missing_languages = '';
@ -1248,11 +1207,11 @@
function missingAppendFunc(value) { function missingAppendFunc(value) {
if (value.forced) { if (value.forced) {
missing_languages += '<button class="get_subtitle btn btn-secondary btn-sm" type="button" data-toggle="tooltip" data-placement="right" data-original-title="' + value.name + '" data-language="' + value.code3 + '" data-forced=' + value.forced + '>' + value.code2 + ':forced <i class="fas fa-search"></i></button> '; missing_languages += '<button class="get_subtitle btn btn-secondary btn-sm" type="button" data-toggle="tooltip" data-placement="right" data-original-title="' + value.name + ' Forced" data-language="' + value.code3 + '" data-hi="' + value.hi + '" data-forced="' + value.forced + '">' + value.code2 + ':forced <i class="fas fa-search"></i></button> ';
} else if (value.hi) { } else if (value.hi) {
missing_languages += '<button class="get_subtitle btn btn-secondary btn-sm" type="button" data-toggle="tooltip" data-placement="right" data-original-title="' + value.name + '" data-language="' + value.code3 + '" data-forced=' + value.forced + '>' + value.code2 + ':HI <i class="fas fa-search"></i></button> '; missing_languages += '<button class="get_subtitle btn btn-secondary btn-sm" type="button" data-toggle="tooltip" data-placement="right" data-original-title="' + value.name + ' HI" data-language="' + value.code3 + '" data-hi="' + value.hi + '" data-forced="' + value.forced + '">' + value.code2 + ':HI <i class="fas fa-search"></i></button> ';
} else { } else {
missing_languages += '<button class="get_subtitle btn btn-secondary btn-sm" type="button" data-toggle="tooltip" data-placement="right" data-original-title="' + value.name + '" data-language="' + value.code3 + '" data-forced=' + value.forced + '>' + value.code2 + ' <i class="fas fa-search"></i></button> '; missing_languages += '<button class="get_subtitle btn btn-secondary btn-sm" type="button" data-toggle="tooltip" data-placement="right" data-original-title="' + value.name + '" data-language="' + value.code3 + '" data-hi="' + value.hi + '" data-forced="' + value.forced + '">' + value.code2 + ' <i class="fas fa-search"></i></button> ';
} }
} }
@ -1260,14 +1219,14 @@
$('[data-toggle="tooltip"]').tooltip({html: true}); $('[data-toggle="tooltip"]').tooltip({html: true});
if (movieDetails['desired_languages'] == '[]') { if (movieDetails['profileId'].id) {
$('#search_button').hide();
$('#manual_button').hide();
$('#upload_button').hide();
} else {
$('#search_button').show(); $('#search_button').show();
$('#manual_button').show(); $('#manual_button').show();
$('#upload_button').show(); $('#upload_button').show();
} else {
$('#search_button').hide();
$('#manual_button').hide();
$('#upload_button').hide();
} }
@ -1335,5 +1294,14 @@
} }
}); });
} }
function getLanguagesProfiles() {
$.ajax({
url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) {
languagesProfiles = data['data'];
}
});
}
</script> </script>
{% endblock tail %} {% endblock tail %}

View File

@ -24,9 +24,7 @@
<th>Name</th> <th>Name</th>
<th>Path Exist</th> <th>Path Exist</th>
<th>Audio Language</th> <th>Audio Language</th>
<th>Subtitles Languages</th> <th>Languages Profile</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
<th>Missing Subtitles</th> <th>Missing Subtitles</th>
<th></th> <th></th>
</tr> </tr>
@ -55,34 +53,10 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
Subtitles Language(s) Languages Profile
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_languages_select" name="languages" multiple <select class="selectpicker" id="edit_languages_select" name="languages"></select>
data-live-search="true"></select>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Hearing-Impaired
</div>
<div class="form-group col-sm-1 pl-sm-0">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="hi_checkbox" name="hi">
<span class="custom-control-label" for="hi_checkbox"></span>
</label>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Forced
</div>
<div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_forced_select" name="forced">
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@ -102,8 +76,7 @@
{% block tail %} {% block tail %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
getLanguages(); getLanguagesProfiles();
getEnabledLanguages();
events.on('event', function (event) { events.on('event', function (event) {
var event_json = JSON.parse(event); var event_json = JSON.parse(event);
@ -185,29 +158,20 @@
} }
} }
}, },
{data: "audio_language.name"},
{ {
data: "languages", data: 'audio_language',
render: function (data) { render: function (data) {
if (data && data !== 'None') { var audio_languages = '';
var languages = ''; data.forEach(appendFunc);
data.forEach(appendFunc); return audio_languages;
return languages;
} else {
return null
}
function appendFunc(value) { function appendFunc(value) {
languages = languages + '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + '">' + value.code2 + '</span> '; audio_languages = audio_languages + '<span class="badge badge-secondary">' + value.name + '</span> ';
} }
} }
}, },
{ {
data: "hearing_impaired", data: "profileId.name",
className: "dt-center"
},
{
data: "forced",
className: "dt-center" className: "dt-center"
}, },
{ {
@ -229,7 +193,7 @@
{ {
data: null, data: null,
render: function (data) { render: function (data) {
return '<a href="" class="edit_button badge badge-secondary" data-radarrId=' + data.radarrId + ' data-audiolanguage="' + data.audio_language.name + '" data-title="' + data.title + '" data-languages=' + JSON.stringify(data.languages) + ' data-hi="' + data.hearing_impaired + '" data-forced="' + data.forced + '"><i class="fas fa-wrench"></i></a>'; return '<a href="" class="edit_button badge badge-secondary" data-radarrId=' + data.radarrId + ' data-audiolanguage=\'' + JSON.stringify(data.audio_language) + '\' data-title="' + data.title + '" data-languages_profile=' + data.profileId.id + ' data-hi="' + data.hearing_impaired + '" data-forced="' + data.forced + '"><i class="fas fa-wrench"></i></a>';
} }
} }
] ]
@ -239,28 +203,20 @@
$('#movies').on('click', '.edit_button', function (e) { $('#movies').on('click', '.edit_button', function (e) {
e.preventDefault(); e.preventDefault();
$("#edit_movies_title_span").html($(this).data('title')); $("#edit_movies_title_span").html($(this).data('title'));
$("#edit_audio_language_span").html($(this).data('audiolanguage')); $("#edit_audio_language_span").empty();
$.each($(this).data('audiolanguage'), function (i, item) {
$("#edit_audio_language_span").append('<div class="badge badge-secondary">' + item['name'] + '</div> ');
})
$('#edit_radarrId').val($(this).data('radarrid')); $('#edit_radarrId').val($(this).data('radarrid'));
$('#edit_languages_select').empty(); $('#edit_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') { $('#edit_languages_select').append('<option value="None">None</option>');
$('#edit_languages_select').selectpicker({maxOptions: 1}); $.each(languagesProfiles, function (i, item) {
} $('#edit_languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
if ('{{settings.general.single_language}}' === 'True') {
$('#edit_languages_select').append('<option value="None">None</option>');
}
$.each(enabledLanguages, function (i, item) {
$('#edit_languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>');
}); });
$("#edit_languages_select").selectpicker("refresh"); $("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array(); $('#edit_languages_select').selectpicker('val', (($(this).data('languages_profile')) ? $(this).data('languages_profile') : 'None'));
$.each(Array.from($(this).data('languages')), function (i, item) {
selected_languages.push(item.code2);
});
$('#edit_languages_select').selectpicker('val', selected_languages);
$('#hi_checkbox').prop('checked', ($(this).data('hi') === 'True'));
$('#edit_forced_select').val($(this).data('forced')).change();
$('#editModal') $('#editModal')
.modal({ .modal({
@ -288,20 +244,11 @@
}); });
}); });
function getLanguages() { function getLanguagesProfiles() {
$.ajax({ $.ajax({
url: "{{ url_for('api.languages') }}?enabled=false", url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) { success: function (data) {
availableLanguages = data; languagesProfiles = data['data'];
}
});
}
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
success: function (data) {
enabledLanguages = data;
} }
}); });
} }

View File

@ -17,37 +17,16 @@
<th></th> <th></th>
<th>Name</th> <th>Name</th>
<th>Audio Language</th> <th>Audio Language</th>
<th>Subtitles Languages</th> <th>Languages Profile</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
</tr> </tr>
</thead> </thead>
</table> </table>
<nav id="edit_bar" class="navbar fixed-bottom navbar-dark bg-dark"> <nav id="edit_bar" class="navbar fixed-bottom navbar-dark bg-dark justify-content-end">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;"> <div class="form-group" style="margin-bottom: 0px;">
<label for="languages_select">Language(s): </label> <label for="languages_select">Languages Profile: </label>
<select class="selectpicker" id="languages_select" name="languages" title="No change" multiple></select> <select class="selectpicker" id="languages_select" name="languages"></select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="hi_select">Hearing-Impaired: </label>
<select class="selectpicker show-tick" id="hi_select" name="hi" title="No change" multiple>
<option value="False">False</option>
<option value="True">True</option>
</select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="forced_select">Forced: </label>
<select class="selectpicker show-tick" id="forced_select" name="forced" title="No change" multiple>
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div> </div>
</div> </div>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
@ -114,29 +93,20 @@
return '<a href="' + "{{ url_for( 'movie', no='tempvalue' ) }}".replace("tempvalue", data.radarrId) + '">' + data.title + '</a>' return '<a href="' + "{{ url_for( 'movie', no='tempvalue' ) }}".replace("tempvalue", data.radarrId) + '">' + data.title + '</a>'
} }
}, },
{data: "audio_language.name"},
{ {
data: "languages", data: 'audio_language',
render: function (data) { render: function (data) {
if (data && data !== 'None') { var audio_languages = '';
var languages = ''; data.forEach(appendFunc);
data.forEach(appendFunc); return audio_languages;
return languages;
} else {
return null;
}
function appendFunc(value) { function appendFunc(value) {
languages = languages + '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + '">' + value.code2 + '</span> '; audio_languages = audio_languages + '<span class="badge badge-secondary">' + value.name + '</span> ';
} }
} }
}, },
{ {
data: "hearing_impaired", data: "profileId.name",
className: "dt-center"
},
{
data: "forced",
className: "dt-center" className: "dt-center"
} }
] ]
@ -180,19 +150,11 @@
} }
}); });
if ('{{settings.general.single_language}}' === 'True') {
$('#languages_select').selectpicker({maxOptions: 1});
}
$('#hi_select').selectpicker({maxOptions: 1});
$('#forced_select').selectpicker({maxOptions: 1});
$('#save_button').on('click', function (e) { $('#save_button').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const values = { const values = {
radarrid: table.rows({selected: true}).ids().toArray(), radarrid: table.rows({selected: true}).ids().toArray(),
languages: $('#languages_select').val(), languages: $('#languages_select').val()
hi: $('#hi_select').val(),
forced: $('#forced_select').val()
}; };
$.ajax({ $.ajax({
@ -208,9 +170,7 @@
}, },
success: function () { success: function () {
table.rows().deselect(); table.rows().deselect();
$('#languages_select').selectpicker('val', ''); $('#languages_select').selectpicker('val', 'None');
$('#hi_select').selectpicker('val', '');
$('#forced_select').selectpicker('val', '');
} }
}); });
}); });
@ -218,11 +178,11 @@
function getEnabledLanguages() { function getEnabledLanguages() {
$.ajax({ $.ajax({
url: "{{ url_for('api.languages') }}?enabled=true", url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) { success: function (data) {
$('#languages_select').append('<option value="None">None</option>'); $('#languages_select').append('<option value="None">None</option>');
$.each(data, function (i, item) { $.each(data['data'], function (i, item) {
$('#languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>'); $('#languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
}); });
$("#languages_select").selectpicker("refresh"); $("#languages_select").selectpicker("refresh");
} }

View File

@ -22,10 +22,8 @@
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Path Exist</th> <th>Path Exist</th>
<th>Audio Profile</th> <th>Audio Profile Languages</th>
<th>Subtitles Languages</th> <th>Languages Profile</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
<th>Subtitles</th> <th>Subtitles</th>
<th></th> <th></th>
</tr> </tr>
@ -46,42 +44,19 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
Audio Profile Audio Profile Languages
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<span id="edit_audio_language_span"></span> <span id="edit_audio_language_span"></span>
</div> </div>
</div> </div>
<br>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
Subtitles Language(s) Languages Profile
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_languages_select" name="languages" multiple <select class="selectpicker" id="edit_languages_select" name="languages"></select>
data-live-search="true"></select>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Hearing-Impaired
</div>
<div class="form-group col-sm-1 pl-sm-0">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="hi_checkbox" name="hi">
<span class="custom-control-label" for="hi_checkbox"></span>
</label>
</div>
</div>
<div class="row">
<div class="col-sm-3 text-right">
Forced
</div>
<div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="edit_forced_select" name="forced">
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@ -102,8 +77,7 @@
{% block tail %} {% block tail %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
getLanguages(); getLanguagesProfiles();
getEnabledLanguages();
events.on('event', function (event) { events.on('event', function (event) {
var event_json = JSON.parse(event); var event_json = JSON.parse(event);
@ -176,29 +150,20 @@
} }
} }
}, },
{data: "audio_language.name"},
{ {
data: "languages", data: 'audio_language',
render: function (data) { render: function (data) {
if (data && data !== 'None') { var audio_languages = '';
var languages = ''; data.forEach(appendFunc);
data.forEach(appendFunc); return audio_languages;
return languages;
} else {
return null;
}
function appendFunc(value) { function appendFunc(value) {
languages = languages + '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + '">' + value.code2 + '</span> '; audio_languages = audio_languages + '<span class="badge badge-secondary">' + value.name + '</span> ';
} }
} }
}, },
{ {
data: "hearing_impaired", data: "profileId.name",
className: "dt-center"
},
{
data: "forced",
className: "dt-center" className: "dt-center"
}, },
{ {
@ -209,7 +174,8 @@
var completed = data.episodeFileCount - data.episodeMissingCount; var completed = data.episodeFileCount - data.episodeMissingCount;
var completed_style = ''; var completed_style = '';
var completed_text = ''; var completed_text = '';
if (data.episodeFileCount && data.languages !== 'None') { console.log(data.profileId);
if (data.episodeFileCount && data.profileId.id !== null) {
completed_style = ' style="width: ' + completed / total * 100 + '%;"'; completed_style = ' style="width: ' + completed / total * 100 + '%;"';
completed_text = completed + '/' + total; completed_text = completed + '/' + total;
} }
@ -219,7 +185,7 @@
{ {
data: null, data: null,
render: function (data) { render: function (data) {
return '<a href="" class="edit_button badge badge-secondary" data-sonarrSeriesId=' + data.sonarrSeriesId + ' data-audiolanguage="' + data.audio_language.name + '" data-title="' + data.title + '" data-languages=' + JSON.stringify(data.languages) + ' data-hi="' + data.hearing_impaired + '" data-forced="' + data.forced + '"><i class="fas fa-wrench"></i></a>'; return '<a href="" class="edit_button badge badge-secondary" data-sonarrSeriesId=' + data.sonarrSeriesId + ' data-audiolanguage=\'' + JSON.stringify(data.audio_language) + '\' data-title="' + data.title + '" data-languages_profile=' + data.profileId.id + ' data-hi="' + data.hearing_impaired + '" data-forced="' + data.forced + '"><i class="fas fa-wrench"></i></a>';
} }
} }
] ]
@ -229,28 +195,20 @@
$('#series').on('click', '.edit_button', function (e) { $('#series').on('click', '.edit_button', function (e) {
e.preventDefault(); e.preventDefault();
$("#edit_series_title_span").html($(this).data('title')); $("#edit_series_title_span").html($(this).data('title'));
$("#edit_audio_language_span").html($(this).data('audiolanguage')); $("#edit_audio_language_span").empty();
$.each($(this).data('audiolanguage'), function (i, item) {
$("#edit_audio_language_span").append('<div class="badge badge-secondary">' + item['name'] + '</div> ');
})
$('#edit_sonarrSeriesId').val($(this).data('sonarrseriesid')); $('#edit_sonarrSeriesId').val($(this).data('sonarrseriesid'));
$('#edit_languages_select').empty(); $('#edit_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') { $('#edit_languages_select').append('<option value="None">None</option>');
$('#edit_languages_select').selectpicker({maxOptions: 1}); $.each(languagesProfiles, function (i, item) {
} $('#edit_languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
if ('{{settings.general.single_language}}' === 'True') {
$('#edit_languages_select').append('<option value="None">None</option>');
}
$.each(enabledLanguages, function (i, item) {
$('#edit_languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>');
}); });
$("#edit_languages_select").selectpicker("refresh"); $("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array(); $('#edit_languages_select').selectpicker('val', (($(this).data('languages_profile')) ? $(this).data('languages_profile') : 'None'));
$.each(Array.from($(this).data('languages')), function (i, item) {
selected_languages.push(item.code2);
});
$('#edit_languages_select').selectpicker('val', selected_languages);
$('#hi_checkbox').prop('checked', ($(this).data('hi') === 'True'));
$('#edit_forced_select').val($(this).data('forced')).change();
$('#editModal') $('#editModal')
.modal({ .modal({
@ -278,20 +236,11 @@
}); });
}); });
function getLanguages() { function getLanguagesProfiles() {
$.ajax({ $.ajax({
url: "{{ url_for('api.languages') }}?enabled=false", url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) { success: function (data) {
availableLanguages = data; languagesProfiles = data['data'];
}
});
}
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
success: function (data) {
enabledLanguages = data;
} }
}); });
} }

View File

@ -16,38 +16,17 @@
<tr> <tr>
<th></th> <th></th>
<th>Name</th> <th>Name</th>
<th>Audio Profile</th> <th>Audio Profile Languages</th>
<th>Subtitles Languages</th> <th>Languages Profile</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
</tr> </tr>
</thead> </thead>
</table> </table>
<nav id="edit_bar" class="navbar fixed-bottom navbar-dark bg-dark"> <nav id="edit_bar" class="navbar fixed-bottom navbar-dark bg-dark justify-content-end">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;"> <div class="form-group" style="margin-bottom: 0px;">
<label for="languages_select">Language(s): </label> <label for="languages_select">Languages Profile: </label>
<select class="selectpicker" id="languages_select" name="languages" title="No change" multiple></select> <select class="selectpicker" id="languages_select" name="languages"></select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="hi_select">Hearing-Impaired: </label>
<select class="selectpicker show-tick" id="hi_select" name="hi" title="No change" multiple>
<option value="False">False</option>
<option value="True">True</option>
</select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="forced_select">Forced: </label>
<select class="selectpicker show-tick" id="forced_select" name="forced" title="No change" multiple>
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div> </div>
</div> </div>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
@ -114,25 +93,19 @@
return '<a href="' + "{{ url_for( 'episodes', no='tempvalue' ) }}".replace("tempvalue", data.sonarrSeriesId) + '">' + data.title + '</a>' return '<a href="' + "{{ url_for( 'episodes', no='tempvalue' ) }}".replace("tempvalue", data.sonarrSeriesId) + '">' + data.title + '</a>'
} }
}, },
{data: "audio_language.name"},
{ {
data: "languages", data: 'audio_language',
render: function (data) { render: function (data) {
if (data && data !== 'None') { var audio_languages = '';
var languages = ''; data.forEach(appendFunc);
data.forEach(appendFunc); return audio_languages;
return languages;
} else {
return null;
}
function appendFunc(value) { function appendFunc(value) {
languages = languages + '<span class="badge badge-secondary" data-toggle="tooltip" data-placement="right" title="' + value.name + '">' + value.code2 + '</span> '; audio_languages = audio_languages + '<span class="badge badge-secondary">' + value.name + '</span> ';
} }
} }
}, },
{data: "hearing_impaired", className: "dt-center"}, {data: "profileId.name", className: "dt-center"}
{data: "forced", className: "dt-center"}
] ]
}); });
@ -174,19 +147,11 @@
} }
}); });
if ('{{settings.general.single_language}}' === 'True') {
$('#languages_select').selectpicker({maxOptions: 1});
}
$('#hi_select').selectpicker({maxOptions: 1});
$('#forced_select').selectpicker({maxOptions: 1});
$('#save_button').on('click', function (e) { $('#save_button').on('click', function (e) {
e.preventDefault(); e.preventDefault();
const values = { const values = {
seriesid: table.rows({selected: true}).ids().toArray(), seriesid: table.rows({selected: true}).ids().toArray(),
languages: $('#languages_select').val(), languages: $('#languages_select').val()
hi: $('#hi_select').val(),
forced: $('#forced_select').val()
}; };
$.ajax({ $.ajax({
@ -202,9 +167,7 @@
}, },
success: function () { success: function () {
table.rows().deselect(); table.rows().deselect();
$('#languages_select').selectpicker('val', ''); $('#languages_select').selectpicker('val', 'None');
$('#hi_select').selectpicker('val', '');
$('#forced_select').selectpicker('val', '');
} }
}); });
}); });
@ -212,11 +175,11 @@
function getEnabledLanguages() { function getEnabledLanguages() {
$.ajax({ $.ajax({
url: "{{ url_for('api.languages') }}?enabled=true", url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) { success: function (data) {
$('#languages_select').append('<option value="None">None</option>'); $('#languages_select').append('<option value="None">None</option>');
$.each(data, function (i, item) { $.each(data['data'], function (i, item) {
$('#languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>'); $('#languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
}); });
$("#languages_select").selectpicker("refresh"); $("#languages_select").selectpicker("refresh");
} }

View File

@ -7,6 +7,10 @@
.warning { .warning {
color: red; color: red;
} }
table.dataTable tbody tr.selected a, table.dataTable tbody th.selected a, table.dataTable tbody td.selected a {
color: revert;
}
</style> </style>
{% endblock page_head %} {% endblock page_head %}
@ -31,7 +35,7 @@
{% block body %} {% block body %}
<div class="container-fluid" style="padding-top: 3em;"> <div class="container-fluid" style="padding-top: 3em;">
<form class="form" name="settings_form" id="settings_form"> <form class="form" name="settings_form" id="settings_form">
<h4>Subtitles languages</h4> <h4>Subtitles Languages</h4>
<hr/> <hr/>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
@ -49,7 +53,7 @@
<br> <br>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
<b>Enabled Languages</b> <b>Languages Filter</b>
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="enabled_languages" name="enabled_languages" data-live-search="true" multiple></select> <select class="selectpicker" id="enabled_languages" name="enabled_languages" data-live-search="true" multiple></select>
@ -57,6 +61,24 @@
</div> </div>
<br> <br>
<h4>Languages Profiles</h4>
<hr/>
<div class="row">
<div class="col-sm-8">
<table class="table table-striped" id="languages_profiles" style="width:100%;">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Cutoff</th>
<th>Languages</th>
</tr>
</thead>
</table>
</div>
</div>
<br>
<h4>Default Settings</h4> <h4>Default Settings</h4>
<hr> <hr>
<div class="row"> <div class="row">
@ -74,38 +96,13 @@
<div id="series_default_div"> <div id="series_default_div">
<div class="row"> <div class="row">
<div class="col-sm-4 text-right"> <div class="col-sm-4 text-right">
<b>Languages</b> <b>Languages Profile Id</b>
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="settings-general-serie_default_language" name="settings-general-serie_default_language" multiple></select> <select class="selectpicker" id="settings-general-serie_default_profile" name="settings-general-serie_default_profile"></select>
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-4 text-right">
<b>Hearing-Impaired</b>
</div>
<div class="form-group col-sm-1">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="settings-general-serie_default_hi" name="settings-general-serie_default_hi">
<span class="custom-control-label" for="settings-general-serie_default_hi"></span>
</label>
</div>
</div>
<div class="row">
<div class="col-sm-4 text-right">
<b>Forced</b>
</div>
<div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="settings-general-serie_default_forced" name="settings-general-serie_default_forced">
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div>
</div>
<br>
</div> </div>
<br>
<div class="row"> <div class="row">
<div class="col-sm-3 text-right"> <div class="col-sm-3 text-right">
<b>Movies Default Settings</b> <b>Movies Default Settings</b>
@ -121,39 +118,140 @@
<div id="movies_default_div"> <div id="movies_default_div">
<div class="row"> <div class="row">
<div class="col-sm-4 text-right"> <div class="col-sm-4 text-right">
<b>Languages</b> <b>Languages Profile Id</b>
</div> </div>
<div class="form-group col-sm-8 pl-sm-0"> <div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="settings-general-movie_default_language" name="settings-general-movie_default_language" multiple></select> <select class="selectpicker" id="settings-general-movie_default_profile" name="settings-general-movie_default_profile"></select>
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-4 text-right">
<b>Hearing-Impaired</b>
</div>
<div class="form-group col-sm-1">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="settings-general-movie_default_hi" name="settings-general-movie_default_hi">
<span class="custom-control-label" for="settings-general-movie_default_hi"></span>
</label>
</div>
</div>
<div class="row">
<div class="col-sm-4 text-right">
<b>Forced</b>
</div>
<div class="form-group col-sm-8 pl-sm-0">
<select class="selectpicker" id="settings-general-movie_default_forced" name="settings-general-movie_default_forced">
<option value="False">False</option>
<option value="True">True</option>
<option value="Both">Both</option>
</select>
</div>
</div>
<br>
</div> </div>
</form> </form>
</div> </div>
<div id="addModal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Languages Profile</h5><br>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form class="form" name="add_form" id="add_form">
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 text-right">
Profile name
</div>
<div class="col-sm-8">
<input type="text" class="form-control" id="add_profile_name" name="add_profile_name" value="">
</div>
</div>
<br>
<div class="row">
<div class="col-sm-3 text-right">
Languages
</div>
<div class="col-sm-8">
<table class="table table-striped" id="add_languages_profiles" style="width:100%;">
<thead>
<tr>
<th></th>
<th>Id</th>
<th>Language</th>
<th>Forced</th>
<th>Hearing-Impaired</th>
<th>Ignore If Matching Audio Track</th>
</tr>
</thead>
</table>
</div>
</div>
<br>
<div class="row">
<div class="col-sm-3 text-right">
Language ID cutoff (ignore others if existing)
</div>
<div class="col-sm-8">
<select class="selectpicker show-tick" id="add_language_cutoff" name="add_language_cutoff">
</select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" id="add_save_button" class="btn btn-info">Add</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
</div>
<div id="editModal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Languages Profile</h5><br>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form class="form" name="edit_form" id="edit_form">
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 text-right">
Profile name
</div>
<div class="col-sm-8">
<input type="text" class="form-control" id="edit_profile_name" name="edit_profile_name" value="">
</div>
</div>
<br>
<div class="row">
<div class="col-sm-3 text-right">
Languages
</div>
<div class="col-sm-8">
<table class="table table-striped" id="edit_languages_profiles" style="width:100%;">
<thead>
<tr>
<th></th>
<th>Id</th>
<th>Language</th>
<th>Forced</th>
<th>Hearing-Impaired</th>
<th>Ignore If Matching Audio Track</th>
</tr>
</thead>
</table>
</div>
</div>
<br>
<div class="row">
<div class="col-sm-3 text-right">
Language ID cutoff (ignore others if existing)
</div>
<div class="col-sm-8">
<select class="selectpicker show-tick inline_select_edit" id="edit_language_cutoff" name="edit_language_cutoff">
</select>
</div>
</div>
<input type="hidden" id="edit_profile_id" value="" />
</div>
</div>
<div class="modal-footer">
<button type="submit" id="edit_save_button" class="btn btn-info">Edit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
</div>
{% endblock body %} {% endblock body %}
{% block tail %} {% block tail %}
@ -173,57 +271,324 @@
$('#save_button_checkmark').hide(); $('#save_button_checkmark').hide();
$('#save_button').prop('disabled', true).css('cursor', 'not-allowed'); $('#save_button').prop('disabled', true).css('cursor', 'not-allowed');
// Listen to selection change on enabled_languages select and keep series and movies default language select synced
$('#enabled_languages').on('changed.bs.select', function(event, clickedIndex, isSelected, previousValue) {
actual = $("#enabled_languages option:selected").map(function() {
return $(this).val();
}).get();
if (previousValue) {
var added = actual.filter(x => !previousValue.includes(x));
var removed = previousValue.filter(x => !actual.includes(x));
if (added.length > 0) {
$('#settings-general-serie_default_language').append('<option value="' + added[0] + '">' + $("#enabled_languages option[value="+added[0]+"]").text() + '</option>');
$('#settings-general-movie_default_language').append('<option value="' + added[0] + '">' + $("#enabled_languages option[value="+added[0]+"]").text() + '</option>');
}
if (removed.length > 0) {
$("#settings-general-serie_default_language option[value="+removed[0]+"]").remove();
$("#settings-general-movie_default_language option[value="+removed[0]+"]").remove();
}
} else {
$(actual).each( function(i, item) {
if ($('#settings-general-serie_default_language option[value='+item+']').length < 1) {
$('#settings-general-serie_default_language').append('<option value="' + item + '">' + $("#enabled_languages option[value="+item+"]").text() + '</option>');
$('#settings-general-movie_default_language').append('<option value="' + item + '">' + $("#enabled_languages option[value="+item+"]").text() + '</option>');
}
});
}
$('#settings-general-serie_default_language').selectpicker('val', {{settings.general.serie_default_language|safe}});
$('#settings-general-movie_default_language').selectpicker('val', {{settings.general.movie_default_language|safe}});
$("#settings-general-serie_default_language").selectpicker("refresh");
$("#settings-general-movie_default_language").selectpicker("refresh");
})
// Set Select input values // Set Select input values
$('#settings-general-serie_default_forced').val('{{settings.general.serie_default_forced}}').trigger('change'); $('#settings-general-serie_default_profile').val('{{settings.general.serie_default_profile}}').trigger('change');
$('#settings-general-movie_default_forced').val('{{settings.general.movie_default_forced}}').trigger('change'); $('#settings-general-movie_default_profile').val('{{settings.general.movie_default_profile}}').trigger('change');
$('.selectpicker').selectpicker('refresh') $('.selectpicker').selectpicker('refresh')
// Listen to single language checkbox change to adapt languages menus // Set Checkbox input values
$('#settings-general-single_language').on('change', function() { $('#settings-general-single_language').prop('checked', {{'true' if settings.general.getboolean('single_language') else 'false'}}).trigger('change');
if ($(this).prop('checked')) { $('#settings-general-serie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('serie_default_enabled') else 'false'}}).trigger('change');
$('#settings-general-serie_default_language').selectpicker({maxOptions:1}).selectpicker('refresh'); $('#settings-general-movie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('movie_default_enabled') else 'false'}}).trigger('change');
$('#settings-general-movie_default_language').selectpicker({maxOptions:1}).selectpicker('refresh');
$('#settings-general-serie_default_language').selectpicker('val', $('#settings-general-serie_default_language').val()[0]); var table = $('#languages_profiles').DataTable({
$('#settings-general-movie_default_language').selectpicker('val', $('#settings-general-movie_default_language').val()[0]); dom: 'Bfrtip',
} else { select: {
$('#settings-general-serie_default_language').selectpicker({maxOptions:false}).selectpicker('refresh'); style: 'single'
$('#settings-general-movie_default_language').selectpicker({maxOptions:false}).selectpicker('refresh'); },
} language: {
zeroRecords: 'No Languages Profiles'
},
searching: false,
ordering: false,
lengthChange: true,
responsive: false,
paging: false,
info: false,
processing: true,
serverSide: false,
ajax: {
url: "{{ url_for('api.languagesprofiles') }}",
type: 'GET'
},
columns: [
{ data: 'profileId',
visible: false
},
{ data: 'name' },
{ data: 'cutoff',
visible: false
},
{ data: 'items',
render: function(data, type, row) {
var languages = '';
$(eval(data)).each(function (index, value) {
if (value.forced == 'True') {
languages += '<span class="badge badge-secondary"' + ((row.cutoff == '65535' || value.id == row.cutoff) ? ' data-toggle="tooltip" title="Ignore others if this one is available" style="background-color:#911f93;"' : '') + '>' + value.language + ':forced' + ((value.audio_exclude === "True") ? ' <i class="fa fa-ban" data-toggle="tooltip" title="Ignore if matching audio track available"></i>' : '') + '</span> ';
} else if (value.hi == 'True') {
languages += '<span class="badge badge-secondary"' + ((row.cutoff == '65535' || value.id == row.cutoff) ? ' data-toggle="tooltip" title="Ignore others if this one is available" style="background-color:#911f93;"' : '') + '>' + value.language + ':hi' + ((value.audio_exclude === "True") ? ' <i class="fa fa-ban" data-toggle="tooltip" title="Ignore if matching audio track available"></i>' : '') + '</span> ';
} else {
languages += '<span class="badge badge-secondary"' + ((row.cutoff == '65535' || value.id == row.cutoff) ? ' data-toggle="tooltip" title="Ignore others if this one is available" style="background-color:#911f93;"' : '') + '>' + value.language + ((value.audio_exclude === "True") ? ' <i class="fa fa-ban" data-toggle="tooltip" title="Ignore if matching audio track available"></i>' : '') + '</span> ';
}
});
return languages;
}
}
],
buttons: [{
text: 'Add',
action: function () {
if ($('#add_languages_profiles').DataTable().rows().length) {
$('#add_languages_profiles').DataTable().clear();
}
$('#add_profile_name').val('');
$('#add_languages_profiles').DataTable().destroy();
var table_add = $('#add_languages_profiles').DataTable({
dom: 'Bfrtip',
select: {
style: 'single'
},
language: {
zeroRecords: 'No Languages In This Profile'
},
searching: false,
ordering: false,
lengthChange: false,
responsive: false,
paging: false,
info: false,
data: null,
columns: [
{ data: 'id',
visible: false
},
{ data: 'id' },
{ data: 'language',
render: function (data) {
var enabled_languages = Array();
$('#enabled_languages option:selected').each(function(){
enabled_languages.push([$(this).val(), $(this).text()]);
});
var html_dropdown = '<select class="selectpicker show-tick inline_select_add" data-width="fit">';
for (i = 0; i < enabled_languages.length; i++) {
html_dropdown += '<option value="' + enabled_languages[i][0] + '"' + ((data == enabled_languages[i][0]) ? ' selected="selected"' : '') + '>' + enabled_languages[i][1] + '</option>';
}
html_dropdown += '</select>';
return html_dropdown;
}
},
{ data: 'forced',
render: function(data) {
return '<select class="selectpicker show-tick inline_select_add" data-width="fit"><option value="True"' + ((data === 'True') ? ' selected="selected"' : '') + '>True</option><option value="False"' + ((data === 'False') ? ' selected="selected"' : '') + '>False</option></select>';
}
},
{ data: 'hi',
render: function(data) {
return '<select class="selectpicker show-tick inline_select_add" data-width="fit"><option value="True"' + ((data === 'True') ? ' selected="selected"' : '') + '>True</option><option value="False"' + ((data === 'False') ? ' selected="selected"' : '') + '>False</option></select>';
}
},
{ data: 'audio_exclude',
render: function(data) {
return '<select class="selectpicker show-tick inline_select_add" data-width="fit"><option value="True"' + ((data === 'True') ? ' selected="selected"' : '') + '>True</option><option value="False"' + ((data === 'False') ? ' selected="selected"' : '') + '>False</option></select>';
}
}
],
buttons: [{
text: 'Add',
action: function () {
var language_id = 0;
if (table_add.rows().data().toArray().length) {
language_id = Math.max(...table_add.column(0).data().toArray());
}
language_id++;
table_add.row.add({
id: language_id,
language: '',
forced: 'False',
hi: 'False',
audio_exclude: 'False'
}).draw();
$('.inline_select_add').selectpicker('refresh');
$('.inline_select_add.selectpicker').on('changed.bs.select', function () {
table_add.row( $(this).closest('tr') ).cell( $(this).closest('td') ).data($(this).val());
$('.inline_select_add').selectpicker('refresh');
table_add.draw();
});
}
},
{
extend: 'selected',
text: 'Delete',
action: function () {
table_add.row( { selected: true } ).remove().draw();
}
}]
});
$('#addModal').modal('show');
}
},
{
extend: 'selected',
text: 'Edit',
action: function () {
$('#edit_profile_name').val(table.row( { selected: true } ).data()['name']);
var items = $.parseJSON(table.row( { selected: true } ).data()['items']);
$('#edit_languages_profiles').DataTable().destroy();
var table_edit = $('#edit_languages_profiles').DataTable({
dom: 'Bfrtip',
select: {
style: 'single'
},
language: {
zeroRecords: 'No Languages In This Profile'
},
searching: false,
ordering: false,
lengthChange: false,
responsive: false,
paging: false,
info: false,
data: items,
columns: [
{ data: 'id',
visible: false
},
{ data: 'id' },
{ data: 'language',
render: function (data) {
var enabled_languages = Array();
$('#enabled_languages option:selected').each(function(){
enabled_languages.push([$(this).val(), $(this).text()]);
});
var html_dropdown = '<select class="selectpicker show-tick inline_select_edit" data-width="fit">';
for (i = 0; i < enabled_languages.length; i++) {
html_dropdown += '<option value="' + enabled_languages[i][0] + '"' + ((data == enabled_languages[i][0]) ? ' selected="selected"' : '') + '>' + enabled_languages[i][1] + '</option>';
}
html_dropdown += '</select>'
return html_dropdown;
}
},
{ data: 'forced',
render: function(data) {
return '<select class="selectpicker show-tick inline_select_edit" data-width="fit"><option value="True"' + ((data === 'True') ? ' selected="selected"' : '') + '>True</option><option value="False"' + ((data === 'False') ? ' selected="selected"' : '') + '>False</option></select>';
}
},
{ data: 'hi',
render: function(data) {
return '<select class="selectpicker show-tick inline_select_edit" data-width="fit"><option value="True"' + ((data === 'True') ? ' selected="selected"' : '') + '>True</option><option value="False"' + ((data === 'False') ? ' selected="selected"' : '') + '>False</option></select>';
}
},
{ data: 'audio_exclude',
render: function(data) {
return '<select class="selectpicker show-tick inline_select_edit" data-width="fit"><option value="True"' + ((data === 'True') ? ' selected="selected"' : '') + '>True</option><option value="False"' + ((data === 'False') ? ' selected="selected"' : '') + '>False</option></select>';
}
}
],
buttons: [{
text: 'Add',
action: function () {
var language_id = 0;
if (table_edit.rows().data().toArray().length) {
language_id = Math.max(...table_edit.column(0).data().toArray());
}
language_id++;
table_edit.row.add({
id: language_id,
language: '',
forced: 'False',
hi: 'False',
audio_exclude: 'False'
}).draw();
$('.inline_select_edit').selectpicker('refresh');
$('.inline_select_edit.selectpicker').on('changed.bs.select', function () {
table_edit.row( $(this).closest('tr') ).cell( $(this).closest('td') ).data($(this).val());
$('.inline_select_edit').selectpicker('refresh');
table_edit.draw();
});
}
},
{
extend: 'selected',
text: 'Delete',
action: function () {
table_edit.row( { selected: true } ).remove().draw();
}
}]
});
$('#edit_language_cutoff').val(table.row( { selected: true } ).data()['cutoff']);
$('#edit_profile_id').val(table.row( { selected: true } ).data()['profileId']);
$('.inline_select_edit').selectpicker('refresh');
$('#editModal').modal('show');
$('.inline_select_edit.selectpicker').on('changed.bs.select', function () {
table_edit.row( $(this).closest('tr') ).cell( $(this).closest('td') ).data($(this).val());
$('.inline_select_edit').selectpicker('refresh');
table_edit.draw();
});
}
},
{
extend: 'selected',
text: 'Delete',
action: function () {
table.row( { selected: true } ).remove().draw();
$('#settings_form').trigger('change');
}
}]
}); });
$('#add_profile_name').on('input', function() {
if ($('#add_languages_profiles').DataTable().rows().count() && $('#add_profile_name').val()) {
$('#add_save_button').prop('disabled', false).css('cursor', 'auto');
} else {
$('#add_save_button').prop('disabled', true).css('cursor', 'not-allowed');
}
})
$('#add_languages_profiles').DataTable().on( 'draw', function () {
if ($('#add_languages_profiles').DataTable().rows().count() && $('#add_profile_name').val()) {
$('#add_save_button').prop('disabled', false).css('cursor', 'auto');
} else {
$('#add_save_button').prop('disabled', true).css('cursor', 'not-allowed');
}
var previousValue = $("#add_language_cutoff").val();
$("#add_language_cutoff").empty();
$('#add_language_cutoff').append('<option value="">Disabled</option>');
$('#add_language_cutoff').append('<option value="65535">Any of them</option>');
var ids = $('#add_languages_profiles').DataTable().rows().data().pluck( 'id' ).toArray();
$(ids).each( function(i, item) {
$('#add_language_cutoff').append('<option value="' + item + '">' + item + '</option>');
});
$("#add_language_cutoff").val(previousValue);
$("#add_language_cutoff").selectpicker("refresh");
} );
$('#edit_profile_name').on('input', function() {
if ($('#edit_languages_profiles').DataTable().rows().count() && $('#edit_profile_name').val()) {
$('#edit_save_button').prop('disabled', false).css('cursor', 'auto');
} else {
$('#edit_save_button').prop('disabled', true).css('cursor', 'not-allowed');
}
})
$('#edit_languages_profiles').DataTable().on( 'draw', function () {
if ($('#edit_languages_profiles').DataTable().rows().count() && $('#edit_profile_name').val()) {
$('#edit_save_button').prop('disabled', false).css('cursor', 'auto');
} else {
$('#edit_save_button').prop('disabled', true).css('cursor', 'not-allowed');
}
var previousValue = $("#edit_language_cutoff").val();
$("#edit_language_cutoff").empty();
$('#edit_language_cutoff').append('<option value="">Disabled</option>');
$('#edit_language_cutoff').append('<option value="65535">Any of them</option>');
var ids = $('#edit_languages_profiles').DataTable().rows().data().pluck( 'id' ).toArray();
$(ids).each( function(i, item) {
$('#edit_language_cutoff').append('<option value="' + item + '">' + item + '</option>');
});
$("#edit_language_cutoff").val(previousValue);
$("#edit_language_cutoff").selectpicker("refresh");
} );
// Hide *_div on default-enabled change // Hide *_div on default-enabled change
$('#settings-general-serie_default_enabled').on('change', function() { $('#settings-general-serie_default_enabled').on('change', function() {
if ($(this).prop('checked')) { if ($(this).prop('checked')) {
@ -241,28 +606,55 @@
} }
}); });
// Set Checkbox input values // Listen to profiles change and populate the default dropdowns accordingly
$('#settings-general-single_language').prop('checked', {{'true' if settings.general.getboolean('single_language') else 'false'}}).trigger('change'); $('#languages_profiles').DataTable().on( 'draw', function () {
$('#settings-general-serie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('serie_default_enabled') else 'false'}}).trigger('change'); if ($("#settings-general-serie_default_profile").length > 1) {
$('#settings-general-serie_default_hi').prop('checked', {{'true' if settings.general.getboolean('serie_default_hi') else 'false'}}).trigger('change'); var previousValueSerie = $("#settings-general-serie_default_profile").val();
$('#settings-general-movie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('movie_default_enabled') else 'false'}}).trigger('change'); } else {
$('#settings-general-movie_default_hi').prop('checked', {{'true' if settings.general.getboolean('movie_default_hi') else 'false'}}).trigger('change'); var previousValueSerie = '{{settings.general.serie_default_profile}}';
}
if ($("#settings-general-movie_default_profile").length > 1) {
var previousValueMovie = $("#settings-general-movie_default_profile").val();
} else {
var previousValueMovie = '{{settings.general.movie_default_profile}}';
}
$("#settings-general-serie_default_profile").empty();
$("#settings-general-movie_default_profile").empty();
$('#settings-general-serie_default_profile').append('<option value="">None</option>');
$('#settings-general-movie_default_profile').append('<option value="">None</option>');
var ids = $('#languages_profiles').DataTable().rows().data().toArray();
$(ids).each( function(i, item) {
$('#settings-general-serie_default_profile').append('<option value="' + item.profileId + '">' + item.name + '</option>');
$('#settings-general-movie_default_profile').append('<option value="' + item.profileId + '">' + item.name + '</option>');
});
$("#settings-general-serie_default_profile").val(previousValueSerie);
$("#settings-general-movie_default_profile").val(previousValueMovie);
$("#settings-general-serie_default_profile").selectpicker("refresh");
$("#settings-general-movie_default_profile").selectpicker("refresh");
} );
$('#save_button').on('click', function(e) {
e.preventDefault();
$('#save_button').on('click', function() {
var formdata = new FormData(document.getElementById("settings_form")); var formdata = new FormData(document.getElementById("settings_form"));
// Make sure empty default languages select are send (bug in bootstrap-select) // Make sure empty default languages select are send (bug in bootstrap-select)
if (formdata.get('settings-general-serie_default_language') == null) { if (formdata.get('settings-general-serie_default_profile') == null) {
formdata.append('settings-general-serie_default_language', null) formdata.append('settings-general-serie_default_profile', null)
} }
if (formdata.get('settings-general-movie_default_language') == null) { if (formdata.get('settings-general-movie_default_profile') == null) {
formdata.append('settings-general-movie_default_language', null) formdata.append('settings-general-movie_default_profile', null)
} }
// Make sure all checkbox input are sent with true/false value // Make sure all checkbox input are sent with true/false value
$('input[type=checkbox]').each(function () { $('input[type=checkbox]').each(function () {
formdata.set($(this).prop('id'), $(this).prop('checked')); formdata.set($(this).prop('id'), $(this).prop('checked'));
}); });
formdata.append('languages_profiles', JSON.stringify(table.rows().data().toArray()));
$.ajax({ $.ajax({
url: "{{ url_for('api.savesettings') }}", url: "{{ url_for('api.savesettings') }}",
data: formdata, data: formdata,
@ -282,42 +674,87 @@
}); });
}); });
$('#add_save_button').on('click', function(e) {
e.preventDefault();
var table_add = $('#add_languages_profiles').DataTable();
var items = [];
table_add.rows().every( function (row) {
var row_item = table_add.cells( row, '' ).render( 'display' );
var language = $(row_item[2]).filter('select').selectpicker().val();
var forced = $(row_item[3]).filter('select').val();
var hi = $(row_item[4]).filter('select').val();
var audio_exclude = $(row_item[5]).filter('select').val();
items.push({
id: row_item[1],
language: language,
forced: forced,
hi: hi,
audio_exclude: audio_exclude
});
})
if (table.rows().data().toArray().length === 0) {
var languages_profile_id = 0;
} else {
var languages_profile_id = Math.max(...table.column(0).data().toArray());
}
languages_profile_id++
table.row.add({
profileId: languages_profile_id,
name: $('#add_profile_name').val(),
cutoff: $('#add_language_cutoff').val(),
items: JSON.stringify(items)
}).draw();
$('#addModal').modal('hide');
$('#settings_form').trigger('change');
});
$('#edit_save_button').on('click', function(e) {
e.preventDefault();
var table_edit = $('#edit_languages_profiles').DataTable();
var items = [];
table_edit.rows().every( function (row) {
var row_item = table_edit.cells( row, '' ).render( 'display' );
var language = $(row_item[2]).filter('select').selectpicker().val();
var forced = $(row_item[3]).filter('select').val();
var hi = $(row_item[4]).filter('select').val();
var audio_exclude = $(row_item[5]).filter('select').val();
items.push({
id: row_item[1],
language: language,
forced: forced,
hi: hi,
audio_exclude: audio_exclude
});
})
table.row( { selected: true } ).data({
profileId: parseInt($('#edit_profile_id').val()),
name: $('#edit_profile_name').val(),
cutoff: $('#edit_language_cutoff').val(),
items: JSON.stringify(items)
});
$('#editModal').modal('hide');
$('#settings_form').trigger('change');
});
function getLanguages() { function getLanguages() {
$.ajax({ $.ajax({
url: "{{ url_for('api.languages') }}?enabled=false", url: "{{ url_for('api.languages') }}?enabled=false",
success: function (data) { success: function (data) {
$('#enabled_languages').empty(); $('#enabled_languages').empty();
$.each(data, function (i, item) { $.each(data, function (i, item) {
$('#enabled_languages').append('<option value="' + item.code2 + '">' + item.name + '</option>'); $('#enabled_languages').append('<option value="' + item.code2 + '"' + ((item.enabled) ? ' selected="selected"' : '') + '>' + item.name + '</option>');
}); });
getEnabledLanguages();
$("#enabled_languages").selectpicker("refresh"); $("#enabled_languages").selectpicker("refresh");
} }
}); });
} }
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
success: function (data) {
let optArr = [];
$.each(data, function (i, item) {
optArr.push(item.code2);
});
$('#enabled_languages').selectpicker('val', optArr);
}
});
}
// monitor changes to the settings_form // monitor changes to the settings_form
setTimeout( $('#settings_form').on('change', function() {
function() form_changed = true;
{ $('#save_button').prop('disabled', false).css('cursor', 'auto');
$('#settings_form').on('change', function() { })
form_changed = true;
$('#save_button').prop('disabled', false).css('cursor', 'auto');
})
}, 1000);
}); });
</script> </script>
{% endblock tail %} {% endblock tail %}

View File

@ -202,7 +202,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" id="add_save_button" class="btn btn-info">Save</button> <button type="submit" id="add_save_button" class="btn btn-info">Add</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div> </div>
</form> </form>
@ -242,7 +242,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" id="edit_save_button" class="btn btn-info">Save</button> <button type="submit" id="edit_save_button" class="btn btn-info">Edit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div> </div>
</form> </form>

View File

@ -215,7 +215,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" id="add_save_button" class="btn btn-info">Save</button> <button type="submit" id="add_save_button" class="btn btn-info">Add</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div> </div>
</form> </form>
@ -255,7 +255,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" id="edit_save_button" class="btn btn-info">Save</button> <button type="submit" id="edit_save_button" class="btn btn-info">Edit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</div> </div>
</form> </form>

View File

@ -422,19 +422,19 @@
<label>Enable the automatic subtitles synchronization after downloading a subtitles.</label> <label>Enable the automatic subtitles synchronization after downloading a subtitles.</label>
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-4 text-right">
<b>Subtitles synchronization debugging</b>
</div>
<div class="form-group col-sm-8">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="settings-subsync-debug" name="settings-subsync-debug">
<span class="custom-control-label" for="settings-subsync-debug"></span>
</label>
<label>Do not actually sync the subtitles but generate a .tar.gz file to be able to open an issue for ffsubsync. This file will reside alongside the media file.</label>
</div>
</div>
<div id="subsync_div"> <div id="subsync_div">
<div class="row">
<div class="col-sm-4 text-right">
<b>Subtitles synchronization debugging</b>
</div>
<div class="form-group col-sm-8">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="settings-subsync-debug" name="settings-subsync-debug">
<span class="custom-control-label" for="settings-subsync-debug"></span>
</label>
<label>Do not actually sync the subtitles but generate a .tar.gz file to be able to open an issue for ffsubsync. This file will reside alongside the media file.</label>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-sm-4 text-right"> <div class="col-sm-4 text-right">
<b>Subtitles synchronization score threshold for series</b> <b>Subtitles synchronization score threshold for series</b>