Skip to content

Commit a23a3d3

Browse files
committed
Merge branch 'refs/heads/development' into python_and_deps_upgrade
2 parents 98af85e + 515bf67 commit a23a3d3

12 files changed

Lines changed: 87 additions & 159 deletions

File tree

bazarr/app/check_update.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ def update_cleaner(zipfile, bazarr_dir, config_dir):
221221
dir_to_ignore = [f'^.{separator}',
222222
f'^bin{separator}',
223223
f'^venv{separator}',
224+
f'^.venv{separator}',
224225
f'^WinPython{separator}',
225226
f'{separator}__pycache__{separator}$']
226227
if os.path.abspath(bazarr_dir).lower() == os.path.abspath(config_dir).lower():

bazarr/app/get_providers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def provider_throttle_map():
9494
TooManyRequests: (datetime.timedelta(minutes=10), "10 minutes"),
9595
},
9696
"titulky": {
97+
TooManyRequests: (datetime.timedelta(minutes=1), "1 minute"),
9798
DownloadLimitExceeded: (
9899
titulky_limit_reset_timedelta(),
99100
f"{titulky_limit_reset_timedelta().seconds // 3600 + 1} hours")

bazarr/app/jobs_queue.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -504,16 +504,19 @@ def consume_jobs_pending_queue(self):
504504
"""
505505
while True:
506506
try:
507-
with self._queue_lock:
508-
can_run_job = (len(self.jobs_running_queue) < settings.general.concurrent_jobs
509-
and len(self.jobs_pending_queue) > 0)
510-
511-
if can_run_job:
512-
job_thread = Thread(target=self._run_job)
513-
job_thread.daemon = True
514-
job_thread.start()
507+
if self.jobs_pending_queue:
508+
with self._queue_lock:
509+
can_run_job = (len(self.jobs_running_queue) < settings.general.concurrent_jobs
510+
and len(self.jobs_pending_queue) > 0)
511+
512+
if can_run_job:
513+
job_thread = Thread(target=self._run_job)
514+
job_thread.daemon = True
515+
job_thread.start()
516+
else:
517+
sleep(0.5)
515518
else:
516-
sleep(0.1)
519+
sleep(0.5)
517520
except (KeyboardInterrupt, SystemExit):
518521
break
519522

bazarr/subtitles/processing.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ def process_subtitle(subtitle, media_type, audio_language, path, max_score, is_u
9898
percent_score=percent_score,
9999
sonarr_series_id=episode_metadata.sonarrSeriesId,
100100
sonarr_episode_id=episode_metadata.sonarrEpisodeId,
101-
job_id=job_id,
102-
job_sub_function=True)
101+
job_id=job_id)
103102
else:
104103
movie_metadata = database.execute(
105104
select(TableMovies.radarrId, TableMovies.imdbId)
@@ -118,8 +117,7 @@ def process_subtitle(subtitle, media_type, audio_language, path, max_score, is_u
118117
srt_lang=downloaded_language_code2,
119118
percent_score=percent_score,
120119
radarr_id=movie_metadata.radarrId,
121-
job_id=job_id,
122-
job_sub_function=True)
120+
job_id=job_id)
123121

124122
if use_postprocessing is True:
125123
command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language, downloaded_language_code2,

bazarr/subtitles/sync.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,16 @@ def sync_subtitles(video_path,
2323
gss=settings.subsync.gss,
2424
no_fix_framerate=settings.subsync.no_fix_framerate,
2525
reference=None,
26-
force_sync=False,
27-
job_sub_function=False):
26+
force_sync=False):
2827
if not settings.subsync.use_subsync and not force_sync:
2928
logging.debug('BAZARR automatic syncing is disabled in settings. Skipping sync routine.')
3029
return False
3130

3231
if not job_id:
33-
jobs_queue.add_job_from_function("Subtitles synchronization", is_progress=True)
32+
jobs_queue.add_job_from_function(f"Syncing {srt_path}", is_progress=False)
3433
return False
3534

36-
if job_sub_function:
37-
jobs_queue.update_job_progress_status(job_id=job_id, is_progress=True)
38-
39-
jobs_queue.update_job_progress(job_id=job_id, progress_message=f"Syncing {srt_path}")
35+
jobs_queue.update_job_name(job_id=job_id, new_job_name=f"Syncing {srt_path}")
4036

4137
if forced:
4238
logging.debug('BAZARR cannot sync forced subtitles. Skipping sync routine.')
@@ -65,10 +61,6 @@ def sync_subtitles(video_path,
6561
'sonarr_series_id': sonarr_series_id,
6662
'sonarr_episode_id': sonarr_episode_id,
6763
'radarr_id': radarr_id,
68-
'progress_callback': lambda x: jobs_queue.update_job_progress(job_id=x['job_id'],
69-
progress_value=x['value'],
70-
progress_max=x['count'],
71-
progress_message=f"Syncing {srt_path}"),
7264
'job_id': job_id,
7365
'force_sync': force_sync,
7466
}
@@ -81,13 +73,11 @@ def sync_subtitles(video_path,
8173
else:
8274
return True
8375
finally:
84-
jobs_queue.update_job_progress(job_id=job_id, progress_value="max",
85-
progress_message=f"Synced {srt_path}")
76+
jobs_queue.update_job_name(job_id=job_id, new_job_name=f"Synced {srt_path}")
8677
del subsync
8778
gc.collect()
8879
else:
8980
logging.debug(f"BAZARR subsync skipped because subtitles score isn't below this "
9081
f"threshold value: {subsync_threshold}%")
9182

92-
jobs_queue.update_job_progress(job_id=job_id, progress_value="max", progress_message=f"Synced {srt_path}")
9383
return False

bazarr/subtitles/tools/subsyncer.py

Lines changed: 2 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22

33
import logging
44
import os
5-
import threading
6-
import time
75

86
from ffsubsync.ffsubsync import run, make_parser
9-
from tqdm import tqdm
107

118
from utilities.binaries import get_binary
129
from radarr.history import history_log_movie
@@ -34,77 +31,16 @@ def __init__(self):
3431
self.log_dir_path = os.path.join(args.config_dir, 'log')
3532
self.progress_callback = None
3633
self.sync_result = None
37-
self.sync_exception = None
38-
self._tqdm_instance = None
3934
self.job_id = None
4035

41-
@staticmethod
42-
def _capture_tqdm_instance():
43-
"""Capture the tqdm instance from ffsubsync's VideoSpeechTransformer."""
44-
try:
45-
# Get all active tqdm instances
46-
if hasattr(tqdm, '_instances'):
47-
instances = list(tqdm._instances)
48-
if instances:
49-
# Return the most recent instance
50-
return instances[-1]
51-
except Exception as e:
52-
logging.debug(f'BAZARR unable to capture tqdm instance: {e}')
53-
return None
54-
55-
def _monitor_tqdm_progress(self, job_id):
56-
"""Monitor tqdm progress in a loop."""
57-
last_n = 0
58-
last_total = 0
59-
60-
while self.sync_result is None and self.sync_exception is None:
61-
try:
62-
time.sleep(1) # Check every 1s
63-
64-
# Try to capture tqdm instance if we don't have it yet
65-
if self._tqdm_instance is None:
66-
self._tqdm_instance = self._capture_tqdm_instance()
67-
68-
if self._tqdm_instance is not None:
69-
# Access tqdm's internal state
70-
current_n = getattr(self._tqdm_instance, 'n', 0)
71-
total = getattr(self._tqdm_instance, 'total', 0)
72-
73-
# Only send update if values changed
74-
if current_n != last_n or total != last_total:
75-
last_n = current_n
76-
last_total = total
77-
78-
if self.progress_callback and total:
79-
# Convert to integer percentages for a cleaner display
80-
value = int(current_n)
81-
count = int(total)
82-
83-
self.progress_callback({
84-
'job_id': job_id,
85-
'value': value,
86-
'count': count,
87-
})
88-
except Exception as e:
89-
logging.debug(f'BAZARR error monitoring tqdm progress: {e}')
90-
time.sleep(1) # Wait longer on error
91-
92-
def _run_sync_in_thread(self):
93-
"""Run the sync operation in a separate thread."""
94-
try:
95-
self.sync_result = run(self.args)
96-
except Exception as e:
97-
self.sync_exception = e
98-
9936
def sync(self, video_path, srt_path, srt_lang, hi, forced,
10037
max_offset_seconds, no_fix_framerate, gss, reference=None, sonarr_series_id=None, sonarr_episode_id=None,
10138
radarr_id=None, progress_callback=None, job_id=None, force_sync=False):
10239
self.reference = video_path
10340
self.srtin = srt_path
10441
self.progress_callback = progress_callback
10542
self.sync_result = None
106-
self.sync_exception = None
107-
self._tqdm_instance = None
43+
10844
if self.srtin.casefold().endswith('.ass'):
10945
# try to preserve the original subtitle style
11046
# ffmpeg will be able to handle this automatically as long as it has the libass filter
@@ -164,31 +100,7 @@ def sync(self, video_path, srt_path, srt_lang, hi, forced,
164100
os.remove(self.srtout)
165101
logging.debug('BAZARR deleted the previous subtitles synchronization attempt file.')
166102

167-
# Start sync in a separate thread
168-
sync_thread = threading.Thread(target=self._run_sync_in_thread, daemon=False)
169-
sync_thread.start()
170-
171-
# Start progress monitoring if callback provided
172-
if progress_callback:
173-
# Give the sync thread a moment to start and create tqdm instance
174-
time.sleep(0.5)
175-
monitor_thread = threading.Thread(
176-
target=self._monitor_tqdm_progress,
177-
args=(job_id,),
178-
daemon=False
179-
)
180-
monitor_thread.start()
181-
182-
# Wait for both threads to complete
183-
sync_thread.join()
184-
monitor_thread.join()
185-
else:
186-
# Just wait for sync to complete
187-
sync_thread.join()
188-
189-
# Check if an exception occurred
190-
if self.sync_exception:
191-
raise self.sync_exception
103+
self.sync_result = run(self.args)
192104

193105
result = self.sync_result
194106
except Exception:

bazarr/subtitles/upload.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,15 @@ def manual_upload_subtitle(path, language, forced, hi, media_type, subtitle, aud
163163
if media_type == 'series':
164164
sync_subtitles(video_path=path, srt_path=subtitle_path, srt_lang=uploaded_language_code2, percent_score=100,
165165
sonarr_series_id=episode_metadata.sonarrSeriesId, forced=forced, hi=hi,
166-
sonarr_episode_id=episode_metadata.sonarrEpisodeId, job_id=job_id, job_sub_function=True,)
166+
sonarr_episode_id=episode_metadata.sonarrEpisodeId, job_id=job_id)
167167
reversed_path = path_mappings.path_replace_reverse(path)
168168
reversed_subtitles_path = path_mappings.path_replace_reverse(subtitle_path)
169169
notify_sonarr(episode_metadata.sonarrSeriesId)
170170
event_stream(type='series', action='update', payload=episode_metadata.sonarrSeriesId)
171171
event_stream(type='episode-wanted', action='delete', payload=episode_metadata.sonarrEpisodeId)
172172
else:
173173
sync_subtitles(video_path=path, srt_path=subtitle_path, srt_lang=uploaded_language_code2, percent_score=100,
174-
radarr_id=movie_metadata.radarrId, forced=forced, hi=hi, job_id=job_id, job_sub_function=True,)
174+
radarr_id=movie_metadata.radarrId, forced=forced, hi=hi, job_id=job_id)
175175
reversed_path = path_mappings.path_replace_reverse_movie(path)
176176
reversed_subtitles_path = path_mappings.path_replace_reverse_movie(subtitle_path)
177177
notify_radarr(movie_metadata.radarrId)

custom_libs/subliminal_patch/providers/opensubtitlescom.py

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -302,26 +302,26 @@ def query(self, languages, video):
302302
self.video = video
303303
if self.use_hash:
304304
file_hash = self.video.hashes.get('opensubtitlescom')
305-
logger.debug(f'Searching using this hash: {file_hash}')
306305
else:
307306
file_hash = None
307+
logger.debug(f'Searching using this hash: {file_hash}')
308308

309-
if isinstance(self.video, Episode):
310-
title = self.video.series
311-
else:
312-
title = self.video.title
313-
314-
imdb_id = None
315-
if isinstance(self.video, Episode) and self.video.imdb_id:
316-
imdb_id = self.sanitize_external_ids(self.video.imdb_id)
317-
elif isinstance(self.video, Movie) and self.video.imdb_id:
318-
imdb_id = self.sanitize_external_ids(self.video.imdb_id)
309+
imdb_id = self.sanitize_external_ids(self.video.imdb_id) if self.video.imdb_id else None
310+
logger.debug(f'Searching using this IMDB ID: {imdb_id}')
319311

320312
title_id = None
321-
if not imdb_id:
313+
if ((isinstance(self.video, Episode) and not self.video.series_imdb_id) or
314+
(isinstance(self.video, Movie) and not imdb_id)):
315+
if isinstance(self.video, Episode):
316+
title = self.video.series
317+
else:
318+
title = self.video.title
319+
logger.debug(f'Searching for this title: {title}')
320+
322321
title_id = self.search_titles(title)
323-
if not title_id:
324-
return []
322+
logger.debug(f'Found this title ID: {title_id}')
323+
else:
324+
logger.debug(f"No need to search for a title ID. We'll use the IMDB ID instead.")
325325

326326
# be sure to remove duplicates using list(set())
327327
langs_list = sorted(list(set([to_opensubtitlescom(lang.basename).lower() for lang in languages])))
@@ -331,31 +331,46 @@ def query(self, languages, video):
331331

332332
# define the proper query parameters based on the video type
333333
# query parameters must be alphabetically ordered to prevent redirect
334+
params: list[tuple[str, str | int]] = [('languages', langs)]
335+
if file_hash:
336+
params.append(('moviehash', file_hash))
337+
if imdb_id:
338+
params.append(('imdb_id', imdb_id))
339+
334340
if isinstance(self.video, Episode):
335-
params = [('episode_number', self.video.episode),
336-
('imdb_id', imdb_id),
337-
('languages', langs),
338-
('moviehash', file_hash),
339-
('parent_feature_id', title_id),
340-
('parent_imdb_id', self.sanitize_external_ids(self.video.series_imdb_id) if
341-
self.video.series_imdb_id else None),
342-
('season_number', self.video.season)]
341+
if not imdb_id and not title_id and not self.video.series_imdb_id:
342+
logger.debug("We don't have any ID to search for, returning empty list.")
343+
return []
344+
345+
if self.video.episode:
346+
params.append(('episode_number', self.video.episode))
347+
if self.video.season:
348+
params.append(('season_number', self.video.season))
349+
if self.video.series_imdb_id:
350+
params.append(('parent_imdb_id', self.sanitize_external_ids(self.video.series_imdb_id)))
351+
if title_id:
352+
params.append(('parent_feature_id', title_id))
343353
else:
344-
params = [('id', title_id),
345-
('imdb_id', imdb_id),
346-
('languages', langs),
347-
('moviehash', file_hash)]
354+
if not imdb_id and not title_id:
355+
logger.debug("We don't have any ID to search for, returning empty list.")
356+
return []
357+
358+
if title_id:
359+
params.append(('id', title_id))
348360

349361
# append the 'exclude' parameter to the list of query parameters if we don't want AI translated subtitles'
350362
if not self.include_ai_translated:
363+
logger.debug("Excluding AI translated subtitles from search results")
351364
params.append(('ai_translated', 'exclude'))
352365

353366
# append the 'exclude' parameter to the list of query parameters if we want machine translated subtitles'
354367
if self.include_machine_translated:
368+
logger.debug("Including machine translated subtitles in search results")
355369
params.append(('machine_translated', 'include'))
356370

357371
# sort params alphabetically to prevent redirect
358372
params = sorted(params, key=lambda param: param[0])
373+
logger.info(f'Query parameters used to query OpenSubtitles.com: {params}')
359374

360375
# query the server
361376
res = self.retry(
@@ -592,25 +607,28 @@ def log_request_response(response, non_standard=True):
592607
if 'Authorization' in redacted_request_headers and isinstance(redacted_request_headers['Authorization'], str):
593608
redacted_request_headers['Authorization'] = redacted_request_headers['Authorization'][:-8]+8*'x'
594609

610+
redacted_request_body = None
595611
if response.request.body:
596-
redacted_request_body = json.loads(response.request.body)
597-
if 'password' in redacted_request_body:
598-
redacted_request_body['password'] = 'redacted'
599-
else:
600-
redacted_request_body = None
612+
try:
613+
redacted_request_body = json.loads(response.request.body)
614+
except json.JSONDecodeError:
615+
logger.debug(f"Response body could not be parsed as JSON: {response.request.body!r}.")
616+
else:
617+
if 'password' in redacted_request_body:
618+
redacted_request_body['password'] = 'redacted'
601619

602620
redacted_response_body = json.loads(response.text)
603621
if 'token' in redacted_response_body and isinstance(redacted_response_body['token'], str):
604622
redacted_response_body['token'] = redacted_response_body['token'][:-8] + 8 * 'x'
605623

606624
if non_standard:
607625
logger.debug("opensubtitlescom returned a non standard response. Logging request/response for debugging "
608-
"purpose.")
626+
"purpose.")
609627
else:
610628
logger.debug("opensubtitlescom returned a standard response. Logging request/response for debugging purpose.")
611629
logger.debug(f"Request URL: {response.request.url}")
612630
logger.debug(f"Request Headers: {redacted_request_headers}")
613-
logger.debug(f"Request Body: {json.dumps(redacted_request_body)}")
631+
logger.debug(f"Request Body: {json.dumps(redacted_request_body) if redacted_request_body else None}")
614632
logger.debug(f"Response Status Code: {response.status_code}")
615633
logger.debug(f"Response Headers: {response.headers}")
616-
logger.debug(f"Response Body: {json.dumps(redacted_response_body)}")
634+
logger.debug(f"Response Body: {json.dumps(redacted_response_body) if redacted_request_body else None}")

0 commit comments

Comments
 (0)