Skip to content

Commit 9004731

Browse files
authored
Merge pull request #12 from LavX/provider_priority
fix: AI translator import error, batch translate deadlock, and missing subtitle indexing
2 parents 198bf02 + b581fb9 commit 9004731

File tree

268 files changed

+108362
-3782
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

268 files changed

+108362
-3782
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
restore-keys: ${{ runner.os }}-modules-
3838

3939
- name: Setup NodeJS
40-
uses: actions/setup-node@v5
40+
uses: actions/setup-node@v6
4141
with:
4242
node-version-file: "${{ env.UI_DIRECTORY }}/.nvmrc"
4343

.github/workflows/test_bazarr_execution.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
submodules: recursive
2525

2626
- name: Setup NodeJS
27-
uses: actions/setup-node@v5
27+
uses: actions/setup-node@v6
2828
with:
2929
node-version-file: "${{ env.UI_DIRECTORY }}/.nvmrc"
3030

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,24 @@ For major changes, please open an issue first.
312312

313313
---
314314

315+
## 🌐 About the Maintainer
316+
317+
This fork is maintained by **LavX**. Explore more of my projects and services:
318+
319+
### 🚀 Services
320+
- **[LavX Managed Systems](https://lavx.hu)** – Enterprise AI solutions, RAG systems, and LLMOps.
321+
- **[LavX News](https://news.lavx.hu)** – Latest insights on AI, Open Source, and emerging tech.
322+
- **[LMS Tools](https://tools.lavx.hu)** – 140+ free, privacy-focused online tools for developers and researchers.
323+
324+
### 🛠️ Open Source Projects
325+
- **[AI Subtitle Translator](https://github.com/LavX/ai-subtitle-translator)** – LLM-powered subtitle translator using OpenRouter API.
326+
- **[OpenSubtitles Scraper](https://github.com/LavX/opensubtitles-scraper)** – Web scraper for OpenSubtitles.org (no VIP required).
327+
- **[JFrog to Nexus OSS](https://github.com/LavX/jfrogtonexusoss)** – Automated migration tool for repository managers.
328+
- **[WeatherFlow](https://github.com/LavX/weatherflow)** – Multi-platform weather data forwarding (WU to Windy/Idokep).
329+
- **[Like4Like Suite](https://github.com/LavX/Like4Like-Suite)** – Social media automation and engagement toolkit.
330+
331+
---
332+
315333
## 📄 License
316334

317335
- [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
@@ -376,6 +394,7 @@ If you need something that is not already part of Bazarr, feel free to create a
376394
- Addic7ed
377395
- AnimeKalesi
378396
- Animetosho (requires AniDb HTTP API client described [here](https://wiki.anidb.net/HTTP_API_Definition))
397+
- AnimeSub.info
379398
- Assrt
380399
- AvistaZ, CinemaZ (Get session cookies using method described [here](https://github.com/morpheus65535/bazarr/pull/2375#issuecomment-2057010996))
381400
- BetaSeries

ai-subtitle-translator

bazarr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def check_python_version():
2727
print("Python " + minimum_py3_str + " or greater required. "
2828
"Current version is " + platform.python_version() + ". Please upgrade Python.")
2929
exit_program(EXIT_PYTHON_UPGRADE_NEEDED)
30-
elif int(python_version[0]) == 3 and int(python_version[1]) > 14:
31-
print("Python version greater than 3.12.x is unsupported. Current version is " + platform.python_version() +
30+
elif int(python_version[0]) == 3 and int(python_version[1]) > 13:
31+
print("Python version greater than 3.13.x is unsupported. Current version is " + platform.python_version() +
3232
". Keep in mind that even if it works, you're on your own.")
3333
elif (int(python_version[0]) == minimum_py3_tuple[0] and int(python_version[1]) < minimum_py3_tuple[1]) or \
3434
(int(python_version[0]) != minimum_py3_tuple[0]):

bazarr/api/episodes/blacklist.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def post(self):
121121
subtitles_path=subtitles_path,
122122
sonarr_series_id=sonarr_series_id,
123123
sonarr_episode_id=sonarr_episode_id):
124-
episode_download_subtitles(sonarr_episode_id)
124+
episode_download_subtitles(no=sonarr_episode_id)
125125
event_stream(type='episode-history')
126126
return '', 200
127127
else:

bazarr/api/episodes/episodes_subtitles.py

Lines changed: 34 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
# coding=utf-8
22

33
import os
4-
import logging
4+
import time
55

66
from flask_restx import Resource, Namespace, reqparse
77
from subliminal_patch.core import SUBTITLE_EXTENSIONS
88
from werkzeug.datastructures import FileStorage
99

10-
from app.database import TableShows, TableEpisodes, get_audio_profile_languages, get_profile_id, database, select
10+
from app.database import TableShows, TableEpisodes, get_profile_id, database, select
1111
from utilities.path_mappings import path_mappings
1212
from subtitles.upload import manual_upload_subtitle
13+
from subtitles.mass_download.series import episode_download_specific_subtitles
1314
from subtitles.download import generate_subtitles
1415
from subtitles.tools.delete import delete_subtitles
15-
from sonarr.history import history_log
16-
from app.notifier import send_notifications
17-
from subtitles.indexer.series import store_subtitles
18-
from app.event_handler import event_stream, show_message
16+
from app.jobs_queue import jobs_queue
17+
from app.event_handler import event_stream
1918
from app.config import settings
2019

2120
from ..utils import authenticate
@@ -42,64 +41,17 @@ class EpisodesSubtitles(Resource):
4241
def patch(self):
4342
"""Download an episode subtitles"""
4443
args = self.patch_request_parser.parse_args()
45-
sonarrSeriesId = args.get('seriesid')
46-
sonarrEpisodeId = args.get('episodeid')
47-
episodeInfo = database.execute(
48-
select(TableEpisodes.path,
49-
TableEpisodes.sceneName,
50-
TableEpisodes.audio_language,
51-
TableShows.title)
52-
.select_from(TableEpisodes)
53-
.join(TableShows)
54-
.where(TableEpisodes.sonarrEpisodeId == sonarrEpisodeId)) \
55-
.first()
56-
57-
if not episodeInfo:
58-
return 'Episode not found', 404
5944

60-
episodePath = path_mappings.path_replace(episodeInfo.path)
45+
job_id = episode_download_specific_subtitles(sonarr_series_id=args.get('seriesid'),
46+
sonarr_episode_id=args.get('episodeid'),
47+
language=args.get('language'), hi=args.get('hi').capitalize(),
48+
forced=args.get('forced').capitalize(), job_id=None)
6149

62-
if not os.path.exists(episodePath):
63-
return 'Episode file not found. Path mapping issue?', 500
50+
# Wait for the job to complete or fail
51+
while jobs_queue.get_job_status(job_id=job_id) in ['pending', 'running']:
52+
time.sleep(1)
6453

65-
sceneName = episodeInfo.sceneName or "None"
66-
67-
title = episodeInfo.title
68-
69-
language = args.get('language')
70-
hi = args.get('hi').capitalize()
71-
forced = args.get('forced').capitalize()
72-
if hi == 'True':
73-
language_str = f'{language}:hi'
74-
elif forced == 'True':
75-
language_str = f'{language}:forced'
76-
else:
77-
language_str = language
78-
79-
audio_language_list = get_audio_profile_languages(episodeInfo.audio_language)
80-
if len(audio_language_list) > 0:
81-
audio_language = audio_language_list[0]['name']
82-
else:
83-
audio_language = None
84-
85-
try:
86-
result = list(generate_subtitles(episodePath, [(language, hi, forced)], audio_language, sceneName,
87-
title, 'series', profile_id=get_profile_id(episode_id=sonarrEpisodeId)))
88-
if isinstance(result, list) and len(result):
89-
result = result[0]
90-
if isinstance(result, tuple) and len(result):
91-
result = result[0]
92-
history_log(1, sonarrSeriesId, sonarrEpisodeId, result)
93-
send_notifications(sonarrSeriesId, sonarrEpisodeId, result.message)
94-
store_subtitles(result.path, episodePath)
95-
else:
96-
event_stream(type='episode', payload=sonarrEpisodeId)
97-
show_message(f'No {language_str.upper()} subtitles found')
98-
return '', 204
99-
except OSError:
100-
return 'Unable to save subtitles file. Permission or path mapping issue?', 409
101-
else:
102-
return '', 204
54+
return jobs_queue.get_job_returned_value(job_id=job_id)
10355

10456
post_request_parser = reqparse.RequestParser()
10557
post_request_parser.add_argument('seriesid', type=int, required=True, help='Series ID')
@@ -120,6 +72,12 @@ def patch(self):
12072
def post(self):
12173
"""Upload an episode subtitles"""
12274
args = self.post_request_parser.parse_args()
75+
76+
_, ext = os.path.splitext(args.get('file').filename)
77+
78+
if not isinstance(ext, str) or ext.lower() not in SUBTITLE_EXTENSIONS:
79+
raise ValueError('A subtitle of an invalid format was uploaded.')
80+
12381
sonarrSeriesId = args.get('seriesid')
12482
sonarrEpisodeId = args.get('episodeid')
12583
episodeInfo = database.execute(
@@ -136,46 +94,21 @@ def post(self):
13694
if not os.path.exists(episodePath):
13795
return 'Episode file not found. Path mapping issue?', 500
13896

139-
audio_language = get_audio_profile_languages(episodeInfo.audio_language)
140-
if len(audio_language) and isinstance(audio_language[0], dict):
141-
audio_language = audio_language[0]
142-
else:
143-
audio_language = {'name': '', 'code2': '', 'code3': ''}
144-
145-
language = args.get('language')
146-
forced = True if args.get('forced') == 'true' else False
147-
hi = True if args.get('hi') == 'true' else False
148-
subFile = args.get('file')
149-
150-
_, ext = os.path.splitext(subFile.filename)
151-
152-
if not isinstance(ext, str) or ext.lower() not in SUBTITLE_EXTENSIONS:
153-
raise ValueError('A subtitle of an invalid format was uploaded.')
154-
155-
try:
156-
result = manual_upload_subtitle(path=episodePath,
157-
language=language,
158-
forced=forced,
159-
hi=hi,
160-
media_type='series',
161-
subtitle=subFile,
162-
audio_language=audio_language)
163-
164-
if not result:
165-
logging.debug(f"BAZARR unable to process subtitles for this episode: {episodePath}")
166-
else:
167-
if isinstance(result, tuple) and len(result):
168-
result = result[0]
169-
provider = "manual"
170-
score = 360
171-
history_log(4, sonarrSeriesId, sonarrEpisodeId, result, fake_provider=provider, fake_score=score)
172-
if not settings.general.dont_notify_manual_actions:
173-
send_notifications(sonarrSeriesId, sonarrEpisodeId, result.message)
174-
store_subtitles(result.path, episodePath)
175-
except OSError:
176-
return 'Unable to save subtitles file. Permission or path mapping issue?', 409
177-
else:
178-
return '', 204
97+
job_id = manual_upload_subtitle(path=episodePath,
98+
language=args.get('language'),
99+
forced=True if args.get('forced') == 'true' else False,
100+
hi=True if args.get('hi') == 'true' else False,
101+
media_type='series',
102+
subtitle=args.get('file'),
103+
audio_language=episodeInfo.audio_language,
104+
sonarrSeriesId=sonarrSeriesId,
105+
sonarrEpisodeId=sonarrEpisodeId)
106+
107+
# Wait for the job to complete or fail
108+
while jobs_queue.get_job_status(job_id=job_id) in ['pending', 'running']:
109+
time.sleep(1)
110+
111+
return jobs_queue.get_job_returned_value(job_id=job_id)
179112

180113
delete_request_parser = reqparse.RequestParser()
181114
delete_request_parser.add_argument('seriesid', type=int, required=True, help='Series ID')

bazarr/api/movies/movies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def post(self):
148148
.values(profileId=profileId)
149149
.where(TableMovies.radarrId == radarrId))
150150

151-
list_missing_subtitles_movies(no=radarrId, send_event=False)
151+
list_missing_subtitles_movies(no=radarrId)
152152

153153
event_stream(type='movie', payload=radarrId)
154154
event_stream(type='movie-wanted', payload=radarrId)

0 commit comments

Comments
 (0)