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
import os
import ast
from datetime import timedelta
import datetime
from dateutil import rrule
import pretty
import time
from operator import itemgetter
import platform
import io
import re
import json
@ -18,13 +15,13 @@ from config import settings, base_url, save_settings
from init import *
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 get_languages import language_from_alpha3, language_from_alpha2, alpha2_from_alpha3, alpha2_from_language, \
alpha3_from_language, alpha3_from_alpha2
from get_subtitle import download_subtitle, series_download_subtitles, movies_download_subtitles, \
manual_search, manual_download_subtitle, manual_upload_subtitle, wanted_search_missing_subtitles_series, \
wanted_search_missing_subtitles_movies, episode_download_subtitles, movies_download_subtitles
from get_languages import language_from_alpha2, alpha3_from_alpha2
from get_subtitle import download_subtitle, series_download_subtitles, manual_search, manual_download_subtitle, \
manual_upload_subtitle, wanted_search_missing_subtitles_series, wanted_search_missing_subtitles_movies, \
episode_download_subtitles, movies_download_subtitles
from notifier import send_notifications, send_notifications_movie
from list_subtitles import store_subtitles, store_subtitles_movie, series_scan_subtitles, movies_scan_subtitles, \
list_missing_subtitles, list_missing_subtitles_movies
@ -128,6 +125,12 @@ class Languages(Resource):
return jsonify(result)
class LanguagesProfiles(Resource):
@authenticate
def get(self):
return jsonify(data=get_profiles_list())
class Notifications(Resource):
@authenticate
def get(self):
@ -182,6 +185,40 @@ class ResetProviders(Resource):
class SaveSettings(Resource):
@authenticate
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()))
return '', 200
@ -283,10 +320,6 @@ class Series(Resource):
if seriesId:
result = database.execute("SELECT * FROM table_shows WHERE sonarrSeriesId=? ORDER BY sortTitle ASC LIMIT ? "
"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:
result = database.execute("SELECT * FROM table_shows ORDER BY sortTitle ASC LIMIT ? OFFSET ?", (length, start))
for item in result:
@ -294,11 +327,10 @@ class Series(Resource):
item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
item.update({"audio_language": get_audio_profile_languages(series_id=item['sonarrSeriesId'])})
# Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']):
@ -306,6 +338,9 @@ class Series(Resource):
"code2": subs,
"code3": alpha3_from_alpha2(subs)}
# Parse profileId
item['profileId'] = {"id": item['profileId'], "name": get_profile_id_name(item['profileId'])}
# Parse alternate titles
if item['alternateTitles']:
item.update({"alternateTitles": ast.literal_eval(item['alternateTitles'])})
@ -343,42 +378,20 @@ class Series(Resource):
item.update({"episodeFileCount": episodeFileCount})
# Add the series desired subtitles language code2
try:
item.update({"desired_languages": desired_languages})
except NameError:
pass
item.update({"desired_languages": get_desired_languages(item['profileId']['id'])})
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
@authenticate
def post(self):
seriesId = request.args.get('seriesid')
lang = request.form.getlist('languages')
if len(lang) > 0:
pass
else:
lang = 'None'
languages_profile = request.form.get('languages')
single_language = settings.general.getboolean('single_language')
if single_language:
if str(lang) == "['None']":
lang = 'None'
else:
lang = str(lang)
else:
if str(lang) == "['']":
lang = '[]'
if languages_profile == 'None':
languages_profile = None
hi = request.form.get('hi')
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))
database.execute("UPDATE table_shows SET profileId=? WHERE sonarrSeriesId=?", (languages_profile, seriesId))
list_missing_subtitles(no=seriesId)
@ -392,7 +405,7 @@ class SeriesEditor(Resource):
def get(self, **kwargs):
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")
row_count = len(result)
@ -402,11 +415,10 @@ class SeriesEditor(Resource):
item.update({"DT_RowId": 'row_' + str(item['sonarrSeriesId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
item.update({"audio_language": get_audio_profile_languages(series_id=item['sonarrSeriesId'])})
# Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']):
@ -414,43 +426,30 @@ class SeriesEditor(Resource):
"code2": 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)
class SeriesEditSave(Resource):
@authenticate
def post(self):
lang = request.form.getlist('languages[]')
hi = request.form.getlist('hi[]')
forced = request.form.getlist('forced[]')
lang = request.form.get('languages')
if lang == ['None']:
lang = 'None'
if lang == 'None':
lang = None
seriesIdList = []
seriesidLangList = []
seriesidHiList = []
seriesidForcedList = []
for item in request.form.getlist('seriesid[]'):
seriesid = item.lstrip('row_')
seriesIdList.append(seriesid)
if len(lang):
seriesidLangList.append([str(lang), seriesid])
if len(hi):
seriesidHiList.append([hi[0], seriesid])
if len(forced):
seriesidForcedList.append([forced[0], seriesid])
seriesidLangList.append([lang, seriesid])
try:
if len(lang):
database.execute("UPDATE table_shows SET languages=? WHERE sonarrSeriesId=?", seriesidLangList,
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)
database.execute("UPDATE table_shows SET profileId=? WHERE sonarrSeriesId=?", seriesidLangList,
execute_many=True)
except:
pass
else:
@ -476,27 +475,22 @@ class Episodes(Resource):
(seriesId,), only_one=True)['count']
if 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:
result = database.execute("SELECT * FROM table_episodes WHERE sonarrSeriesId=? ORDER BY season DESC, "
"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:
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:
# Add Datatables rowId
item.update({"DT_RowId": 'row_' + str(item['sonarrEpisodeId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
item.update({"audio_language": get_audio_profile_languages(episode_id=item['sonarrEpisodeId'])})
# Parse subtitles
if item['subtitles']:
@ -616,8 +610,12 @@ class EpisodesSubtitlesDownload(Resource):
title = request.form.get('title')
providers_list = get_providers()
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:
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')
if sceneName == "null":
sceneName = "None"
language = request.form.get('language')
hi = request.form.get('hi').capitalize()
forced = request.form.get('forced').capitalize()
profileId = request.form.get('profileId')
title = request.form.get('title')
providers_list = get_providers()
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')
if not data:
data = []
@ -690,8 +686,12 @@ class EpisodesSubtitlesManualDownload(Resource):
sonarrEpisodeId = request.form.get('sonarrEpisodeId')
title = request.form.get('title')
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:
result = manual_download_subtitle(episodePath, language, audio_language, hi, forced, subtitle,
@ -887,10 +887,6 @@ class Movies(Resource):
if moviesId:
result = database.execute("SELECT * FROM table_movies WHERE radarrId=? ORDER BY sortTitle ASC LIMIT ? "
"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:
result = database.execute("SELECT * FROM table_movies ORDER BY sortTitle ASC LIMIT ? OFFSET ?",
(length, start))
@ -899,11 +895,10 @@ class Movies(Resource):
item.update({"DT_RowId": 'row_' + str(item['radarrId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
item.update({"audio_language": get_audio_profile_languages(movie_id=item['radarrId'])})
# Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']):
@ -911,6 +906,9 @@ class Movies(Resource):
"code2": subs,
"code3": alpha3_from_alpha2(subs)}
# Parse profileId
item['profileId'] = {"id": item['profileId'], "name": get_profile_id_name(item['profileId'])}
# Parse alternate titles
if 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)})
# Add the movie desired subtitles language code2
try:
item.update({"desired_languages": desired_languages})
except NameError:
pass
item.update({"desired_languages": get_desired_languages(item['profileId']['id'])})
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=result)
@authenticate
def post(self):
radarrId = request.args.get('radarrid')
lang = request.form.getlist('languages')
if len(lang) > 0:
pass
else:
lang = 'None'
languages_profile = request.form.get('languages')
single_language = settings.general.getboolean('single_language')
if single_language:
if str(lang) == "['None']":
lang = 'None'
else:
lang = str(lang)
else:
if str(lang) == "['']":
lang = '[]'
if languages_profile == 'None':
languages_profile = None
hi = request.form.get('hi')
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))
database.execute("UPDATE table_movies SET profileId=? WHERE radarrId=?", (languages_profile, radarrId))
list_missing_subtitles_movies(no=radarrId)
@ -1024,7 +1000,7 @@ class MoviesEditor(Resource):
def get(self):
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")
row_count = len(result)
@ -1034,11 +1010,10 @@ class MoviesEditor(Resource):
item.update({"DT_RowId": 'row_' + str(item['radarrId'])})
# Parse audio language
item.update({"audio_language": {"name": item['audio_language'],
"code2": alpha2_from_language(item['audio_language']) or None,
"code3": alpha3_from_language(item['audio_language']) or None}})
item.update({"audio_language": get_audio_profile_languages(movie_id=item['radarrId'])})
# Parse desired languages
item['languages'] = str(get_desired_languages(item['profileId']))
if item['languages'] and item['languages'] != 'None':
item.update({"languages": ast.literal_eval(item['languages'])})
for i, subs in enumerate(item['languages']):
@ -1046,42 +1021,29 @@ class MoviesEditor(Resource):
"code2": 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)
class MoviesEditSave(Resource):
@authenticate
def post(self):
lang = request.form.getlist('languages[]')
hi = request.form.getlist('hi[]')
forced = request.form.getlist('forced[]')
lang = request.form.get('languages')
if lang == ['None']:
lang = 'None'
if lang == 'None':
lang = None
radarrIdList = []
radarrIdLangList = []
radarrIdHiList = []
radarrIdForcedList = []
for item in request.form.getlist('radarrid[]'):
radarrid = item.lstrip('row_')
radarrIdList.append(radarrid)
if len(lang):
radarrIdLangList.append([str(lang), radarrid])
if len(hi):
radarrIdHiList.append([hi[0], radarrid])
if len(forced):
radarrIdForcedList.append([forced[0], radarrid])
radarrIdLangList.append([lang, radarrid])
try:
if len(lang):
database.execute("UPDATE table_movies SET languages=? WHERE radarrId=?", radarrIdLangList,
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)
database.execute("UPDATE table_movies SET profileId=? WHERE radarrId=?", radarrIdLangList,
execute_many=True)
except:
pass
else:
@ -1131,8 +1093,12 @@ class MovieSubtitlesDownload(Resource):
title = request.form.get('title')
providers_list = get_providers()
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:
result = download_subtitle(moviePath, language, audio_language, hi, forced, providers_list,
@ -1174,14 +1140,12 @@ class MovieSubtitlesManualSearch(Resource):
sceneName = request.form.get('sceneName')
if sceneName == "null":
sceneName = "None"
language = request.form.get('language')
hi = request.form.get('hi').capitalize()
forced = request.form.get('forced').capitalize()
profileId = request.form.get('profileId')
title = request.form.get('title')
providers_list = get_providers()
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')
if not data:
data = []
@ -1204,8 +1168,12 @@ class MovieSubtitlesManualDownload(Resource):
radarrId = request.form.get('radarrId')
title = request.form.get('title')
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:
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_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE action IN (" +
','.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",
(minimum_timestamp,))
get_exclusion_clause('series') + " GROUP BY table_history.video_path", (minimum_timestamp,))
for upgradable_episode in upgradable_episodes:
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_history.description, table_history.sonarrSeriesId, table_episodes.path, "
"table_history.language, table_history.score, table_shows.tags, table_history.action, "
"table_history.subtitles_path, table_history.sonarrEpisodeId, table_history.provider "
"FROM table_history LEFT JOIN table_shows on table_shows.sonarrSeriesId = "
"table_history.sonarrSeriesId LEFT JOIN table_episodes on "
"table_history.subtitles_path, table_history.sonarrEpisodeId, table_history.provider, "
"table_shows.seriesType FROM table_history LEFT JOIN table_shows on "
"table_shows.sonarrSeriesId = table_history.sonarrSeriesId LEFT JOIN table_episodes on "
"table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
"table_episodes.title is not NULL ORDER BY timestamp DESC LIMIT ? OFFSET ?",
(length, start))
for item in data:
# 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})
else:
item.update({"upgradable": False})
@ -1516,8 +1483,8 @@ class HistoryMovies(Resource):
upgradable_movies = database.execute(
"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 (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" +
get_exclusion_clause('movie') + " GROUP BY video_path, language", (minimum_timestamp,))
','.join(map(str, query_actions)) + ") AND timestamp > ? AND score is not NULL" +
get_exclusion_clause('movie') + " GROUP BY video_path", (minimum_timestamp,))
for upgradable_movie in upgradable_movies:
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, "
"table_episodes.season || 'x' || table_episodes.episode as episode_number, "
"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.failedAttempts, table_shows.seriesType FROM table_episodes INNER JOIN "
"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 != '[]'" +
get_exclusion_clause('movie'))
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 != '[]'" +
get_exclusion_clause('movie') + " ORDER BY _rowid_ DESC LIMIT " + length + " OFFSET " +
start)
@ -2002,6 +1969,7 @@ api.add_resource(BadgesSeries, '/badges_series')
api.add_resource(BadgesMovies, '/badges_movies')
api.add_resource(BadgesProviders, '/badges_providers')
api.add_resource(Languages, '/languages')
api.add_resource(LanguagesProfiles, '/languages_profiles')
api.add_resource(Notifications, '/notifications')
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_debugtoolbar import DebugToolbarExtension
from flask_socketio import SocketIO

View File

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

View File

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

View File

@ -1,7 +1,10 @@
# coding=utf-8
import os
import ast
import sqlite3
import logging
import json
from sqlite3worker import Sqlite3Worker
@ -9,6 +12,9 @@ from get_args import args
from helper import path_mappings
from config import settings
global profile_id_list
profile_id_list = []
def db_init():
if not os.path.exists(os.path.join(args.config_dir, 'db', 'bazarr.db')):
@ -95,6 +101,7 @@ def db_upgrade():
['table_shows', 'tags', 'text', '[]'],
['table_shows', 'seriesType', 'text', ''],
['table_shows', 'imdbId', 'text', ''],
['table_shows', 'profileId', 'integer'],
['table_episodes', 'format', 'text'],
['table_episodes', 'resolution', 'text'],
['table_episodes', 'video_codec', 'text'],
@ -112,6 +119,7 @@ def db_upgrade():
['table_movies', 'forced', 'text', 'False'],
['table_movies', 'movie_file_id', 'integer'],
['table_movies', 'tags', 'text', '[]'],
['table_movies', 'profileId', 'integer'],
['table_history', 'video_path', 'text'],
['table_history', 'language', 'text'],
['table_history', 'provider', 'text'],
@ -143,20 +151,61 @@ def db_upgrade():
except:
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
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist (sonarr_series_id integer, sonarr_episode_id integer, "
"timestamp integer, provider text, subs_id text, language text)")
database.execute("CREATE TABLE IF NOT EXISTS table_blacklist_movie (radarr_id integer, timestamp integer, "
"provider text, subs_id text, language text)")
# Create languages profiles table and populate it
lang_table_content = database.execute("SELECT * FROM table_languages_profiles")
if isinstance(lang_table_content, list):
lang_table_exist = True
else:
lang_table_exist = False
database.execute("CREATE TABLE IF NOT EXISTS table_languages_profiles ("
"profileId INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL, "
"cutoff INTEGER, items TEXT NOT NULL)")
if not lang_table_exist:
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):
where_clause = ''
@ -184,3 +233,115 @@ def get_exclusion_clause(type):
where_clause += ' AND table_shows.seriesType != "' + type + '"'
return where_clause
def update_profile_id_list():
global profile_id_list
profile_id_list = database.execute("SELECT profileId, name, cutoff, items FROM table_languages_profiles")
def get_profiles_list(profile_id=None):
if not len(profile_id_list):
update_profile_id_list()
if profile_id:
for profile in profile_id_list:
if profile['profileId'] == profile_id:
return profile
else:
return profile_id_list
def get_desired_languages(profile_id):
languages = []
if not len(profile_id_list):
update_profile_id_list()
if profile_id:
for profile in profile_id_list:
profileId, name, cutoff, items = profile.values()
if profileId == int(profile_id):
items_list = ast.literal_eval(items)
languages = [x['language'] for x in items_list]
break
return languages
def get_profile_id_name(profile_id):
name_from_id = None
if not len(profile_id_list):
update_profile_id_list()
if profile_id:
for profile in profile_id_list:
profileId, name, cutoff, items = profile.values()
if profileId == int(profile_id):
name_from_id = name
break
return name_from_id
def get_profile_cutoff(profile_id):
cutoff_language = None
if not len(profile_id_list):
update_profile_id_list()
if profile_id:
cutoff_language = []
for profile in profile_id_list:
profileId, name, cutoff, items = profile.values()
if cutoff:
if profileId == int(profile_id):
for item in ast.literal_eval(items):
if item['id'] == cutoff:
return [item]
elif cutoff == 65535:
cutoff_language.append(item)
if not len(cutoff_language):
cutoff_language = None
return cutoff_language
def get_audio_profile_languages(series_id=None, episode_id=None, movie_id=None):
from get_languages import alpha2_from_language, alpha3_from_language
audio_languages = []
if series_id:
audio_languages_list_str = database.execute("SELECT audio_language FROM table_shows WHERE sonarrSeriesId=?",
(series_id,), only_one=True)['audio_language']
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
from enzyme.exceptions import MalformedMKVError
import logging

