diff --git a/bazarr/api.py b/bazarr/api.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/bazarr/api/badges/badges.py b/bazarr/api/badges/badges.py index 4d1b6ddf2..e59c28580 100644 --- a/bazarr/api/badges/badges.py +++ b/bazarr/api/badges/badges.py @@ -22,7 +22,7 @@ class Badges(Resource): missing_episodes = TableEpisodes.select(TableShows.tags, TableShows.seriesType, TableEpisodes.monitored)\ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\ + .join(TableShows)\ .where(reduce(operator.and_, episodes_conditions))\ .count() diff --git a/bazarr/api/episodes/blacklist.py b/bazarr/api/episodes/blacklist.py index 800ad4ed8..3978d6aa1 100644 --- a/bazarr/api/episodes/blacklist.py +++ b/bazarr/api/episodes/blacklist.py @@ -9,7 +9,6 @@ from flask_restful import Resource from database import TableEpisodes, TableShows, TableBlacklist from ..utils import authenticate, postprocessEpisode from utils import blacklist_log, delete_subtitles, blacklist_delete_all, blacklist_delete -from helper import path_mappings from get_subtitle import episode_download_subtitles from event_handler import event_stream @@ -26,13 +25,13 @@ class EpisodesBlacklist(Resource): data = TableBlacklist.select(TableShows.title.alias('seriesTitle'), TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias('episode_number'), TableEpisodes.title.alias('episodeTitle'), - TableEpisodes.sonarrSeriesId, + TableEpisodes.seriesId, TableBlacklist.provider, TableBlacklist.subs_id, TableBlacklist.language, TableBlacklist.timestamp)\ - .join(TableEpisodes, on=(TableBlacklist.sonarr_episode_id == TableEpisodes.sonarrEpisodeId))\ - .join(TableShows, on=(TableBlacklist.sonarr_series_id == TableShows.sonarrSeriesId))\ + .join(TableEpisodes)\ + .join(TableShows)\ .order_by(TableBlacklist.timestamp.desc())\ .limit(length)\ .offset(start)\ @@ -50,22 +49,22 @@ class EpisodesBlacklist(Resource): @authenticate def post(self): - sonarr_series_id = int(request.args.get('seriesid')) - sonarr_episode_id = int(request.args.get('episodeid')) + series_id = int(request.args.get('seriesid')) + episode_id = int(request.args.get('episodeid')) provider = request.form.get('provider') subs_id = request.form.get('subs_id') language = request.form.get('language') episodeInfo = TableEpisodes.select(TableEpisodes.path)\ - .where(TableEpisodes.sonarrEpisodeId == sonarr_episode_id)\ + .where(TableEpisodes.episodeId == episode_id)\ .dicts()\ .get() media_path = episodeInfo['path'] subtitles_path = request.form.get('subtitles_path') - blacklist_log(sonarr_series_id=sonarr_series_id, - sonarr_episode_id=sonarr_episode_id, + blacklist_log(series_id=series_id, + episode_id=episode_id, provider=provider, subs_id=subs_id, language=language) @@ -73,11 +72,11 @@ class EpisodesBlacklist(Resource): language=language, forced=False, hi=False, - media_path=path_mappings.path_replace(media_path), + media_path=media_path, subtitles_path=subtitles_path, - sonarr_series_id=sonarr_series_id, - sonarr_episode_id=sonarr_episode_id) - episode_download_subtitles(sonarr_episode_id) + series_id=series_id, + episode_id=episode_id) + episode_download_subtitles(episode_id) event_stream(type='episode-history') return '', 200 diff --git a/bazarr/api/episodes/episodes.py b/bazarr/api/episodes/episodes.py index 1ce718c48..ec3499ed0 100644 --- a/bazarr/api/episodes/episodes.py +++ b/bazarr/api/episodes/episodes.py @@ -5,6 +5,7 @@ from flask_restful import Resource from database import TableEpisodes from ..utils import authenticate, postprocessEpisode +from event_handler import event_stream class Episodes(Resource): @@ -14,10 +15,10 @@ class Episodes(Resource): episodeId = request.args.getlist('episodeid[]') if len(episodeId) > 0: - result = TableEpisodes.select().where(TableEpisodes.sonarrEpisodeId.in_(episodeId)).dicts() + result = TableEpisodes.select().where(TableEpisodes.episodeId.in_(episodeId)).dicts() elif len(seriesId) > 0: result = TableEpisodes.select()\ - .where(TableEpisodes.sonarrSeriesId.in_(seriesId))\ + .where(TableEpisodes.seriesId.in_(seriesId))\ .order_by(TableEpisodes.season.desc(), TableEpisodes.episode.desc())\ .dicts() else: @@ -28,3 +29,29 @@ class Episodes(Resource): postprocessEpisode(item) return jsonify(data=result) + + @authenticate + def patch(self): + episodeid = request.form.get('episodeid') + action = request.form.get('action') + value = request.form.get('value') + if action == "monitored": + if value == 'false': + new_monitored_value = 'True' + else: + new_monitored_value = 'False' + + # update episode monitored status + TableEpisodes.update({ + TableEpisodes.monitored: new_monitored_value + }) \ + .where(TableEpisodes.episodeId == episodeid) \ + .execute() + + event_stream(type='episode', payload=episodeid) + event_stream(type='badges') + event_stream(type='episode-wanted', payload=episodeid) + + return '', 204 + + return '', 400 diff --git a/bazarr/api/episodes/episodes_subtitles.py b/bazarr/api/episodes/episodes_subtitles.py index 86f8b0024..a4f5fefdc 100644 --- a/bazarr/api/episodes/episodes_subtitles.py +++ b/bazarr/api/episodes/episodes_subtitles.py @@ -8,7 +8,6 @@ from subliminal_patch.core import SUBTITLE_EXTENSIONS from database import TableEpisodes, get_audio_profile_languages from ..utils import authenticate -from helper import path_mappings from get_providers import get_providers, get_providers_auth from get_subtitle import download_subtitle, manual_upload_subtitle from utils import history_log, delete_subtitles @@ -24,21 +23,17 @@ from config import settings class EpisodesSubtitles(Resource): @authenticate def patch(self): - sonarrSeriesId = request.args.get('seriesid') - sonarrEpisodeId = request.args.get('episodeid') + seriesId = request.args.get('seriesid') + episodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, - TableEpisodes.scene_name, TableEpisodes.audio_language)\ - .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\ + .where(TableEpisodes.episodeId == episodeId)\ .dicts()\ .get() title = episodeInfo['title'] - episodePath = path_mappings.path_replace(episodeInfo['path']) - sceneName = episodeInfo['scene_name'] - audio_language = episodeInfo['audio_language'] - if sceneName is None: sceneName = "None" + episodePath = episodeInfo['path'] language = request.form.get('language') hi = request.form.get('hi').capitalize() @@ -47,7 +42,7 @@ class EpisodesSubtitles(Resource): providers_list = get_providers() providers_auth = get_providers_auth() - audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId) + audio_language_list = get_audio_profile_languages(episode_id=episodeId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: @@ -55,7 +50,7 @@ class EpisodesSubtitles(Resource): try: result = download_subtitle(episodePath, language, audio_language, hi, forced, providers_list, - providers_auth, sceneName, title, 'series') + providers_auth, title, 'series') if result is not None: message = result[0] path = result[1] @@ -70,12 +65,12 @@ class EpisodesSubtitles(Resource): score = result[4] subs_id = result[6] subs_path = result[7] - history_log(1, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id, + history_log(1, seriesId, episodeId, message, path, language_code, provider, score, subs_id, subs_path) - send_notifications(sonarrSeriesId, sonarrEpisodeId, message) - store_subtitles(path, episodePath) + send_notifications(seriesId, episodeId, message) + store_subtitles(episodePath) else: - event_stream(type='episode', payload=sonarrEpisodeId) + event_stream(type='episode', payload=episodeId) except OSError: pass @@ -84,21 +79,18 @@ class EpisodesSubtitles(Resource): @authenticate def post(self): - sonarrSeriesId = request.args.get('seriesid') - sonarrEpisodeId = request.args.get('episodeid') + seriesId = request.args.get('seriesid') + episodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, - TableEpisodes.scene_name, TableEpisodes.audio_language)\ - .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\ + .where(TableEpisodes.episodeId == episodeId)\ .dicts()\ .get() title = episodeInfo['title'] - episodePath = path_mappings.path_replace(episodeInfo['path']) - sceneName = episodeInfo['scene_name'] + episodePath = episodeInfo['path'] audio_language = episodeInfo['audio_language'] - if sceneName is None: sceneName = "None" language = request.form.get('language') forced = True if request.form.get('forced') == 'true' else False @@ -116,7 +108,6 @@ class EpisodesSubtitles(Resource): forced=forced, hi=hi, title=title, - scene_name=sceneName, media_type='series', subtitle=subFile, audio_language=audio_language) @@ -133,11 +124,11 @@ class EpisodesSubtitles(Resource): language_code = language provider = "manual" score = 360 - history_log(4, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, + history_log(4, seriesId, episodeId, message, path, language_code, provider, score, subtitles_path=subs_path) if not settings.general.getboolean('dont_notify_manual_actions'): - send_notifications(sonarrSeriesId, sonarrEpisodeId, message) - store_subtitles(path, episodePath) + send_notifications(seriesId, episodeId, message) + store_subtitles(episodePath) except OSError: pass @@ -146,32 +137,29 @@ class EpisodesSubtitles(Resource): @authenticate def delete(self): - sonarrSeriesId = request.args.get('seriesid') - sonarrEpisodeId = request.args.get('episodeid') + seriesId = request.args.get('seriesid') + episodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, - TableEpisodes.scene_name, TableEpisodes.audio_language)\ - .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)\ + .where(TableEpisodes.episodeId == episodeId)\ .dicts()\ .get() - episodePath = path_mappings.path_replace(episodeInfo['path']) + episodePath = episodeInfo['path'] language = request.form.get('language') forced = request.form.get('forced') hi = request.form.get('hi') subtitlesPath = request.form.get('path') - subtitlesPath = path_mappings.path_replace_reverse(subtitlesPath) - delete_subtitles(media_type='series', language=language, forced=forced, hi=hi, media_path=episodePath, subtitles_path=subtitlesPath, - sonarr_series_id=sonarrSeriesId, - sonarr_episode_id=sonarrEpisodeId) + series_id=seriesId, + episode_id=episodeId) return '', 204 diff --git a/bazarr/api/episodes/history.py b/bazarr/api/episodes/history.py index c3b4cab7c..1bb493aa5 100644 --- a/bazarr/api/episodes/history.py +++ b/bazarr/api/episodes/history.py @@ -14,7 +14,6 @@ from datetime import timedelta from database import get_exclusion_clause, TableEpisodes, TableShows, TableHistory, TableBlacklist from ..utils import authenticate, postprocessEpisode from config import settings -from helper import path_mappings class EpisodesHistory(Resource): @@ -27,7 +26,7 @@ class EpisodesHistory(Resource): upgradable_episodes_not_perfect = [] if settings.general.getboolean('upgrade_subs'): days_to_upgrade_subs = settings.general.days_to_upgrade_subs - minimum_timestamp = ((datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs))) - + minimum_timestamp = ((datetime.datetime.now() - datetime.timedelta(days=int(days_to_upgrade_subs))) - datetime.datetime(1970, 1, 1)).total_seconds() if settings.general.getboolean('upgrade_manual'): @@ -45,8 +44,8 @@ class EpisodesHistory(Resource): TableShows.tags, TableEpisodes.monitored, TableShows.seriesType)\ - .join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\ - .join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\ + .join(TableEpisodes)\ + .join(TableShows)\ .where(reduce(operator.and_, upgradable_episodes_conditions))\ .group_by(TableHistory.video_path)\ .dicts() @@ -63,7 +62,7 @@ class EpisodesHistory(Resource): query_conditions = [(TableEpisodes.title is not None)] if episodeid: - query_conditions.append((TableEpisodes.sonarrEpisodeId == episodeid)) + query_conditions.append((TableEpisodes.episodeId == episodeid)) query_condition = reduce(operator.and_, query_conditions) episode_history = TableHistory.select(TableHistory.id, TableShows.title.alias('seriesTitle'), @@ -73,18 +72,18 @@ class EpisodesHistory(Resource): TableHistory.timestamp, TableHistory.subs_id, TableHistory.description, - TableHistory.sonarrSeriesId, + TableHistory.seriesId, TableEpisodes.path, TableHistory.language, TableHistory.score, TableShows.tags, TableHistory.action, TableHistory.subtitles_path, - TableHistory.sonarrEpisodeId, + TableHistory.episodeId, TableHistory.provider, TableShows.seriesType)\ - .join(TableShows, on=(TableHistory.sonarrSeriesId == TableShows.sonarrSeriesId))\ - .join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\ + .join(TableShows)\ + .join(TableEpisodes)\ .where(query_condition)\ .order_by(TableHistory.timestamp.desc())\ .limit(length)\ @@ -98,13 +97,18 @@ class EpisodesHistory(Resource): for item in episode_history: # Mark episode as upgradable or not item.update({"upgradable": False}) - 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: - if os.path.isfile(path_mappings.path_replace(item['subtitles_path'])): + 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: + if os.path.isfile(item["subtitles_path"]): item.update({"upgradable": True}) - del item['path'] + del item["path"] postprocessEpisode(item) @@ -127,7 +131,7 @@ class EpisodesHistory(Resource): break count = TableHistory.select()\ - .join(TableEpisodes, on=(TableHistory.sonarrEpisodeId == TableEpisodes.sonarrEpisodeId))\ + .join(TableEpisodes)\ .where(TableEpisodes.title is not None).count() return jsonify(data=episode_history, total=count) diff --git a/bazarr/api/episodes/wanted.py b/bazarr/api/episodes/wanted.py index 3634a69b3..998b99e1a 100644 --- a/bazarr/api/episodes/wanted.py +++ b/bazarr/api/episodes/wanted.py @@ -18,7 +18,7 @@ class EpisodesWanted(Resource): wanted_conditions = [(TableEpisodes.missing_subtitles != '[]')] if len(episodeid) > 0: - wanted_conditions.append((TableEpisodes.sonarrEpisodeId in episodeid)) + wanted_conditions.append((TableEpisodes.episodeId in episodeid)) wanted_conditions += get_exclusion_clause('series') wanted_condition = reduce(operator.and_, wanted_conditions) @@ -28,13 +28,12 @@ class EpisodesWanted(Resource): TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias('episode_number'), TableEpisodes.title.alias('episodeTitle'), TableEpisodes.missing_subtitles, - TableEpisodes.sonarrSeriesId, - TableEpisodes.sonarrEpisodeId, - TableEpisodes.scene_name.alias('sceneName'), + TableEpisodes.seriesId, + TableEpisodes.episodeId, TableShows.tags, TableEpisodes.failedAttempts, TableShows.seriesType)\ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\ + .join(TableShows)\ .where(wanted_condition)\ .dicts() else: @@ -45,15 +44,14 @@ class EpisodesWanted(Resource): TableEpisodes.season.concat('x').concat(TableEpisodes.episode).alias('episode_number'), TableEpisodes.title.alias('episodeTitle'), TableEpisodes.missing_subtitles, - TableEpisodes.sonarrSeriesId, - TableEpisodes.sonarrEpisodeId, - TableEpisodes.scene_name.alias('sceneName'), + TableEpisodes.seriesId, + TableEpisodes.episodeId, TableShows.tags, TableEpisodes.failedAttempts, TableShows.seriesType)\ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\ + .join(TableShows)\ .where(wanted_condition)\ - .order_by(TableEpisodes.rowid.desc())\ + .order_by(TableEpisodes.episodeId.desc())\ .limit(length)\ .offset(start)\ .dicts() @@ -67,7 +65,7 @@ class EpisodesWanted(Resource): count = TableEpisodes.select(TableShows.tags, TableShows.seriesType, TableEpisodes.monitored)\ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\ + .join(TableShows)\ .where(reduce(operator.and_, count_conditions))\ .count() diff --git a/bazarr/api/files/__init__.py b/bazarr/api/files/__init__.py index 7c78565c9..db1ff9d73 100644 --- a/bazarr/api/files/__init__.py +++ b/bazarr/api/files/__init__.py @@ -4,13 +4,9 @@ from flask import Blueprint from flask_restful import Api from .files import BrowseBazarrFS -from .files_sonarr import BrowseSonarrFS -from .files_radarr import BrowseRadarrFS api_bp_files = Blueprint('api_files', __name__) api = Api(api_bp_files) api.add_resource(BrowseBazarrFS, '/files') -api.add_resource(BrowseSonarrFS, '/files/sonarr') -api.add_resource(BrowseRadarrFS, '/files/radarr') diff --git a/bazarr/api/files/files_radarr.py b/bazarr/api/files/files_radarr.py deleted file mode 100644 index 156d73d33..000000000 --- a/bazarr/api/files/files_radarr.py +++ /dev/null @@ -1,24 +0,0 @@ -# coding=utf-8 - -from flask import request, jsonify -from flask_restful import Resource - -from filesystem import browse_radarr_filesystem - -from ..utils import authenticate - - -class BrowseRadarrFS(Resource): - @authenticate - def get(self): - path = request.args.get('path') or '' - data = [] - try: - result = browse_radarr_filesystem(path) - if result is None: - raise ValueError - except Exception: - return jsonify([]) - for item in result['directories']: - data.append({'name': item['name'], 'children': True, 'path': item['path']}) - return jsonify(data) diff --git a/bazarr/api/files/files_sonarr.py b/bazarr/api/files/files_sonarr.py deleted file mode 100644 index 0007157dc..000000000 --- a/bazarr/api/files/files_sonarr.py +++ /dev/null @@ -1,24 +0,0 @@ -# coding=utf-8 - -from flask import request, jsonify -from flask_restful import Resource - -from filesystem import browse_sonarr_filesystem - -from ..utils import authenticate - - -class BrowseSonarrFS(Resource): - @authenticate - def get(self): - path = request.args.get('path') or '' - data = [] - try: - result = browse_sonarr_filesystem(path) - if result is None: - raise ValueError - except Exception: - return jsonify([]) - for item in result['directories']: - data.append({'name': item['name'], 'children': True, 'path': item['path']}) - return jsonify(data) diff --git a/bazarr/api/history/stats.py b/bazarr/api/history/stats.py index b9651399d..bc7691d57 100644 --- a/bazarr/api/history/stats.py +++ b/bazarr/api/history/stats.py @@ -71,8 +71,7 @@ class HistoryStats(Resource): .dicts() data_movies = list(data_movies) - for dt in rrule.rrule(rrule.DAILY, - dtstart=datetime.datetime.now() - datetime.timedelta(seconds=delay), + for dt in rrule.rrule(rrule.DAILY, dtstart=datetime.datetime.now() - datetime.timedelta(seconds=delay), until=datetime.datetime.now()): if not any(d['date'] == dt.strftime('%Y-%m-%d') for d in data_series): data_series.append({'date': dt.strftime('%Y-%m-%d'), 'count': 0}) diff --git a/bazarr/api/movies/__init__.py b/bazarr/api/movies/__init__.py index eb691f529..1968f83c4 100644 --- a/bazarr/api/movies/__init__.py +++ b/bazarr/api/movies/__init__.py @@ -8,6 +8,11 @@ from .movies_subtitles import MoviesSubtitles from .history import MoviesHistory from .wanted import MoviesWanted from .blacklist import MoviesBlacklist +from .rootfolders import MoviesRootfolders +from .directories import MoviesDirectories +from .lookup import MoviesLookup +from .add import MoviesAdd +from .modify import MoviesModify api_bp_movies = Blueprint('api_movies', __name__) @@ -18,3 +23,8 @@ api.add_resource(MoviesWanted, '/movies/wanted') api.add_resource(MoviesSubtitles, '/movies/subtitles') api.add_resource(MoviesHistory, '/movies/history') api.add_resource(MoviesBlacklist, '/movies/blacklist') +api.add_resource(MoviesRootfolders, '/movies/rootfolders') +api.add_resource(MoviesDirectories, '/movies/directories') +api.add_resource(MoviesLookup, '/movies/lookup') +api.add_resource(MoviesAdd, '/movies/add') +api.add_resource(MoviesModify, '/movies/modify') diff --git a/bazarr/api/movies/add.py b/bazarr/api/movies/add.py new file mode 100644 index 000000000..d2caeb4eb --- /dev/null +++ b/bazarr/api/movies/add.py @@ -0,0 +1,27 @@ +# coding=utf-8 + +from flask import request +from flask_restful import Resource + +from ..utils import authenticate +from indexer.movies.local.movies_indexer import get_movies_metadata +from database import TableMovies +from list_subtitles import store_subtitles_movie + + +class MoviesAdd(Resource): + @authenticate + def post(self): + # add a new movie to database + tmdbId = request.args.get('tmdbid') + rootdir_id = request.args.get('rootdir_id') + directory = request.args.get('directory') + movies_metadata = get_movies_metadata(tmdbid=tmdbId, root_dir_id=rootdir_id, dir_name=directory) + if movies_metadata and movies_metadata['path']: + try: + result = TableMovies.insert(movies_metadata).execute() + except Exception: + pass + else: + if result: + store_subtitles_movie(movies_metadata['path']) diff --git a/bazarr/api/movies/blacklist.py b/bazarr/api/movies/blacklist.py index cda24f348..6af5cb62b 100644 --- a/bazarr/api/movies/blacklist.py +++ b/bazarr/api/movies/blacklist.py @@ -9,7 +9,6 @@ from flask_restful import Resource from database import TableMovies, TableBlacklistMovie from ..utils import authenticate, postprocessMovie from utils import blacklist_log_movie, delete_subtitles, blacklist_delete_all_movie, blacklist_delete_movie -from helper import path_mappings from get_subtitle import movies_download_subtitles from event_handler import event_stream @@ -24,12 +23,12 @@ class MoviesBlacklist(Resource): length = request.args.get('length') or -1 data = TableBlacklistMovie.select(TableMovies.title, - TableMovies.radarrId, + TableMovies.movieId, TableBlacklistMovie.provider, TableBlacklistMovie.subs_id, TableBlacklistMovie.language, TableBlacklistMovie.timestamp)\ - .join(TableMovies, on=(TableBlacklistMovie.radarr_id == TableMovies.radarrId))\ + .join(TableMovies)\ .order_by(TableBlacklistMovie.timestamp.desc())\ .limit(length)\ .offset(start)\ @@ -47,7 +46,7 @@ class MoviesBlacklist(Resource): @authenticate def post(self): - radarr_id = int(request.args.get('radarrid')) + movie_id = int(request.args.get('movieid')) provider = request.form.get('provider') subs_id = request.form.get('subs_id') language = request.form.get('language') @@ -55,12 +54,12 @@ class MoviesBlacklist(Resource): forced = False hi = False - data = TableMovies.select(TableMovies.path).where(TableMovies.radarrId == radarr_id).dicts().get() + data = TableMovies.select(TableMovies.path).where(TableMovies.movieId == movie_id).dicts().get() media_path = data['path'] subtitles_path = request.form.get('subtitles_path') - blacklist_log_movie(radarr_id=radarr_id, + blacklist_log_movie(movie_id=movie_id, provider=provider, subs_id=subs_id, language=language) @@ -68,10 +67,10 @@ class MoviesBlacklist(Resource): language=language, forced=forced, hi=hi, - media_path=path_mappings.path_replace_movie(media_path), + media_path=media_path, subtitles_path=subtitles_path, - radarr_id=radarr_id) - movies_download_subtitles(radarr_id) + movie_id=movie_id) + movies_download_subtitles(movie_id) event_stream(type='movie-history') return '', 200 diff --git a/bazarr/api/movies/directories.py b/bazarr/api/movies/directories.py new file mode 100644 index 000000000..79aad2abb --- /dev/null +++ b/bazarr/api/movies/directories.py @@ -0,0 +1,15 @@ +# coding=utf-8 + +from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate +from indexer.movies.local.movies_indexer import list_movies_directories + + +class MoviesDirectories(Resource): + @authenticate + def get(self): + # list movies directories inside a specific root folder + root_folder_id = request.args.get('id') + return jsonify(data=list_movies_directories(root_dir=root_folder_id)) diff --git a/bazarr/api/movies/history.py b/bazarr/api/movies/history.py index 13e3c6d88..270a2277e 100644 --- a/bazarr/api/movies/history.py +++ b/bazarr/api/movies/history.py @@ -14,7 +14,6 @@ from datetime import timedelta from database import get_exclusion_clause, TableMovies, TableHistoryMovie, TableBlacklistMovie from ..utils import authenticate, postprocessMovie from config import settings -from helper import path_mappings class MoviesHistory(Resource): @@ -22,13 +21,13 @@ class MoviesHistory(Resource): def get(self): start = request.args.get('start') or 0 length = request.args.get('length') or -1 - radarrid = request.args.get('radarrid') + movieid = request.args.get('movieid') upgradable_movies = [] upgradable_movies_not_perfect = [] if settings.general.getboolean('upgrade_subs'): days_to_upgrade_subs = settings.general.days_to_upgrade_subs - minimum_timestamp = ((datetime.datetime.now() - timedelta(days=int(days_to_upgrade_subs))) - + minimum_timestamp = ((datetime.datetime.now() - datetime.timedelta(days=int(days_to_upgrade_subs))) - datetime.datetime(1970, 1, 1)).total_seconds() if settings.general.getboolean('upgrade_manual'): @@ -45,7 +44,7 @@ class MoviesHistory(Resource): TableHistoryMovie.score, TableMovies.tags, TableMovies.monitored)\ - .join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\ + .join(TableMovies)\ .where(reduce(operator.and_, upgradable_movies_conditions))\ .group_by(TableHistoryMovie.video_path)\ .dicts() @@ -62,8 +61,8 @@ class MoviesHistory(Resource): upgradable_movies_not_perfect.append(upgradable_movie) query_conditions = [(TableMovies.title is not None)] - if radarrid: - query_conditions.append((TableMovies.radarrId == radarrid)) + if movieid: + query_conditions.append((TableMovies.movieId == movieid)) query_condition = reduce(operator.and_, query_conditions) movie_history = TableHistoryMovie.select(TableHistoryMovie.id, @@ -71,7 +70,7 @@ class MoviesHistory(Resource): TableMovies.title, TableHistoryMovie.timestamp, TableHistoryMovie.description, - TableHistoryMovie.radarrId, + TableHistoryMovie.movieId, TableMovies.monitored, TableHistoryMovie.video_path.alias('path'), TableHistoryMovie.language, @@ -80,7 +79,7 @@ class MoviesHistory(Resource): TableHistoryMovie.subs_id, TableHistoryMovie.provider, TableHistoryMovie.subtitles_path)\ - .join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\ + .join(TableMovies)\ .where(query_condition)\ .order_by(TableHistoryMovie.timestamp.desc())\ .limit(length)\ @@ -94,12 +93,17 @@ class MoviesHistory(Resource): for item in movie_history: # Mark movies as upgradable or not item.update({"upgradable": False}) - if {"video_path": str(item['path']), "timestamp": float(item['timestamp']), "score": str(item['score']), - "tags": str(item['tags']), "monitored": str(item['monitored'])} in upgradable_movies_not_perfect: - if os.path.isfile(path_mappings.path_replace_movie(item['subtitles_path'])): + if { + "video_path": str(item["path"]), + "timestamp": float(item["timestamp"]), + "score": str(item["score"]), + "tags": str(item["tags"]), + "monitored": str(item["monitored"]), + } in upgradable_movies_not_perfect: + if os.path.isfile(item["subtitles_path"]): item.update({"upgradable": True}) - del item['path'] + del item["path"] postprocessMovie(item) @@ -116,13 +120,13 @@ class MoviesHistory(Resource): item.update({"blacklisted": False}) if item['action'] not in [0, 4, 5]: for blacklisted_item in blacklist_db: - if blacklisted_item['provider'] == item['provider'] and blacklisted_item['subs_id'] == item[ - 'subs_id']: + if blacklisted_item['provider'] == item['provider'] and \ + blacklisted_item['subs_id'] == item['subs_id']: item.update({"blacklisted": True}) break count = TableHistoryMovie.select()\ - .join(TableMovies, on=(TableHistoryMovie.radarrId == TableMovies.radarrId))\ + .join(TableMovies)\ .where(TableMovies.title is not None)\ .count() diff --git a/bazarr/api/movies/lookup.py b/bazarr/api/movies/lookup.py new file mode 100644 index 000000000..67fd6d5d7 --- /dev/null +++ b/bazarr/api/movies/lookup.py @@ -0,0 +1,16 @@ +# coding=utf-8 + +from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate +from indexer.movies.local.movies_indexer import get_movies_match + + +class MoviesLookup(Resource): + @authenticate + def get(self): + # return possible matches from TMDB for a specific movie directory + dir_name = request.args.get('dir_name') + matches = get_movies_match(directory=dir_name) + return jsonify(data=matches) diff --git a/bazarr/api/movies/modify.py b/bazarr/api/movies/modify.py new file mode 100644 index 000000000..ee832adef --- /dev/null +++ b/bazarr/api/movies/modify.py @@ -0,0 +1,13 @@ +# coding=utf-8 + +# from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate + + +class MoviesModify(Resource): + @authenticate + def patch(self): + # modify an existing movie in database + pass diff --git a/bazarr/api/movies/movies.py b/bazarr/api/movies/movies.py index d088a71a5..d047154f7 100644 --- a/bazarr/api/movies/movies.py +++ b/bazarr/api/movies/movies.py @@ -15,13 +15,13 @@ class Movies(Resource): def get(self): start = request.args.get('start') or 0 length = request.args.get('length') or -1 - radarrId = request.args.getlist('radarrid[]') + movieId = request.args.getlist('movieid[]') count = TableMovies.select().count() - if len(radarrId) != 0: + if len(movieId) != 0: result = TableMovies.select()\ - .where(TableMovies.radarrId.in_(radarrId))\ + .where(TableMovies.movieId.in_(movieId))\ .order_by(TableMovies.sortTitle)\ .dicts() else: @@ -34,12 +34,14 @@ class Movies(Resource): @authenticate def post(self): - radarrIdList = request.form.getlist('radarrid') + movieIdList = request.form.getlist('movieid') profileIdList = request.form.getlist('profileid') + monitoredList = request.form.getlist('monitored') - for idx in range(len(radarrIdList)): - radarrId = radarrIdList[idx] + for idx in range(len(movieIdList)): + movieId = movieIdList[idx] profileId = profileIdList[idx] + monitored = monitoredList[idx] if profileId in None_Keys: profileId = None @@ -50,31 +52,56 @@ class Movies(Resource): return '', 400 TableMovies.update({ - TableMovies.profileId: profileId + TableMovies.profileId: profileId, + TableMovies.monitored: monitored })\ - .where(TableMovies.radarrId == radarrId)\ + .where(TableMovies.movieId == movieId)\ .execute() - list_missing_subtitles_movies(no=radarrId, send_event=False) + list_missing_subtitles_movies(no=movieId, send_event=False) - event_stream(type='movie', payload=radarrId) - event_stream(type='movie-wanted', payload=radarrId) + event_stream(type='movie', payload=movieId) + event_stream(type='movie-wanted', payload=movieId) event_stream(type='badges') return '', 204 @authenticate def patch(self): - radarrid = request.form.get('radarrid') + movieid = request.form.get('movieid') action = request.form.get('action') - if action == "scan-disk": - movies_scan_subtitles(radarrid) + value = request.form.get('value') + tmdbid = request.form.get('tmdbid') + if tmdbid: + TableMovies.update({TableMovies.tmdbId: tmdbid}).where(TableMovies.movieId == movieid).execute() + event_stream(type='movie', payload=movieid) + movies_scan_subtitles(movieid) + return '', 204 + elif action == "refresh": + movies_scan_subtitles(movieid) return '', 204 elif action == "search-missing": - movies_download_subtitles(radarrid) + movies_download_subtitles(movieid) return '', 204 elif action == "search-wanted": wanted_search_missing_subtitles_movies() return '', 204 + elif action == "monitored": + if value == 'false': + new_monitored_value = 'True' + else: + new_monitored_value = 'False' + + TableMovies.update({ + TableMovies.monitored: new_monitored_value + }) \ + .where(TableMovies.movieId == movieid) \ + .execute() + + event_stream(type='movie', payload=movieid) + event_stream(type='badges') + event_stream(type='movie-wanted') + + return '', 204 return '', 400 diff --git a/bazarr/api/movies/movies_subtitles.py b/bazarr/api/movies/movies_subtitles.py index cc2a07e75..5dcc3f7a0 100644 --- a/bazarr/api/movies/movies_subtitles.py +++ b/bazarr/api/movies/movies_subtitles.py @@ -8,7 +8,6 @@ from subliminal_patch.core import SUBTITLE_EXTENSIONS from database import TableMovies, get_audio_profile_languages from ..utils import authenticate -from helper import path_mappings from get_providers import get_providers, get_providers_auth from get_subtitle import download_subtitle, manual_upload_subtitle from utils import history_log_movie, delete_subtitles @@ -25,19 +24,16 @@ class MoviesSubtitles(Resource): @authenticate def patch(self): # Download - radarrId = request.args.get('radarrid') + movieId = request.args.get('movieid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, - TableMovies.sceneName, TableMovies.audio_language)\ - .where(TableMovies.radarrId == radarrId)\ + .where(TableMovies.movieId == movieId)\ .dicts()\ .get() - moviePath = path_mappings.path_replace_movie(movieInfo['path']) - sceneName = movieInfo['sceneName'] - if sceneName is None: sceneName = 'None' + moviePath = movieInfo['path'] title = movieInfo['title'] audio_language = movieInfo['audio_language'] @@ -49,7 +45,7 @@ class MoviesSubtitles(Resource): providers_list = get_providers() providers_auth = get_providers_auth() - audio_language_list = get_audio_profile_languages(movie_id=radarrId) + audio_language_list = get_audio_profile_languages(movie_id=movieId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: @@ -57,7 +53,7 @@ class MoviesSubtitles(Resource): try: result = download_subtitle(moviePath, language, audio_language, hi, forced, providers_list, - providers_auth, sceneName, title, 'movie') + providers_auth, title, 'movie') if result is not None: message = result[0] path = result[1] @@ -72,11 +68,11 @@ class MoviesSubtitles(Resource): score = result[4] subs_id = result[6] subs_path = result[7] - history_log_movie(1, radarrId, message, path, language_code, provider, score, subs_id, subs_path) - send_notifications_movie(radarrId, message) - store_subtitles_movie(path, moviePath) + history_log_movie(1, movieId, message, path, language_code, provider, score, subs_id, subs_path) + send_notifications_movie(movieId, message) + store_subtitles_movie(moviePath) else: - event_stream(type='movie', payload=radarrId) + event_stream(type='movie', payload=movieId) except OSError: pass @@ -86,18 +82,15 @@ class MoviesSubtitles(Resource): def post(self): # Upload # TODO: Support Multiply Upload - radarrId = request.args.get('radarrid') + movieId = request.args.get('movieid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, - TableMovies.sceneName, TableMovies.audio_language) \ - .where(TableMovies.radarrId == radarrId) \ + .where(TableMovies.movieId == movieId) \ .dicts() \ .get() - moviePath = path_mappings.path_replace_movie(movieInfo['path']) - sceneName = movieInfo['sceneName'] - if sceneName is None: sceneName = 'None' + moviePath = movieInfo['path'] title = movieInfo['title'] audioLanguage = movieInfo['audio_language'] @@ -118,7 +111,6 @@ class MoviesSubtitles(Resource): forced=forced, hi=hi, title=title, - scene_name=sceneName, media_type='movie', subtitle=subFile, audio_language=audioLanguage) @@ -135,10 +127,10 @@ class MoviesSubtitles(Resource): language_code = language provider = "manual" score = 120 - history_log_movie(4, radarrId, message, path, language_code, provider, score, subtitles_path=subs_path) + history_log_movie(4, movieId, message, path, language_code, provider, score, subtitles_path=subs_path) if not settings.general.getboolean('dont_notify_manual_actions'): - send_notifications_movie(radarrId, message) - store_subtitles_movie(path, moviePath) + send_notifications_movie(movieId, message) + store_subtitles_movie(moviePath) except OSError: pass @@ -147,28 +139,26 @@ class MoviesSubtitles(Resource): @authenticate def delete(self): # Delete - radarrId = request.args.get('radarrid') + movieId = request.args.get('movieid') movieInfo = TableMovies.select(TableMovies.path) \ - .where(TableMovies.radarrId == radarrId) \ + .where(TableMovies.movieId == movieId) \ .dicts() \ .get() - moviePath = path_mappings.path_replace_movie(movieInfo['path']) + moviePath = movieInfo['path'] language = request.form.get('language') forced = request.form.get('forced') hi = request.form.get('hi') subtitlesPath = request.form.get('path') - subtitlesPath = path_mappings.path_replace_reverse_movie(subtitlesPath) - result = delete_subtitles(media_type='movie', language=language, forced=forced, hi=hi, media_path=moviePath, subtitles_path=subtitlesPath, - radarr_id=radarrId) + movie_id=movieId) if result: return '', 202 else: diff --git a/bazarr/api/movies/rootfolders.py b/bazarr/api/movies/rootfolders.py new file mode 100644 index 000000000..2fcce3f89 --- /dev/null +++ b/bazarr/api/movies/rootfolders.py @@ -0,0 +1,27 @@ +# coding=utf-8 + +from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate +from database import TableMoviesRootfolder + + +class MoviesRootfolders(Resource): + @authenticate + def get(self): + # list existing movies root folders + root_folders = TableMoviesRootfolder.select().dicts() + root_folders = list(root_folders) + return jsonify(data=root_folders) + + @authenticate + def post(self): + # add a new movies root folder + path = request.form.get('path') + result = TableMoviesRootfolder.insert({ + TableMoviesRootfolder.path: path, + TableMoviesRootfolder.accessible: 1, # TODO: test it instead of assuming it's accessible + TableMoviesRootfolder.error: '' + }).execute() + return jsonify(data=list(TableMoviesRootfolder.select().where(TableMoviesRootfolder.rootId == result).dicts())) diff --git a/bazarr/api/movies/wanted.py b/bazarr/api/movies/wanted.py index 45511f0ab..8129c65ef 100644 --- a/bazarr/api/movies/wanted.py +++ b/bazarr/api/movies/wanted.py @@ -14,19 +14,18 @@ from ..utils import authenticate, postprocessMovie class MoviesWanted(Resource): @authenticate def get(self): - radarrid = request.args.getlist("radarrid[]") + movieid = request.args.getlist("movieid[]") wanted_conditions = [(TableMovies.missing_subtitles != '[]')] - if len(radarrid) > 0: - wanted_conditions.append((TableMovies.radarrId.in_(radarrid))) + if len(movieid) > 0: + wanted_conditions.append((TableMovies.movieId.in_(movieid))) wanted_conditions += get_exclusion_clause('movie') wanted_condition = reduce(operator.and_, wanted_conditions) - if len(radarrid) > 0: + if len(movieid) > 0: result = TableMovies.select(TableMovies.title, TableMovies.missing_subtitles, - TableMovies.radarrId, - TableMovies.sceneName, + TableMovies.movieId, TableMovies.failedAttempts, TableMovies.tags, TableMovies.monitored)\ @@ -37,13 +36,12 @@ class MoviesWanted(Resource): length = request.args.get('length') or -1 result = TableMovies.select(TableMovies.title, TableMovies.missing_subtitles, - TableMovies.radarrId, - TableMovies.sceneName, + TableMovies.movieId, TableMovies.failedAttempts, TableMovies.tags, TableMovies.monitored)\ .where(wanted_condition)\ - .order_by(TableMovies.rowid.desc())\ + .order_by(TableMovies.movieId.desc())\ .limit(length)\ .offset(start)\ .dicts() diff --git a/bazarr/api/providers/providers.py b/bazarr/api/providers/providers.py index 0bcfb45a7..734044ecd 100644 --- a/bazarr/api/providers/providers.py +++ b/bazarr/api/providers/providers.py @@ -15,10 +15,10 @@ class Providers(Resource): history = request.args.get('history') if history and history not in False_Keys: providers = list(TableHistory.select(TableHistory.provider) - .where(TableHistory.provider != None and TableHistory.provider != "manual") + .where(TableHistory.provider is not None and TableHistory.provider != "manual") .dicts()) providers += list(TableHistoryMovie.select(TableHistoryMovie.provider) - .where(TableHistoryMovie.provider != None and TableHistoryMovie.provider != "manual") + .where(TableHistoryMovie.provider is not None and TableHistoryMovie.provider != "manual") .dicts()) providers_list = list(set([x['provider'] for x in providers])) providers_dicts = [] diff --git a/bazarr/api/providers/providers_episodes.py b/bazarr/api/providers/providers_episodes.py index feedc0134..c69bd1641 100644 --- a/bazarr/api/providers/providers_episodes.py +++ b/bazarr/api/providers/providers_episodes.py @@ -4,7 +4,6 @@ from flask import request, jsonify from flask_restful import Resource from database import TableEpisodes, TableShows, get_audio_profile_languages -from helper import path_mappings from get_providers import get_providers, get_providers_auth from get_subtitle import manual_search, manual_download_subtitle from utils import history_log @@ -19,27 +18,23 @@ class ProviderEpisodes(Resource): @authenticate def get(self): # Manual Search - sonarrEpisodeId = request.args.get('episodeid') + episodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, TableEpisodes.path, - TableEpisodes.scene_name, TableShows.profileId) \ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\ - .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \ + .join(TableShows) \ + .where(TableEpisodes.episodeId == episodeId) \ .dicts() \ .get() title = episodeInfo['title'] - episodePath = path_mappings.path_replace(episodeInfo['path']) - sceneName = episodeInfo['scene_name'] + episodePath = episodeInfo['path'] profileId = episodeInfo['profileId'] - if sceneName is None: sceneName = "None" providers_list = get_providers() providers_auth = get_providers_auth() - data = manual_search(episodePath, profileId, providers_list, providers_auth, sceneName, title, - 'series') + data = manual_search(episodePath, profileId, providers_list, providers_auth, title, 'series') if not data: data = [] return jsonify(data=data) @@ -47,19 +42,16 @@ class ProviderEpisodes(Resource): @authenticate def post(self): # Manual Download - sonarrSeriesId = request.args.get('seriesid') - sonarrEpisodeId = request.args.get('episodeid') + seriesId = request.args.get('seriesid') + episodeId = request.args.get('episodeid') episodeInfo = TableEpisodes.select(TableEpisodes.title, - TableEpisodes.path, - TableEpisodes.scene_name) \ - .where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId) \ + TableEpisodes.path) \ + .where(TableEpisodes.episodeId == episodeId) \ .dicts() \ .get() title = episodeInfo['title'] - episodePath = path_mappings.path_replace(episodeInfo['path']) - sceneName = episodeInfo['scene_name'] - if sceneName is None: sceneName = "None" + episodePath = episodeInfo['path'] language = request.form.get('language') hi = request.form.get('hi').capitalize() @@ -68,7 +60,7 @@ class ProviderEpisodes(Resource): subtitle = request.form.get('subtitle') providers_auth = get_providers_auth() - audio_language_list = get_audio_profile_languages(episode_id=sonarrEpisodeId) + audio_language_list = get_audio_profile_languages(episode_id=episodeId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: @@ -76,7 +68,7 @@ class ProviderEpisodes(Resource): try: result = manual_download_subtitle(episodePath, language, audio_language, hi, forced, subtitle, - selected_provider, providers_auth, sceneName, title, 'series') + selected_provider, providers_auth, title, 'series') if result is not None: message = result[0] path = result[1] @@ -91,11 +83,10 @@ class ProviderEpisodes(Resource): score = result[4] subs_id = result[6] subs_path = result[7] - history_log(2, sonarrSeriesId, sonarrEpisodeId, message, path, language_code, provider, score, subs_id, - subs_path) + history_log(2, seriesId, episodeId, message, path, language_code, provider, score, subs_id, subs_path) if not settings.general.getboolean('dont_notify_manual_actions'): - send_notifications(sonarrSeriesId, sonarrEpisodeId, message) - store_subtitles(path, episodePath) + send_notifications(seriesId, episodeId, message) + store_subtitles(episodePath) return result, 201 except OSError: pass diff --git a/bazarr/api/providers/providers_movies.py b/bazarr/api/providers/providers_movies.py index c189ab2c4..975e5854d 100644 --- a/bazarr/api/providers/providers_movies.py +++ b/bazarr/api/providers/providers_movies.py @@ -4,7 +4,6 @@ from flask import request, jsonify from flask_restful import Resource from database import TableMovies, get_audio_profile_languages -from helper import path_mappings from get_providers import get_providers, get_providers_auth from get_subtitle import manual_search, manual_download_subtitle from utils import history_log_movie @@ -19,26 +18,22 @@ class ProviderMovies(Resource): @authenticate def get(self): # Manual Search - radarrId = request.args.get('radarrid') + movieId = request.args.get('movieid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, - TableMovies.sceneName, TableMovies.profileId) \ - .where(TableMovies.radarrId == radarrId) \ + .where(TableMovies.movieId == movieId) \ .dicts() \ .get() title = movieInfo['title'] - moviePath = path_mappings.path_replace_movie(movieInfo['path']) - sceneName = movieInfo['sceneName'] + moviePath = movieInfo['path'] profileId = movieInfo['profileId'] - if sceneName is None: sceneName = "None" providers_list = get_providers() providers_auth = get_providers_auth() - data = manual_search(moviePath, profileId, providers_list, providers_auth, sceneName, title, - 'movie') + data = manual_search(moviePath, profileId, providers_list, providers_auth, title, 'movie') if not data: data = [] return jsonify(data=data) @@ -46,19 +41,16 @@ class ProviderMovies(Resource): @authenticate def post(self): # Manual Download - radarrId = request.args.get('radarrid') + movieId = request.args.get('movieid') movieInfo = TableMovies.select(TableMovies.title, TableMovies.path, - TableMovies.sceneName, TableMovies.audio_language) \ - .where(TableMovies.radarrId == radarrId) \ + .where(TableMovies.movieId == movieId) \ .dicts() \ .get() title = movieInfo['title'] - moviePath = path_mappings.path_replace_movie(movieInfo['path']) - sceneName = movieInfo['sceneName'] - if sceneName is None: sceneName = "None" + moviePath = movieInfo['path'] audio_language = movieInfo['audio_language'] language = request.form.get('language') @@ -69,7 +61,7 @@ class ProviderMovies(Resource): providers_auth = get_providers_auth() - audio_language_list = get_audio_profile_languages(movie_id=radarrId) + audio_language_list = get_audio_profile_languages(movie_id=movieId) if len(audio_language_list) > 0: audio_language = audio_language_list[0]['name'] else: @@ -77,7 +69,7 @@ class ProviderMovies(Resource): try: result = manual_download_subtitle(moviePath, language, audio_language, hi, forced, subtitle, - selected_provider, providers_auth, sceneName, title, 'movie') + selected_provider, providers_auth, title, 'movie') if result is not None: message = result[0] path = result[1] @@ -92,10 +84,10 @@ class ProviderMovies(Resource): score = result[4] subs_id = result[6] subs_path = result[7] - history_log_movie(2, radarrId, message, path, language_code, provider, score, subs_id, subs_path) + history_log_movie(2, movieId, message, path, language_code, provider, score, subs_id, subs_path) if not settings.general.getboolean('dont_notify_manual_actions'): - send_notifications_movie(radarrId, message) - store_subtitles_movie(path, moviePath) + send_notifications_movie(movieId, message) + store_subtitles_movie(moviePath) except OSError: pass diff --git a/bazarr/api/series/__init__.py b/bazarr/api/series/__init__.py index 68f437ba4..8264ea0ec 100644 --- a/bazarr/api/series/__init__.py +++ b/bazarr/api/series/__init__.py @@ -4,9 +4,19 @@ from flask import Blueprint from flask_restful import Api from .series import Series +from .rootfolders import SeriesRootfolders +from .directories import SeriesDirectories +from .lookup import SeriesLookup +from .add import SeriesAdd +from .modify import SeriesModify api_bp_series = Blueprint('api_series', __name__) api = Api(api_bp_series) api.add_resource(Series, '/series') +api.add_resource(SeriesRootfolders, '/series/rootfolders') +api.add_resource(SeriesDirectories, '/series/directories') +api.add_resource(SeriesLookup, '/series/lookup') +api.add_resource(SeriesAdd, '/series/add') +api.add_resource(SeriesModify, '/series/modify') diff --git a/bazarr/api/series/add.py b/bazarr/api/series/add.py new file mode 100644 index 000000000..ee2beb7f9 --- /dev/null +++ b/bazarr/api/series/add.py @@ -0,0 +1,27 @@ +# coding=utf-8 + +from flask import request +from flask_restful import Resource + +from ..utils import authenticate +from indexer.series.local.series_indexer import get_series_metadata +from database import TableShows +from list_subtitles import store_subtitles + + +class SeriesAdd(Resource): + @authenticate + def post(self): + # add a new series to database + tmdbId = request.args.get('tmdbid') + rootdir_id = request.args.get('rootdir_id') + directory = request.args.get('directory') + series_metadata = get_series_metadata(tmdbid=tmdbId, root_dir_id=rootdir_id, dir_name=directory) + if series_metadata and series_metadata['path']: + try: + result = TableShows.insert(series_metadata).execute() + except Exception: + pass + else: + if result: + store_subtitles(series_metadata['path']) diff --git a/bazarr/api/series/directories.py b/bazarr/api/series/directories.py new file mode 100644 index 000000000..8d5bf09ba --- /dev/null +++ b/bazarr/api/series/directories.py @@ -0,0 +1,15 @@ +# coding=utf-8 + +from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate +from indexer.series.local.series_indexer import list_series_directories + + +class SeriesDirectories(Resource): + @authenticate + def get(self): + # list series directories inside a specific root folder + root_folder_id = request.args.get('id') + return jsonify(data=list_series_directories(root_dir=root_folder_id)) diff --git a/bazarr/api/series/lookup.py b/bazarr/api/series/lookup.py new file mode 100644 index 000000000..e1a61c4ef --- /dev/null +++ b/bazarr/api/series/lookup.py @@ -0,0 +1,16 @@ +# coding=utf-8 + +from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate +from indexer.series.local.series_indexer import get_series_match + + +class SeriesLookup(Resource): + @authenticate + def get(self): + # return possible matches from TMDB for a specific series directory + dir_name = request.args.get('dir_name') + matches = get_series_match(directory=dir_name) + return jsonify(data=matches) diff --git a/bazarr/api/series/modify.py b/bazarr/api/series/modify.py new file mode 100644 index 000000000..bf7db58f1 --- /dev/null +++ b/bazarr/api/series/modify.py @@ -0,0 +1,12 @@ +# coding=utf-8 + +from flask_restful import Resource + +from ..utils import authenticate + + +class SeriesModify(Resource): + @authenticate + def patch(self): + # modify an existing series in database + pass diff --git a/bazarr/api/series/rootfolders.py b/bazarr/api/series/rootfolders.py new file mode 100644 index 000000000..95408d7eb --- /dev/null +++ b/bazarr/api/series/rootfolders.py @@ -0,0 +1,27 @@ +# coding=utf-8 + +from flask import request, jsonify +from flask_restful import Resource + +from ..utils import authenticate +from database import TableShowsRootfolder + + +class SeriesRootfolders(Resource): + @authenticate + def get(self): + # list existing series root folders + root_folders = TableShowsRootfolder.select().dicts() + root_folders = list(root_folders) + return jsonify(data=root_folders) + + @authenticate + def post(self): + # add a new series root folder + path = request.form.get('path') + result = TableShowsRootfolder.insert({ + TableShowsRootfolder.path: path, + TableShowsRootfolder.accessible: 1, # TODO: test it instead of assuming it's accessible + TableShowsRootfolder.error: '' + }).execute() + return jsonify(data=list(TableShowsRootfolder.select().where(TableShowsRootfolder.rootId == result).dicts())) diff --git a/bazarr/api/series/series.py b/bazarr/api/series/series.py index 057462f11..db532727d 100644 --- a/bazarr/api/series/series.py +++ b/bazarr/api/series/series.py @@ -24,7 +24,7 @@ class Series(Resource): if len(seriesId) != 0: result = TableShows.select() \ - .where(TableShows.sonarrSeriesId.in_(seriesId)) \ + .where(TableShows.seriesId.in_(seriesId)) \ .order_by(TableShows.sortTitle).dicts() else: result = TableShows.select().order_by(TableShows.sortTitle).limit(length).offset(start).dicts() @@ -35,14 +35,14 @@ class Series(Resource): postprocessSeries(item) # Add missing subtitles episode count - episodes_missing_conditions = [(TableEpisodes.sonarrSeriesId == item['sonarrSeriesId']), + episodes_missing_conditions = [(TableEpisodes.seriesId == item['seriesId']), (TableEpisodes.missing_subtitles != '[]')] episodes_missing_conditions += get_exclusion_clause('series') episodeMissingCount = TableEpisodes.select(TableShows.tags, TableEpisodes.monitored, TableShows.seriesType) \ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \ + .join(TableShows) \ .where(reduce(operator.and_, episodes_missing_conditions)) \ .count() item.update({"episodeMissingCount": episodeMissingCount}) @@ -51,8 +51,8 @@ class Series(Resource): episodeFileCount = TableEpisodes.select(TableShows.tags, TableEpisodes.monitored, TableShows.seriesType) \ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \ - .where(TableEpisodes.sonarrSeriesId == item['sonarrSeriesId']) \ + .join(TableShows) \ + .where(TableEpisodes.seriesId == item['seriesId']) \ .count() item.update({"episodeFileCount": episodeFileCount}) @@ -62,10 +62,12 @@ class Series(Resource): def post(self): seriesIdList = request.form.getlist('seriesid') profileIdList = request.form.getlist('profileid') + monitoredList = request.form.getlist('monitored') for idx in range(len(seriesIdList)): seriesId = seriesIdList[idx] profileId = profileIdList[idx] + monitored = monitoredList[idx] if profileId in None_Keys: profileId = None @@ -76,9 +78,16 @@ class Series(Resource): return '', 400 TableShows.update({ - TableShows.profileId: profileId + TableShows.profileId: profileId, + TableShows.monitored: monitored }) \ - .where(TableShows.sonarrSeriesId == seriesId) \ + .where(TableShows.seriesId == seriesId) \ + .execute() + + TableEpisodes.update({ + TableEpisodes.monitored: monitored + }) \ + .where(TableEpisodes.seriesId == seriesId) \ .execute() list_missing_subtitles(no=seriesId, send_event=False) @@ -86,12 +95,13 @@ class Series(Resource): event_stream(type='series', payload=seriesId) episode_id_list = TableEpisodes \ - .select(TableEpisodes.sonarrEpisodeId) \ - .where(TableEpisodes.sonarrSeriesId == seriesId) \ + .select(TableEpisodes.episodeId) \ + .where(TableEpisodes.seriesId == seriesId) \ .dicts() for item in episode_id_list: - event_stream(type='episode-wanted', payload=item['sonarrEpisodeId']) + event_stream(type='episode', payload=item['episodeId']) + event_stream(type='episode-wanted', payload=item['episodeId']) event_stream(type='badges') @@ -101,7 +111,15 @@ class Series(Resource): def patch(self): seriesid = request.form.get('seriesid') action = request.form.get('action') - if action == "scan-disk": + value = request.form.get('value') + tmdbid = request.form.get('tmdbid') + + if tmdbid: + TableShows.update({TableShows.tmdbId: tmdbid}).where(TableShows.seriesId == seriesid).execute() + event_stream(type='series', payload=seriesid) + series_scan_subtitles(seriesid) + return '', 204 + elif action == "refresh": series_scan_subtitles(seriesid) return '', 204 elif action == "search-missing": @@ -110,5 +128,36 @@ class Series(Resource): elif action == "search-wanted": wanted_search_missing_subtitles_series() return '', 204 + elif action == "monitored": + if value == 'false': + new_monitored_value = 'True' + else: + new_monitored_value = 'False' + + # update series monitored status + TableShows.update({ + TableShows.monitored: new_monitored_value + }) \ + .where(TableShows.seriesId == seriesid) \ + .execute() + + event_stream(type='series', payload=seriesid) + + # update each series episode monitored status + series_episodes = TableEpisodes.select(TableEpisodes.episodeId) \ + .where(TableEpisodes.seriesId == seriesid) \ + .dicts() + + TableEpisodes.update({ + TableEpisodes.monitored: new_monitored_value + }) \ + .where(TableEpisodes.seriesId == seriesid) \ + .execute() + + for episodeid in series_episodes: + event_stream(type='badges') + event_stream(type='episode-wanted', payload=episodeid['episodeId']) + + return '', 204 return '', 400 diff --git a/bazarr/api/subtitles/subtitles.py b/bazarr/api/subtitles/subtitles.py index 7799a9679..33da23784 100644 --- a/bazarr/api/subtitles/subtitles.py +++ b/bazarr/api/subtitles/subtitles.py @@ -7,7 +7,6 @@ from flask import request from flask_restful import Resource from database import TableEpisodes, TableMovies -from helper import path_mappings from ..utils import authenticate from subsyncer import subsync from utils import translate_subtitles_file, subtitles_apply_mods @@ -26,25 +25,23 @@ class Subtitles(Resource): id = request.form.get('id') if media_type == 'episode': - subtitles_path = path_mappings.path_replace(subtitles_path) - metadata = TableEpisodes.select(TableEpisodes.path, TableEpisodes.sonarrSeriesId)\ - .where(TableEpisodes.sonarrEpisodeId == id)\ + metadata = TableEpisodes.select(TableEpisodes.path, TableEpisodes.seriesId) \ + .where(TableEpisodes.episodeId == id) \ .dicts()\ .get() - video_path = path_mappings.path_replace(metadata['path']) + video_path = metadata['path'] else: - subtitles_path = path_mappings.path_replace_movie(subtitles_path) - metadata = TableMovies.select(TableMovies.path).where(TableMovies.radarrId == id).dicts().get() - video_path = path_mappings.path_replace_movie(metadata['path']) + metadata = TableMovies.select(TableMovies.path).where(TableMovies.movieId == id).dicts().get() + video_path = metadata['path'] if action == 'sync': if media_type == 'episode': subsync.sync(video_path=video_path, srt_path=subtitles_path, - srt_lang=language, media_type='series', sonarr_series_id=metadata['sonarrSeriesId'], - sonarr_episode_id=int(id)) + srt_lang=language, media_type='series', series_id=metadata['seriesId'], + episode_id = int(id)) else: subsync.sync(video_path=video_path, srt_path=subtitles_path, - srt_lang=language, media_type='movies', radarr_id=id) + srt_lang=language, media_type='movies', movie_id=id) elif action == 'translate': dest_language = language forced = True if request.form.get('forced') == 'true' else False @@ -54,9 +51,9 @@ class Subtitles(Resource): forced=forced, hi=hi) if result: if media_type == 'episode': - store_subtitles(path_mappings.path_replace_reverse(video_path), video_path) + store_subtitles(video_path) else: - store_subtitles_movie(path_mappings.path_replace_reverse_movie(video_path), video_path) + store_subtitles_movie(video_path) return '', 200 else: return '', 404 diff --git a/bazarr/api/system/languages.py b/bazarr/api/system/languages.py index 0f69a8eea..43964dfc6 100644 --- a/bazarr/api/system/languages.py +++ b/bazarr/api/system/languages.py @@ -16,12 +16,12 @@ class Languages(Resource): history = request.args.get('history') if history and history not in False_Keys: languages = list(TableHistory.select(TableHistory.language) - .where(TableHistory.language != None) + .where(TableHistory.language is not None) .dicts()) languages += list(TableHistoryMovie.select(TableHistoryMovie.language) - .where(TableHistoryMovie.language != None) + .where(TableHistoryMovie.language is not None) .dicts()) - languages_list = list(set([l['language'].split(':')[0] for l in languages])) + languages_list = list(set([lang['language'].split(':')[0] for lang in languages])) languages_dicts = [] for language in languages_list: code2 = None @@ -40,7 +40,7 @@ class Languages(Resource): # Compatibility: Use false temporarily 'enabled': False }) - except: + except Exception: continue return jsonify(sorted(languages_dicts, key=itemgetter('name'))) diff --git a/bazarr/api/system/searches.py b/bazarr/api/system/searches.py index 5386be226..d0f211020 100644 --- a/bazarr/api/system/searches.py +++ b/bazarr/api/system/searches.py @@ -15,10 +15,10 @@ class Searches(Resource): search_list = [] if query: - if settings.general.getboolean('use_sonarr'): + if settings.general.getboolean('use_series'): # Get matching series series = TableShows.select(TableShows.title, - TableShows.sonarrSeriesId, + TableShows.seriesId, TableShows.year)\ .where(TableShows.title.contains(query))\ .order_by(TableShows.title)\ @@ -26,10 +26,10 @@ class Searches(Resource): series = list(series) search_list += series - if settings.general.getboolean('use_radarr'): + if settings.general.getboolean('use_movies'): # Get matching movies movies = TableMovies.select(TableMovies.title, - TableMovies.radarrId, + TableMovies.movieId, TableMovies.year) \ .where(TableMovies.title.contains(query)) \ .order_by(TableMovies.title) \ diff --git a/bazarr/api/system/settings.py b/bazarr/api/system/settings.py index c8d9574fc..026adbea3 100644 --- a/bazarr/api/system/settings.py +++ b/bazarr/api/system/settings.py @@ -83,9 +83,9 @@ class SystemSettings(Resource): update_profile_id_list() event_stream("languages") - if settings.general.getboolean('use_sonarr'): + if settings.general.getboolean('use_series'): scheduler.add_job(list_missing_subtitles, kwargs={'send_event': False}) - if settings.general.getboolean('use_radarr'): + if settings.general.getboolean('use_movies'): scheduler.add_job(list_missing_subtitles_movies, kwargs={'send_event': False}) # Update Notification diff --git a/bazarr/api/system/status.py b/bazarr/api/system/status.py index 4a8b69a16..1306e2782 100644 --- a/bazarr/api/system/status.py +++ b/bazarr/api/system/status.py @@ -7,7 +7,6 @@ from flask import jsonify from flask_restful import Resource from ..utils import authenticate -from utils import get_sonarr_info, get_radarr_info from get_args import args from init import startTime @@ -17,8 +16,6 @@ class SystemStatus(Resource): def get(self): system_status = {} system_status.update({'bazarr_version': os.environ["BAZARR_VERSION"]}) - system_status.update({'sonarr_version': get_sonarr_info.version()}) - system_status.update({'radarr_version': get_radarr_info.version()}) system_status.update({'operating_system': platform.platform()}) system_status.update({'python_version': platform.python_version()}) system_status.update({'bazarr_directory': os.path.dirname(os.path.dirname(__file__))}) diff --git a/bazarr/api/utils.py b/bazarr/api/utils.py index 61e19c947..3104e5d45 100644 --- a/bazarr/api/utils.py +++ b/bazarr/api/utils.py @@ -6,10 +6,9 @@ from functools import wraps from flask import request, abort from operator import itemgetter -from config import settings, base_url +from config import settings from get_languages import language_from_alpha2, alpha3_from_alpha2 from database import get_audio_profile_languages, get_desired_languages -from helper import path_mappings None_Keys = ['null', 'undefined', '', None] @@ -74,7 +73,7 @@ def postprocessSeries(item): postprocess(item) # Parse audio language if 'audio_language' in item and item['audio_language'] is not None: - item['audio_language'] = get_audio_profile_languages(series_id=item['sonarrSeriesId']) + item['audio_language'] = get_audio_profile_languages(series_id=item['seriesId']) if 'alternateTitles' in item: if item['alternateTitles'] is None: @@ -87,23 +86,11 @@ def postprocessSeries(item): if 'seriesType' in item and item['seriesType'] is not None: item['seriesType'] = item['seriesType'].capitalize() - if 'path' in item: - item['path'] = path_mappings.path_replace(item['path']) - - # map poster and fanart to server proxy - if 'poster' in item: - poster = item['poster'] - item['poster'] = f"{base_url}/images/series{poster}" if poster else None - - if 'fanart' in item: - fanart = item['fanart'] - item['fanart'] = f"{base_url}/images/series{fanart}" if fanart else None - def postprocessEpisode(item): postprocess(item) if 'audio_language' in item and item['audio_language'] is not None: - item['audio_language'] = get_audio_profile_languages(episode_id=item['sonarrEpisodeId']) + item['audio_language'] = get_audio_profile_languages(episode_id=item['episodeId']) if 'subtitles' in item: if item['subtitles'] is None: @@ -117,7 +104,7 @@ def postprocessEpisode(item): sub = {"name": language_from_alpha2(subtitle[0]), "code2": subtitle[0], "code3": alpha3_from_alpha2(subtitle[0]), - "path": path_mappings.path_replace(subs[1]), + "path": subs[1], "forced": False, "hi": False} if len(subtitle) > 1: @@ -147,21 +134,13 @@ def postprocessEpisode(item): "hi": True if subtitle[1] == 'hi' else False }) - if 'scene_name' in item: - item["sceneName"] = item["scene_name"] - del item["scene_name"] - - if 'path' in item and item['path']: - # Provide mapped path - item['path'] = path_mappings.path_replace(item['path']) - # TODO: Move def postprocessMovie(item): postprocess(item) # Parse audio language if 'audio_language' in item and item['audio_language'] is not None: - item['audio_language'] = get_audio_profile_languages(movie_id=item['radarrId']) + item['audio_language'] = get_audio_profile_languages(movie_id=item['movieId']) # Parse alternate titles if 'alternativeTitles' in item: @@ -183,7 +162,7 @@ def postprocessMovie(item): item['subtitles'] = ast.literal_eval(item['subtitles']) for i, subs in enumerate(item['subtitles']): language = subs[0].split(':') - item['subtitles'][i] = {"path": path_mappings.path_replace_movie(subs[1]), + item['subtitles'][i] = {"path": subs[1], "name": language_from_alpha2(language[0]), "code2": language[0], "code3": alpha3_from_alpha2(language[0]), @@ -219,21 +198,3 @@ def postprocessMovie(item): "forced": True if language[1] == 'forced' else False, "hi": True if language[1] == 'hi' else False }) - - # Provide mapped path - if 'path' in item: - if item['path']: - item['path'] = path_mappings.path_replace_movie(item['path']) - - if 'subtitles_path' in item: - # Provide mapped subtitles path - item['subtitles_path'] = path_mappings.path_replace_movie(item['subtitles_path']) - - # map poster and fanart to server proxy - if 'poster' in item: - poster = item['poster'] - item['poster'] = f"{base_url}/images/movies{poster}" if poster else None - - if 'fanart' in item: - fanart = item['fanart'] - item['fanart'] = f"{base_url}/images/movies{fanart}" if fanart else None diff --git a/bazarr/api/webhooks/plex.py b/bazarr/api/webhooks/plex.py index 6cc6e9da8..1c30596b0 100644 --- a/bazarr/api/webhooks/plex.py +++ b/bazarr/api/webhooks/plex.py @@ -47,30 +47,30 @@ class WebHooksPlex(Resource): headers={"User-Agent": os.environ["SZ_USER_AGENT"]}) soup = bso(r.content, "html.parser") series_imdb_id = soup.find('a', {'class': re.compile(r'SeriesParentLink__ParentTextLink')})['href'].split('/')[2] - except: + except Exception: return '', 404 else: - sonarrEpisodeId = TableEpisodes.select(TableEpisodes.sonarrEpisodeId) \ - .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId)) \ + episodeId = TableEpisodes.select(TableEpisodes.episodeId) \ + .join(TableShows) \ .where(TableShows.imdbId == series_imdb_id, TableEpisodes.season == season, TableEpisodes.episode == episode) \ .dicts() \ .get() - if sonarrEpisodeId: - episode_download_subtitles(no=sonarrEpisodeId['sonarrEpisodeId'], send_progress=True) + if episodeId: + episode_download_subtitles(no=episodeId['episodeId'], send_progress=True) else: try: movie_imdb_id = [x['imdb'] for x in ids if 'imdb' in x][0] - except: + except Exception: return '', 404 else: - radarrId = TableMovies.select(TableMovies.radarrId)\ + movieId = TableMovies.select(TableMovies.movieId)\ .where(TableMovies.imdbId == movie_imdb_id)\ .dicts()\ .get() - if radarrId: - movies_download_subtitles(no=radarrId['radarrId']) + if movieId: + movies_download_subtitles(no=movieId['movieId']) return '', 200