View File

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

View File

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

View File

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

View File

@ -1,11 +1,12 @@
# coding=utf-8
import requests
import logging
from database import database, dict_converter, get_exclusion_clause
from config import settings, url_sonarr
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 event_handler import event_stream
@ -85,12 +86,12 @@ def sync_episodes():
videoCodec = None
audioCodec = None
audio_language = None
audio_language = []
if 'language' in episode['episodeFile'] and len(episode['episodeFile']['language']):
item = episode['episodeFile']['language']
if isinstance(item, dict):
if 'name' in item:
audio_language = item['name']
audio_language.append(item['name'])
else:
audio_language = database.execute("SELECT audio_language FROM table_shows WHERE "
"sonarrSeriesId=?", (episode['seriesId'],),
@ -113,7 +114,7 @@ def sync_episodes():
'video_codec': videoCodec,
'audio_codec': audioCodec,
'episode_file_id': episode['episodeFile']['id'],
'audio_language': audio_language})
'audio_language': str(audio_language)})
else:
episodes_to_add.append({'sonarrSeriesId': episode['seriesId'],
'sonarrEpisodeId': episode['id'],
@ -128,7 +129,7 @@ def sync_episodes():
'video_codec': videoCodec,
'audio_codec': audioCodec,
'episode_file_id': episode['episodeFile']['id'],
'audio_language': audio_language})
'audio_language': str(audio_language)})
# Remove old episodes from DB
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))

View File

@ -1,7 +1,6 @@
# coding=utf-8
import pycountry
import ast
from subzero.language import Language
from database import database
@ -76,48 +75,5 @@ def get_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__':
load_language_in_db()

View File

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

View File

@ -22,13 +22,11 @@ def update_series():
serie_default_enabled = settings.general.getboolean('serie_default_enabled')
if serie_default_enabled is True:
serie_default_language = settings.general.serie_default_language
serie_default_hi = settings.general.serie_default_hi
serie_default_forced = settings.general.serie_default_forced
serie_default_profile = settings.general.serie_default_profile
if serie_default_profile == '':
serie_default_profile = None
else:
serie_default_language = '[]'
serie_default_hi = 'False'
serie_default_forced = 'False'
serie_default_profile = None
audio_profiles = get_profile_list()
tagsDict = get_tags()
@ -76,6 +74,7 @@ def update_series():
if show['alternateTitles'] is not None:
alternate_titles = str([item['title'] for item in show['alternateTitles']])
audio_language = []
if sonarr_version.startswith('2'):
audio_language = profile_id_to_language(show['qualityProfileId'], audio_profiles)
else:
@ -96,7 +95,7 @@ def update_series():
'overview': overview,
'poster': poster,
'fanart': fanart,
'audio_language': audio_language,
'audio_language': str(audio_language),
'sortTitle': show['sortTitle'],
'year': str(show['year']),
'alternateTitles': alternate_titles,
@ -107,20 +106,18 @@ def update_series():
series_to_add.append({'title': show["title"],
'path': show["path"],
'tvdbId': show["tvdbId"],
'languages': serie_default_language,
'hearing_impaired': serie_default_hi,
'sonarrSeriesId': show["id"],
'overview': overview,
'poster': poster,
'fanart': fanart,
'audio_language': audio_language,
'audio_language': str(audio_language),
'sortTitle': show['sortTitle'],
'year': str(show['year']),
'alternateTitles': alternate_titles,
'forced': serie_default_forced,
'tags': str(tags),
'seriesType': show['seriesType'],
'imdbId': imdbId})
'imdbId': imdbId,
'profileId': serie_default_profile})
# Remove old series from DB
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):
profiles_to_return = []
for profile in profiles:
if id_ == profile[0]:
return profile[1]
profiles_to_return.append(profile[1])
return profiles_to_return
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 subsyncer import subsync
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 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)
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)
final_subtitles = []
initial_hi = True if hi == "True" else False
if hi == "True":
hi = "force HI"
else:
hi = "force non-HI"
initial_language_set = set()
language_set = set()
if forced == "True":
providers_auth['podnapisi']['only_foreign'] = True
providers_auth['subscene']['only_foreign'] = True
providers_auth['opensubtitles']['only_foreign'] = True
else:
providers_auth['podnapisi']['only_foreign'] = False
providers_auth['subscene']['only_foreign'] = False
providers_auth['opensubtitles']['only_foreign'] = False
# where [3] is items list of dict(id, lang, forced, hi)
language_items = ast.literal_eval(get_profiles_list(profile_id=int(profileId))['items'])
for language in language_items:
lang_id, lang, forced, hi, audio_exclude = language.values()
for lang in ast.literal_eval(language):
lang = alpha3_from_alpha2(lang)
if lang == 'pob':
lang_obj = Language('por', 'BR')
if forced == "True":
lang_obj = Language.rebuild(lang_obj, forced=True)
else:
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":
lang_obj_hi = Language.rebuild(lang_obj, hi=True)
language_set.add(lang_obj_hi)
providers_auth['podnapisi']['also_foreign'] = True
providers_auth['opensubtitles']['also_foreign'] = True
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_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),
throttle_callback=provider_throttle,
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:
subtitles = []
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)
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)
if 'hash' not in 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):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_shows.seriesType, table_episodes.audio_language FROM table_episodes "
"INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_shows.seriesType, table_episodes.audio_language, table_shows.title "
"FROM table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_episodes.sonarrSeriesId WHERE table_episodes.sonarrSeriesId=? and "
"missing_subtitles!='[]'" + get_exclusion_clause('series'), (no,))
if not episodes_details:
logging.debug("BAZARR no episode for that sonarrSeriesId can be found in database:", str(no))
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_auth = get_providers_auth()
@ -704,15 +732,21 @@ def series_download_subtitles(no):
if providers_list:
for language in ast.literal_eval(episode['missing_subtitles']):
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']),
str(alpha3_from_alpha2(language.split(':')[0])),
episode['audio_language'],
series_details['hearing_impaired'],
audio_language,
"True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False",
providers_list,
providers_auth,
str(episode['scene_name']),
series_details['title'],
episode['title'],
'series')
if result is not None:
message = result[0]
@ -740,10 +774,9 @@ def series_download_subtitles(no):
def episode_download_subtitles(no):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, monitored, "
"table_episodes.sonarrEpisodeId, table_episodes.scene_name, table_shows.tags, "
"table_shows.hearing_impaired, table_shows.title, table_shows.sonarrSeriesId, "
"table_shows.forced, table_episodes.audio_language, table_shows.seriesType FROM "
"table_episodes LEFT JOIN table_shows on table_episodes.sonarrSeriesId = "
"table_shows.sonarrSeriesId WHERE sonarrEpisodeId=?" +
"table_shows.title, table_shows.sonarrSeriesId, table_episodes.audio_language, "
"table_shows.seriesType FROM table_episodes LEFT JOIN table_shows on "
"table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId WHERE sonarrEpisodeId=?" +
get_exclusion_clause('series'), (no,))
if not episodes_details:
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:
for language in ast.literal_eval(episode['missing_subtitles']):
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']),
str(alpha3_from_alpha2(language.split(':')[0])),
episode['audio_language'],
episode['hearing_impaired'],
audio_language,
"True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False",
providers_list,
providers_auth,
@ -791,7 +830,7 @@ def episode_download_subtitles(no):
def movies_download_subtitles(no):
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,))
if not len(movies):
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):
if providers_list:
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']),
str(alpha3_from_alpha2(language.split(':')[0])),
movie['audio_language'],
movie['hearing_impaired'],
audio_language,
"True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False",
providers_list,
providers_auth,
@ -845,8 +890,8 @@ def movies_download_subtitles(no):
def wanted_download_subtitles(path, l, count_episodes):
episodes_details = database.execute("SELECT table_episodes.path, table_episodes.missing_subtitles, "
"table_episodes.sonarrEpisodeId, table_episodes.sonarrSeriesId, "
"table_shows.hearing_impaired, table_episodes.audio_language, table_episodes.scene_name,"
"table_episodes.failedAttempts, table_shows.title, table_shows.forced "
"table_episodes.audio_language, table_episodes.scene_name,"
"table_episodes.failedAttempts, table_shows.title "
"FROM table_episodes LEFT JOIN table_shows on "
"table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId "
"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)):
if attempt[i][0] == language:
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']),
str(alpha3_from_alpha2(language.split(':')[0])),
episode['audio_language'],
episode['hearing_impaired'],
audio_language,
"True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False",
providers_list,
providers_auth,
@ -910,8 +961,8 @@ def wanted_download_subtitles(path, l, count_episodes):
def wanted_download_subtitles_movie(path, l, count_movies):
movies_details = database.execute(
"SELECT path, missing_subtitles, radarrId, hearing_impaired, audio_language, sceneName, "
"failedAttempts, title, forced FROM table_movies WHERE path = ? "
"SELECT path, missing_subtitles, radarrId, audio_language, sceneName, "
"failedAttempts, title FROM table_movies WHERE path = ? "
"AND missing_subtitles != '[]'", (path_mappings.path_replace_reverse_movie(path),))
providers_list = get_providers()
@ -936,10 +987,16 @@ def wanted_download_subtitles_movie(path, l, count_movies):
for i in range(len(attempt)):
if attempt[i][0] == language:
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']),
str(alpha3_from_alpha2(language.split(':')[0])),
movie['audio_language'],
movie['hearing_impaired'],
audio_language,
"True" if language.endswith(':hi') else "False",
"True" if language.endswith(':forced') else "False",
providers_list,
providers_auth,
@ -1144,17 +1201,17 @@ def upgrade_subtitles():
if settings.general.getboolean('use_sonarr'):
upgradable_episodes = database.execute("SELECT table_history.video_path, table_history.language, "
"table_history.score, table_shows.hearing_impaired, "
"table_episodes.audio_language, table_episodes.scene_name, table_episodes.title,"
"table_episodes.sonarrSeriesId, table_episodes.sonarrEpisodeId,"
"MAX(table_history.timestamp) as timestamp, table_episodes.monitored, "
"table_shows.languages, table_shows.forced, table_shows.tags, "
"table_shows.seriesType FROM table_history INNER JOIN table_shows on "
"table_shows.sonarrSeriesId = table_history.sonarrSeriesId INNER JOIN "
"table_episodes on table_episodes.sonarrEpisodeId = "
"table_history.sonarrEpisodeId WHERE action IN "
"(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score"
" is not null" + get_exclusion_clause('series') + " GROUP BY "
"table_history.score, table_shows.tags, table_shows.profileId, "
"table_episodes.audio_language, table_episodes.scene_name, "
"table_episodes.title, table_episodes.sonarrSeriesId, "
"table_episodes.sonarrEpisodeId, MAX(table_history.timestamp) "
"as timestamp, table_episodes.monitored, table_shows.seriesType FROM "
"table_history INNER JOIN table_shows on table_shows.sonarrSeriesId = "
"table_history.sonarrSeriesId INNER JOIN table_episodes on "
"table_episodes.sonarrEpisodeId = table_history.sonarrEpisodeId WHERE "
"action IN (" + ','.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",
(minimum_timestamp,))
upgradable_episodes_not_perfect = []
@ -1177,13 +1234,13 @@ def upgrade_subtitles():
if settings.general.getboolean('use_radarr'):
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.radarrId, MAX(table_history_movie.timestamp) as timestamp, "
"table_movies.languages, table_movies.forced, table_movies.tags, "
"table_movies.monitored FROM table_history_movie INNER JOIN table_movies "
"on table_movies.radarrId = table_history_movie.radarrId WHERE action IN "
"(" + ','.join(map(str, query_actions)) + ") AND timestamp > ? AND score "
"table_movies.tags, table_movies.monitored FROM table_history_movie 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" + get_exclusion_clause('movie') + " GROUP BY "
"table_history_movie.video_path, table_history_movie.language",
(minimum_timestamp,))
@ -1210,110 +1267,113 @@ def upgrade_subtitles():
if settings.general.getboolean('use_sonarr'):
for i, episode in enumerate(episodes_to_upgrade, 1):
if episode['languages'] in [None, 'None', '[]']:
continue
providers = get_providers()
if not providers:
logging.info("BAZARR All providers are throttled")
return
if episode['languages']:
desired_languages = ast.literal_eval(str(episode['languages']))
if episode['forced'] == "True":
forced_languages = [l + ":forced" for l in desired_languages]
elif episode['forced'] == "Both":
forced_languages = [l + ":forced" for l in desired_languages] + desired_languages
if episode['language'].endswith('forced'):
language = episode['language'].split(':')[0]
is_forced = True
is_hi = False
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
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:
forced_languages = desired_languages
if episode['language'] in forced_languages:
if episode['language'].endswith('forced'):
language = episode['language'].split(':')[0]
is_forced = "True"
else:
language = episode['language']
is_forced = "False"
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)
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'):
for i, movie in enumerate(movies_to_upgrade, 1):
if movie['languages'] in [None, 'None', '[]']:
continue
providers = get_providers()
if not providers:
logging.info("BAZARR All providers are throttled")
return
if movie['languages']:
desired_languages = ast.literal_eval(str(movie['languages']))
if movie['forced'] == "True":
forced_languages = [l + ":forced" for l in desired_languages]
elif movie['forced'] == "Both":
forced_languages = [l + ":forced" for l in desired_languages] + desired_languages
else:
forced_languages = desired_languages
if not providers:
logging.info("BAZARR All providers are throttled")
return
if episode['language'].endswith('forced'):
language = episode['language'].split(':')[0]
is_forced = True
is_hi = False
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:
if movie['language'].endswith('forced'):
language = movie['language'].split(':')[0]
is_forced = "True"
else:
language = movie['language']
is_forced = "False"
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['video_path']),
str(alpha3_from_alpha2(language)),
movie['audio_language'],
movie['hearing_impaired'],
is_forced,
providers_list,
providers_auth,
str(movie['sceneName']),
movie['title'],
'movie',
forced_minimum_score=int(movie['score']),
is_upgrade=True)
if result is not None:
message = result[0]
path = result[1]
forced = result[5]
language_code = result[2] + ":forced" if forced else result[2]
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles_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)
send_notifications_movie(movie['radarrId'], message)
result = download_subtitle(path_mappings.path_replace_movie(movie['video_path']),
str(alpha3_from_alpha2(language)),
audio_language,
is_hi,
is_forced,
providers_list,
providers_auth,
str(movie['sceneName']),
movie['title'],
'movie',
forced_minimum_score=int(movie['score']),
is_upgrade=True)
if result is not None:
message = result[0]
path = result[1]
forced = result[5]
language_code = result[2] + ":forced" if forced else result[2]
provider = result[3]
score = result[4]
subs_id = result[6]
subs_path = result[7]
store_subtitles_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)
send_notifications_movie(movie['radarrId'], message)
def postprocessing(command, path):

View File

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

View File

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

View File

@ -9,8 +9,8 @@ from guess_language import guess_language
from subliminal_patch import core, search_external_subtitles
from subzero.language import Language
from database import database
from get_languages import alpha2_from_alpha3, get_language_set
from database import database, get_profiles_list, get_profile_cutoff
from get_languages import alpha2_from_alpha3, language_from_alpha2, get_language_set
from config import settings
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):
if no is not None:
episodes_subtitles_clause = " WHERE table_episodes.sonarrSeriesId=" + str(no)
elif epno is not None:
if epno is not None:
episodes_subtitles_clause = " WHERE table_episodes.sonarrEpisodeId=" + str(epno)
elif no is not None:
episodes_subtitles_clause = " WHERE table_episodes.sonarrSeriesId=" + str(no)
else:
episodes_subtitles_clause = ""
episodes_subtitles = database.execute("SELECT table_shows.sonarrSeriesId, table_episodes.sonarrEpisodeId, "
"table_episodes.subtitles, table_shows.languages, table_shows.forced, "
"table_shows.hearing_impaired FROM table_episodes LEFT JOIN table_shows "
"on table_episodes.sonarrSeriesId = table_shows.sonarrSeriesId" +
episodes_subtitles_clause)
"table_episodes.subtitles, table_shows.profileId, "
"table_episodes.audio_language FROM table_episodes "
"LEFT JOIN table_shows on table_episodes.sonarrSeriesId = "
"table_shows.sonarrSeriesId" + episodes_subtitles_clause)
if isinstance(episodes_subtitles, str):
logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + episodes_subtitles)
return
missing_subtitles_global = []
use_embedded_subs = settings.general.getboolean('use_embedded_subs')
for episode_subtitles in episodes_subtitles:
actual_subtitles_temp = []
desired_subtitles_temp = []
actual_subtitles = []
desired_subtitles = []
missing_subtitles = []
if episode_subtitles['subtitles'] is not None:
if use_embedded_subs:
actual_subtitles = ast.literal_eval(episode_subtitles['subtitles'])
else:
actual_subtitles_temp = ast.literal_eval(episode_subtitles['subtitles'])
for subtitle in actual_subtitles_temp:
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
missing_subtitles_text = '[]'
if episode_subtitles['profileId']:
# get desired subtitles
desired_subtitles_temp = get_profiles_list(profile_id=episode_subtitles['profileId'])
desired_subtitles_list = []
if desired_subtitles_temp:
for language in ast.literal_eval(desired_subtitles_temp['items']):
if language['audio_exclude'] == "True":
if language_from_alpha2(language['language']) in ast.literal_eval(episode_subtitles['audio_language']):
continue
desired_subtitles_list.append([language['language'], language['forced'], language['hi']])
if episode_subtitles['hearing_impaired'] == "True" and desired_subtitles is not None:
for i, desired_subtitle in desired_subtitles_enum:
desired_subtitles[i] = desired_subtitle + ":hi"
elif episode_subtitles['forced'] == "True" and desired_subtitles is not None:
for i, desired_subtitle in desired_subtitles_enum:
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")
# get existing subtitles
actual_subtitles_list = []
if episode_subtitles['subtitles'] is not None:
if use_embedded_subs:
actual_subtitles_temp = ast.literal_eval(episode_subtitles['subtitles'])
else:
actual_subtitles_list.append(item[0])
missing_subtitles = list(set(desired_subtitles) - set(actual_subtitles_list))
hi_subs_to_remove = []
for item in missing_subtitles:
if item + ':hi' in actual_subtitles_list:
hi_subs_to_remove.append(item)
missing_subtitles = list(set(missing_subtitles) - set(hi_subs_to_remove))
missing_subtitles_global.append(tuple([str(missing_subtitles), episode_subtitles['sonarrEpisodeId'],
episode_subtitles['sonarrSeriesId']]))
actual_subtitles_temp = [x for x in ast.literal_eval(episode_subtitles['subtitles']) if x[1]]
for subtitles in actual_subtitles_temp:
subtitles = subtitles[0].split(':')
lang = subtitles[0]
forced = False
hi = False
if len(subtitles) > 1:
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=?",
(missing_subtitles_item[0], missing_subtitles_item[1]))
(missing_subtitles_text, episode_subtitles['sonarrEpisodeId']))
if send_event:
event_stream(type='episode', action='update', series=missing_subtitles_item[2],
episode=missing_subtitles_item[1])
event_stream(type='episode', action='update', series=episode_subtitles['sonarrSeriesId'],
episode=episode_subtitles['sonarrEpisodeId'])
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:
movies_subtitles_clause = " WHERE radarrId=" + str(no)
else:
movies_subtitles_clause = ""
movies_subtitles = database.execute("SELECT radarrId, subtitles, languages, forced, hearing_impaired FROM "
"table_movies" + movies_subtitles_clause)
movies_subtitles = database.execute("SELECT radarrId, subtitles, profileId, audio_language FROM table_movies" +
movies_subtitles_clause)
if isinstance(movies_subtitles, str):
logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + movies_subtitles)
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:
desired_subtitles[i] = desired_subtitle + ":hi"
elif movie_subtitles['forced'] == "True" and desired_subtitles is not None:
for i, desired_subtitle in desired_subtitles_enum:
desired_subtitles[i] = desired_subtitle + ":forced"
elif movie_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(['[]', movie_subtitles['radarrId']]))
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")
use_embedded_subs = settings.general.getboolean('use_embedded_subs')
for movie_subtitles in movies_subtitles:
missing_subtitles_text = '[]'
if movie_subtitles['profileId']:
# get desired subtitles
desired_subtitles_temp = get_profiles_list(profile_id=movie_subtitles['profileId'])
desired_subtitles_list = []
if desired_subtitles_temp:
for language in ast.literal_eval(desired_subtitles_temp['items']):
if language['audio_exclude'] == "True":
if language_from_alpha2(language['language']) in ast.literal_eval(movie_subtitles['audio_language']):
continue
desired_subtitles_list.append([language['language'], language['forced'], language['hi']])
# get existing subtitles
actual_subtitles_list = []
if movie_subtitles['subtitles'] is not None:
if use_embedded_subs:
actual_subtitles_temp = ast.literal_eval(movie_subtitles['subtitles'])
else:
actual_subtitles_list.append(item[0])
missing_subtitles = list(set(desired_subtitles) - set(actual_subtitles_list))
hi_subs_to_remove = []
for item in missing_subtitles:
if item + ':hi' in actual_subtitles_list:
hi_subs_to_remove.append(item)
missing_subtitles = list(set(missing_subtitles) - set(hi_subs_to_remove))
missing_subtitles_global.append(tuple([str(missing_subtitles), movie_subtitles['radarrId']]))
for missing_subtitles_item in missing_subtitles_global:
actual_subtitles_temp = [x for x in ast.literal_eval(movie_subtitles['subtitles']) if x[1]]
for subtitles in actual_subtitles_temp:
subtitles = subtitles[0].split(':')
lang = subtitles[0]
forced = False
hi = False
if len(subtitles) > 1:
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=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=?",
(missing_subtitles_item[0], missing_subtitles_item[1]))
(missing_subtitles_text, movie_subtitles['radarrId']))
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')

View File

@ -1,32 +1,29 @@
# coding=utf-8
bazarr_version = '0.9.0.8'
bazarr_version = '0.9.1'
import os
os.environ["BAZARR_VERSION"] = bazarr_version
import gc
import sys
import libs
import hashlib
import apprise
import requests
import calendar
from get_args import args
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 database import database, dict_mapper
from database import database
from notifier import update_notifier
from urllib.parse import unquote
from get_languages import load_language_in_db, language_from_alpha3, language_from_alpha2, alpha2_from_alpha3, \
alpha3_from_alpha2
from get_languages import load_language_in_db, language_from_alpha2, alpha3_from_alpha2
from flask import make_response, request, redirect, abort, render_template, Response, session, flash, url_for, \
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_series import update_series
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 get_args import args
if not args.no_update:
@ -21,6 +22,7 @@ from calendar import day_name
import pretty
from random import randrange
from event_handler import event_stream
import os
class Scheduler:
@ -62,10 +64,10 @@ class Scheduler:
if args.no_tasks:
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(
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):
self.aps_scheduler.modify_job(taskid, next_run_time=datetime.now())
@ -252,3 +254,12 @@ class 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 logging
import os

View File

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

View File

@ -269,11 +269,11 @@
class="fas fa-cogs"></i><span class="hide-menu"> Settings</span></a>
<ul aria-expanded="false" class="collapse">
<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('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('settingsscheduler') }}"> Scheduler</a></li>
</ul>

View File

@ -87,7 +87,9 @@
title="None" data-html="true"></i>
</div>
<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="seriesFileCount" 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>
</div>
<div class="row">
<h5><span id="seriesSubtitlesLanguages"></span></h5>
<h5><span id="seriesSubtitlesLanguagesProfile" class="badge badge-secondary"></span></h5>
</div>
<div class="row">
<h5><span id="seriesHearingImpaired" class="badge badge-secondary"></span></h5>
@ -117,7 +119,7 @@
<th></th>
<th>Episode</th>
<th>Title</th>
<th>Audio Language</th>
<th>Audio Languages</th>
<th>Existing Subtitles</th>
<th>Missing Subtitles</th>
<th>Manual Search</th>
@ -287,42 +289,19 @@
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 text-right">
Audio Profile
Audio Profile Languages
</div>
<div class="form-group col-sm-8 pl-sm-0">
<span id="edit_audio_language_span"></span>
</div>
</div>
<br>
<div class="row">
<div class="col-sm-3 text-right">
Subtitles Language(s)
Languages Profile
</div>
<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>
</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>
<select class="selectpicker" id="edit_languages_select" name="languages"></select>
</div>
</div>
</div>
@ -564,6 +543,7 @@
episodesDetailsRefresh();
getLanguages();
getEnabledLanguages();
getLanguagesProfiles();
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,
@ -686,7 +675,7 @@
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,
render: function (data) {
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 {
return ''
}
@ -704,7 +693,7 @@
data: null,
render: function (data) {
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 {
return ''
}
@ -783,9 +772,7 @@
episodePath = $(this).attr("data-episodePath");
sceneName = $(this).attr("data-sceneName");
language = $(this).attr("data-language");
hi = seriesDetails['hearing_impaired'];
forced = seriesDetails['forced'];
profileId = seriesDetails['profileId'].id;
sonarrSeriesId = seriesDetails['sonarrSeriesId'];
sonarrEpisodeId = $(this).attr("data-sonarrEpisodeId");
var languages = Array.from(seriesDetails['languages']);
@ -795,9 +782,7 @@
const values = {
episodePath: episodePath,
sceneName: sceneName,
language: language,
hi: hi,
forced: forced,
profileId: profileId,
sonarrSeriesId: sonarrSeriesId,
sonarrEpisodeId: sonarrEpisodeId,
title: seriesDetails['title']
@ -906,7 +891,7 @@
data: null,
searchable: false,
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_sonarrEpisodeId').val($(this).data("sonarrepisodeid"));
$('#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();
$.each(enabledLanguages, function (i, item) {
@ -1340,26 +1325,20 @@
$('#edit_button').on('click', function (e) {
e.preventDefault();
$("#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_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') {
$('#edit_languages_select').selectpicker({maxOptions: 1});
$('#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').append('<option value="None">None</option>');
$.each(languagesProfiles, function (i, item) {
$('#edit_languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
});
$("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array();
$.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();
$('#edit_languages_select').selectpicker('val', ((seriesDetails['profileId'].id) ? seriesDetails['profileId'].id : 'None'));
$('#editModal')
.modal({
@ -1826,30 +1805,23 @@
$('#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').attr("data-original-title", seriesDetails['mapped_path']);
$('#seriesFileCount').text(seriesDetails['episodeFileCount'] + ' files');
$('#seriesType').text(seriesDetails['seriesType']);
var languages = '';
if (seriesDetails['languages'] && seriesDetails['languages'] !== 'None') {
seriesDetails['languages'].forEach(appendFunc);
}
$('#seriesSubtitlesLanguagesProfile').text(seriesDetails['profileId'].name);
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']);
if (seriesDetails['desired_languages'] == '[]') {
$('#search_button').hide();
} else {
if (seriesDetails['profileId'].id) {
$('#search_button').show();
} else {
$('#search_button').hide();
}
$('[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>
{% 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>
</div>
<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="movieTags" class="badge badge-secondary" data-toggle="tooltip" data-placement="right"
title="None" data-html="true">Tags</span></h5>
</div>
<div class="row">
<h5><span id="movieSubtitlesLanguages"></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>
<h5><span id="movieSubtitlesLanguagesProfile" class="badge badge-secondary"></span></h5>
</div>
<div class="row">
<span id="movieDescription"></span>
@ -244,33 +242,10 @@
</div>
<div class="row">
<div class="col-sm-3 text-right">
Subtitles Language(s)
Languages Profile
</div>
<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>
</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>
<select class="selectpicker" id="edit_languages_select" name="languages"></select>
</div>
</div>
</div>
@ -511,6 +486,7 @@
movieDetailsRefresh();
getLanguages();
getEnabledLanguages();
getLanguagesProfiles();
//test
$('#movieSubtitles').on('click', '.remove_subtitles', function(e){
@ -544,8 +520,8 @@
moviePath: movieDetails['mapped_path'],
sceneName: movieDetails['sceneName'],
language: $(this).attr("data-language"),
hi: movieDetails['hearing_impaired'],
forced:$(this).attr("data-forced"),
hi: $(this).attr("data-hi"),
forced: $(this).attr("data-forced"),
radarrId: movieDetails['radarrId'],
title: movieDetails['title']
};
@ -569,9 +545,7 @@
moviePath = movieDetails['mapped_path'];
sceneName = movieDetails['sceneName'];
language = movieDetails['desired_languages'];
hi = movieDetails['hearing_impaired'];
forced = movieDetails['forced'];
profileId = movieDetails['profileId'].id;
radarrId = movieDetails['radarrId'];
var languages = Array.from(movieDetails['languages']);
var is_pb = languages.includes('pb');
@ -580,9 +554,7 @@
const values = {
moviePath: moviePath,
sceneName: sceneName,
language: language,
hi: hi,
forced: forced,
profileId: profileId,
radarrId: radarrId,
title: movieDetails['title']
};
@ -683,7 +655,7 @@
{ data: null,
searchable: false,
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_radarrId').val(movieDetails['radarrId']);
$('#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();
$.each(enabledLanguages, function (i, item) {
@ -794,26 +766,20 @@
$('#edit_button').on('click', function(e){
e.preventDefault();
$("#edit_movie_title_span").html(movieDetails['title']);
$("#edit_audio_language_span").text(movieDetails['audio_language']['name']);
$('#edit_radarrId').val(movieDetails['radarrId']);
$("#edit_audio_language_span").empty();
$.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();
if ('{{settings.general.single_language}}' === 'True') {
$('#edit_languages_select').selectpicker({maxOptions: 1});
$('#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').append('<option value="None">None</option>');
$.each(languagesProfiles, function (i, item) {
$('#edit_languages_select').append('<option value="'+item.profileId+'">'+item.name+'</option>');
});
$("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array();
$.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();
$('#edit_languages_select').selectpicker('val', ((movieDetails['profileId'].id) ? movieDetails['profileId'].id : 'None'));
$('#editModal')
.modal({
@ -1223,22 +1189,15 @@
$('#movieTags').hide();
}
$('#movieAudioLanguage').text(movieDetails['audio_language']['name']);
$('#movieMappedPath').text(movieDetails['mapped_path']);
$("#movieAudioLanguage").empty();
$.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']);
var languages = '';
if (movieDetails['languages'] && movieDetails['languages'] !== 'None') {
movieDetails['languages'].forEach(appendFunc);
}
$('#movieSubtitlesLanguagesProfile').text(movieDetails['profileId'].name);
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']);
var missing_languages = '';
@ -1248,11 +1207,11 @@
function missingAppendFunc(value) {
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) {
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 {
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});
if (movieDetails['desired_languages'] == '[]') {
$('#search_button').hide();
$('#manual_button').hide();
$('#upload_button').hide();
} else {
if (movieDetails['profileId'].id) {
$('#search_button').show();
$('#manual_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>
{% endblock tail %}

View File

@ -24,9 +24,7 @@
<th>Name</th>
<th>Path Exist</th>
<th>Audio Language</th>
<th>Subtitles Languages</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
<th>Languages Profile</th>
<th>Missing Subtitles</th>
<th></th>
</tr>
@ -55,34 +53,10 @@
</div>
<div class="row">
<div class="col-sm-3 text-right">
Subtitles Language(s)
Languages Profile
</div>
<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>
</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>
<select class="selectpicker" id="edit_languages_select" name="languages"></select>
</div>
</div>
</div>
@ -102,8 +76,7 @@
{% block tail %}
<script>
$(document).ready(function () {
getLanguages();
getEnabledLanguages();
getLanguagesProfiles();
events.on('event', function (event) {
var event_json = JSON.parse(event);
@ -185,29 +158,20 @@
}
}
},
{data: "audio_language.name"},
{
data: "languages",
data: 'audio_language',
render: function (data) {
if (data && data !== 'None') {
var languages = '';
data.forEach(appendFunc);
return languages;
} else {
return null
}
var audio_languages = '';
data.forEach(appendFunc);
return audio_languages;
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: "forced",
data: "profileId.name",
className: "dt-center"
},
{
@ -229,7 +193,7 @@
{
data: null,
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) {
e.preventDefault();
$("#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_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') {
$('#edit_languages_select').selectpicker({maxOptions: 1});
}
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').append('<option value="None">None</option>');
$.each(languagesProfiles, function (i, item) {
$('#edit_languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
});
$("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array();
$.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();
$('#edit_languages_select').selectpicker('val', (($(this).data('languages_profile')) ? $(this).data('languages_profile') : 'None'));
$('#editModal')
.modal({
@ -288,20 +244,11 @@
});
});
function getLanguages() {
function getLanguagesProfiles() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=false",
url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) {
availableLanguages = data;
}
});
}
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
success: function (data) {
enabledLanguages = data;
languagesProfiles = data['data'];
}
});
}

View File

@ -17,37 +17,16 @@
<th></th>
<th>Name</th>
<th>Audio Language</th>
<th>Subtitles Languages</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
<th>Languages Profile</th>
</tr>
</thead>
</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-group" style="margin-bottom: 0px;">
<label for="languages_select">Language(s): </label>
<select class="selectpicker" id="languages_select" name="languages" title="No change" multiple></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>
<label for="languages_select">Languages Profile: </label>
<select class="selectpicker" id="languages_select" name="languages"></select>
</div>
</div>
<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>'
}
},
{data: "audio_language.name"},
{
data: "languages",
data: 'audio_language',
render: function (data) {
if (data && data !== 'None') {
var languages = '';
data.forEach(appendFunc);
return languages;
} else {
return null;
}
var audio_languages = '';
data.forEach(appendFunc);
return audio_languages;
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: "forced",
data: "profileId.name",
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) {
e.preventDefault();
const values = {
radarrid: table.rows({selected: true}).ids().toArray(),
languages: $('#languages_select').val(),
hi: $('#hi_select').val(),
forced: $('#forced_select').val()
languages: $('#languages_select').val()
};
$.ajax({
@ -208,9 +170,7 @@
},
success: function () {
table.rows().deselect();
$('#languages_select').selectpicker('val', '');
$('#hi_select').selectpicker('val', '');
$('#forced_select').selectpicker('val', '');
$('#languages_select').selectpicker('val', 'None');
}
});
});
@ -218,11 +178,11 @@
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) {
$('#languages_select').append('<option value="None">None</option>');
$.each(data, function (i, item) {
$('#languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>');
$.each(data['data'], function (i, item) {
$('#languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
});
$("#languages_select").selectpicker("refresh");
}

View File

@ -22,10 +22,8 @@
<tr>
<th>Name</th>
<th>Path Exist</th>
<th>Audio Profile</th>
<th>Subtitles Languages</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
<th>Audio Profile Languages</th>
<th>Languages Profile</th>
<th>Subtitles</th>
<th></th>
</tr>
@ -46,42 +44,19 @@
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 text-right">
Audio Profile
Audio Profile Languages
</div>
<div class="form-group col-sm-8 pl-sm-0">
<span id="edit_audio_language_span"></span>
</div>
</div>
<br>
<div class="row">
<div class="col-sm-3 text-right">
Subtitles Language(s)
Languages Profile
</div>
<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>
</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>
<select class="selectpicker" id="edit_languages_select" name="languages"></select>
</div>
</div>
</div>
@ -102,8 +77,7 @@
{% block tail %}
<script>
$(document).ready(function () {
getLanguages();
getEnabledLanguages();
getLanguagesProfiles();
events.on('event', function (event) {
var event_json = JSON.parse(event);
@ -176,29 +150,20 @@
}
}
},
{data: "audio_language.name"},
{
data: "languages",
data: 'audio_language',
render: function (data) {
if (data && data !== 'None') {
var languages = '';
data.forEach(appendFunc);
return languages;
} else {
return null;
}
var audio_languages = '';
data.forEach(appendFunc);
return audio_languages;
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: "forced",
data: "profileId.name",
className: "dt-center"
},
{
@ -209,7 +174,8 @@
var completed = data.episodeFileCount - data.episodeMissingCount;
var completed_style = '';
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_text = completed + '/' + total;
}
@ -219,7 +185,7 @@
{
data: null,
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) {
e.preventDefault();
$("#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_languages_select').empty();
if ('{{settings.general.single_language}}' === 'True') {
$('#edit_languages_select').selectpicker({maxOptions: 1});
}
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').append('<option value="None">None</option>');
$.each(languagesProfiles, function (i, item) {
$('#edit_languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
});
$("#edit_languages_select").selectpicker("refresh");
var selected_languages = Array();
$.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();
$('#edit_languages_select').selectpicker('val', (($(this).data('languages_profile')) ? $(this).data('languages_profile') : 'None'));
$('#editModal')
.modal({
@ -278,20 +236,11 @@
});
});
function getLanguages() {
function getLanguagesProfiles() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=false",
url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) {
availableLanguages = data;
}
});
}
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
success: function (data) {
enabledLanguages = data;
languagesProfiles = data['data'];
}
});
}

View File

@ -16,38 +16,17 @@
<tr>
<th></th>
<th>Name</th>
<th>Audio Profile</th>
<th>Subtitles Languages</th>
<th>Hearing-Impaired</th>
<th>Forced</th>
<th>Audio Profile Languages</th>
<th>Languages Profile</th>
</tr>
</thead>
</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-group" style="margin-bottom: 0px;">
<label for="languages_select">Language(s): </label>
<select class="selectpicker" id="languages_select" name="languages" title="No change" multiple></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>
<label for="languages_select">Languages Profile: </label>
<select class="selectpicker" id="languages_select" name="languages"></select>
</div>
</div>
<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>'
}
},
{data: "audio_language.name"},
{
data: "languages",
data: 'audio_language',
render: function (data) {
if (data && data !== 'None') {
var languages = '';
data.forEach(appendFunc);
return languages;
} else {
return null;
}
var audio_languages = '';
data.forEach(appendFunc);
return audio_languages;
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: "forced", className: "dt-center"}
{data: "profileId.name", 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) {
e.preventDefault();
const values = {
seriesid: table.rows({selected: true}).ids().toArray(),
languages: $('#languages_select').val(),
hi: $('#hi_select').val(),
forced: $('#forced_select').val()
languages: $('#languages_select').val()
};
$.ajax({
@ -202,9 +167,7 @@
},
success: function () {
table.rows().deselect();
$('#languages_select').selectpicker('val', '');
$('#hi_select').selectpicker('val', '');
$('#forced_select').selectpicker('val', '');
$('#languages_select').selectpicker('val', 'None');
}
});
});
@ -212,11 +175,11 @@
function getEnabledLanguages() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=true",
url: "{{ url_for('api.languagesprofiles') }}",
success: function (data) {
$('#languages_select').append('<option value="None">None</option>');
$.each(data, function (i, item) {
$('#languages_select').append('<option value="' + item.code2 + '">' + item.name + '</option>');
$.each(data['data'], function (i, item) {
$('#languages_select').append('<option value="' + item.profileId + '">' + item.name + '</option>');
});
$("#languages_select").selectpicker("refresh");
}

View File

@ -7,6 +7,10 @@
.warning {
color: red;
}
table.dataTable tbody tr.selected a, table.dataTable tbody th.selected a, table.dataTable tbody td.selected a {
color: revert;
}
</style>
{% endblock page_head %}
@ -31,7 +35,7 @@
{% block body %}
<div class="container-fluid" style="padding-top: 3em;">
<form class="form" name="settings_form" id="settings_form">
<h4>Subtitles languages</h4>
<h4>Subtitles Languages</h4>
<hr/>
<div class="row">
<div class="col-sm-3 text-right">
@ -49,7 +53,7 @@
<br>
<div class="row">
<div class="col-sm-3 text-right">
<b>Enabled Languages</b>
<b>Languages Filter</b>
</div>
<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>
@ -57,6 +61,24 @@
</div>
<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>
<hr>
<div class="row">
@ -74,38 +96,13 @@
<div id="series_default_div">
<div class="row">
<div class="col-sm-4 text-right">
<b>Languages</b>
<b>Languages Profile Id</b>
</div>
<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 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>
<br>
<div class="row">
<div class="col-sm-3 text-right">
<b>Movies Default Settings</b>
@ -121,39 +118,140 @@
<div id="movies_default_div">
<div class="row">
<div class="col-sm-4 text-right">
<b>Languages</b>
<b>Languages Profile Id</b>
</div>
<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 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>
</form>
</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 %}
{% block tail %}
@ -173,57 +271,324 @@
$('#save_button_checkmark').hide();
$('#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
$('#settings-general-serie_default_forced').val('{{settings.general.serie_default_forced}}').trigger('change');
$('#settings-general-movie_default_forced').val('{{settings.general.movie_default_forced}}').trigger('change');
$('#settings-general-serie_default_profile').val('{{settings.general.serie_default_profile}}').trigger('change');
$('#settings-general-movie_default_profile').val('{{settings.general.movie_default_profile}}').trigger('change');
$('.selectpicker').selectpicker('refresh')
// Listen to single language checkbox change to adapt languages menus
$('#settings-general-single_language').on('change', function() {
if ($(this).prop('checked')) {
$('#settings-general-serie_default_language').selectpicker({maxOptions:1}).selectpicker('refresh');
$('#settings-general-movie_default_language').selectpicker({maxOptions:1}).selectpicker('refresh');
$('#settings-general-serie_default_language').selectpicker('val', $('#settings-general-serie_default_language').val()[0]);
$('#settings-general-movie_default_language').selectpicker('val', $('#settings-general-movie_default_language').val()[0]);
} else {
$('#settings-general-serie_default_language').selectpicker({maxOptions:false}).selectpicker('refresh');
$('#settings-general-movie_default_language').selectpicker({maxOptions:false}).selectpicker('refresh');
}
// Set Checkbox input values
$('#settings-general-single_language').prop('checked', {{'true' if settings.general.getboolean('single_language') else 'false'}}).trigger('change');
$('#settings-general-serie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('serie_default_enabled') else 'false'}}).trigger('change');
$('#settings-general-movie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('movie_default_enabled') else 'false'}}).trigger('change');
var table = $('#languages_profiles').DataTable({
dom: 'Bfrtip',
select: {
style: 'single'
},
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
$('#settings-general-serie_default_enabled').on('change', function() {
if ($(this).prop('checked')) {
@ -241,28 +606,55 @@
}
});
// Set Checkbox input values
$('#settings-general-single_language').prop('checked', {{'true' if settings.general.getboolean('single_language') else 'false'}}).trigger('change');
$('#settings-general-serie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('serie_default_enabled') else 'false'}}).trigger('change');
$('#settings-general-serie_default_hi').prop('checked', {{'true' if settings.general.getboolean('serie_default_hi') else 'false'}}).trigger('change');
$('#settings-general-movie_default_enabled').prop('checked', {{'true' if settings.general.getboolean('movie_default_enabled') else 'false'}}).trigger('change');
$('#settings-general-movie_default_hi').prop('checked', {{'true' if settings.general.getboolean('movie_default_hi') else 'false'}}).trigger('change');
// Listen to profiles change and populate the default dropdowns accordingly
$('#languages_profiles').DataTable().on( 'draw', function () {
if ($("#settings-general-serie_default_profile").length > 1) {
var previousValueSerie = $("#settings-general-serie_default_profile").val();
} else {
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"));
// Make sure empty default languages select are send (bug in bootstrap-select)
if (formdata.get('settings-general-serie_default_language') == null) {
formdata.append('settings-general-serie_default_language', null)
if (formdata.get('settings-general-serie_default_profile') == null) {
formdata.append('settings-general-serie_default_profile', null)
}
if (formdata.get('settings-general-movie_default_language') == null) {
formdata.append('settings-general-movie_default_language', null)
if (formdata.get('settings-general-movie_default_profile') == null) {
formdata.append('settings-general-movie_default_profile', null)
}
// Make sure all checkbox input are sent with true/false value
$('input[type=checkbox]').each(function () {
formdata.set($(this).prop('id'), $(this).prop('checked'));
});
formdata.append('languages_profiles', JSON.stringify(table.rows().data().toArray()));
$.ajax({
url: "{{ url_for('api.savesettings') }}",
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() {
$.ajax({
url: "{{ url_for('api.languages') }}?enabled=false",
success: function (data) {
$('#enabled_languages').empty();
$.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");
}
});
}
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
setTimeout(
function()
{
$('#settings_form').on('change', function() {
form_changed = true;
$('#save_button').prop('disabled', false).css('cursor', 'auto');
})
}, 1000);
$('#settings_form').on('change', function() {
form_changed = true;
$('#save_button').prop('disabled', false).css('cursor', 'auto');
})
});
</script>
{% endblock tail %}

View File

@ -202,7 +202,7 @@
</div>
</div>
<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>
</div>
</form>
@ -242,7 +242,7 @@
</div>
</div>
<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>
</div>
</form>

View File

@ -215,7 +215,7 @@
</div>
</div>
<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>
</div>
</form>
@ -255,7 +255,7 @@
</div>
</div>
<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>
</div>
</form>

View File

@ -422,19 +422,19 @@
<label>Enable the automatic subtitles synchronization after downloading a subtitles.</label>
</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 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="col-sm-4 text-right">
<b>Subtitles synchronization score threshold for series</b>