refactor: limit item add

This commit is contained in:
arabcoders 2026-01-26 16:36:14 +03:00
parent fe6fe42a65
commit 41bca681e5
5 changed files with 31 additions and 7 deletions

View file

@ -439,12 +439,11 @@ async def task_metadata(request: Request, repo: TasksRepository, config: Config,
opts["proxy"] = proxy opts["proxy"] = proxy
try: try:
from httpx_curl_cffi import AsyncCurlTransport, CurlOpt from httpx_curl_cffi import AsyncCurlTransport
opts["transport"] = AsyncCurlTransport( opts["transport"] = AsyncCurlTransport(
impersonate="chrome", impersonate="chrome",
default_headers=True, default_headers=True,
curl_options={CurlOpt.FRESH_CONNECT: True},
) )
opts.pop("headers", None) opts.pop("headers", None)
except Exception: except Exception:

View file

@ -207,6 +207,9 @@ class Config(metaclass=Singleton):
live_premiere_buffer: int = 5 live_premiere_buffer: int = 5
"""The buffer time in minutes to add to video duration to wait before starting premiere download.""" """The buffer time in minutes to add to video duration to wait before starting premiere download."""
add_items_concurrency: int = 4
"""The number of concurrent add items to be processed at same time."""
playlist_items_concurrency: int = 4 playlist_items_concurrency: int = 4
"""The number of concurrent playlist items to be processed at same time.""" """The number of concurrent playlist items to be processed at same time."""
@ -274,6 +277,7 @@ class Config(metaclass=Singleton):
"auto_clear_history_days", "auto_clear_history_days",
"default_pagination", "default_pagination",
"extract_info_concurrency", "extract_info_concurrency",
"add_items_concurrency",
"flaresolverr_max_timeout", "flaresolverr_max_timeout",
"flaresolverr_client_timeout", "flaresolverr_client_timeout",
"flaresolverr_cache_ttl", "flaresolverr_cache_ttl",

View file

@ -76,7 +76,11 @@ async def add_item(
async def add( async def add(
queue: "DownloadQueue", item: "Item", already: set | None = None, entry: dict | None = None queue: "DownloadQueue",
item: "Item",
already: set | None = None,
entry: dict | None = None,
playlist: bool = False,
) -> dict[str, str]: ) -> dict[str, str]:
""" """
Add an item to the download queue. Add an item to the download queue.
@ -86,11 +90,23 @@ async def add(
item: Item to be added to the queue item: Item to be added to the queue
already: Set of already downloaded items already: Set of already downloaded items
entry: Entry associated with the item (if already extracted) entry: Entry associated with the item (if already extracted)
playlist: Whether the item is part of a playlist
Returns: Returns:
dict[str, str]: Status dict with "status" and optional "msg" keys dict[str, str]: Status dict with "status" and optional "msg" keys
""" """
if playlist:
# If not done like this, it would lead to deadlocks when adding playlist items.
return await _add_impl(queue=queue, item=item, already=already, entry=entry)
async with queue.add_sem:
return await _add_impl(queue=queue, item=item, already=already, entry=entry)
async def _add_impl(
queue: "DownloadQueue", item: "Item", already: set | None = None, entry: dict | None = None
) -> dict[str, str]:
_preset: Preset | None = Presets.get_instance().get(item.preset) _preset: Preset | None = Presets.get_instance().get(item.preset)
if item.has_cli(): if item.has_cli():

View file

@ -98,9 +98,9 @@ async def process_playlist(
): ):
dct = merge_dict(merge_dict({"_type": "video"}, etr), entry) dct = merge_dict(merge_dict({"_type": "video"}, etr), entry)
dct.pop("entries", None) dct.pop("entries", None)
return await queue.add(item=newItem, entry=dct, already=already) return await queue.add(item=newItem, entry=dct, already=already, playlist=True)
return await queue.add(item=newItem, already=already) return await queue.add(item=newItem, already=already, playlist=True)
finally: finally:
if acquired: if acquired:
queue.processors.release() queue.processors.release()

View file

@ -43,6 +43,8 @@ class DownloadQueue(metaclass=Singleton):
"DataStore for the download queue." "DataStore for the download queue."
self.processors = asyncio.Semaphore(self.config.playlist_items_concurrency) self.processors = asyncio.Semaphore(self.config.playlist_items_concurrency)
"Semaphore to limit the number of concurrent processors." "Semaphore to limit the number of concurrent processors."
self.add_sem = asyncio.Semaphore(self.config.add_items_concurrency)
"Semaphore to limit the number of concurrent add item operations."
self.pool = PoolManager(queue=self, config=self.config) self.pool = PoolManager(queue=self, config=self.config)
"Pool manager for coordinating download execution." "Pool manager for coordinating download execution."
@ -221,7 +223,9 @@ class DownloadQueue(metaclass=Singleton):
async def on_shutdown(self, _: web.Application): async def on_shutdown(self, _: web.Application):
await self.pool.shutdown() await self.pool.shutdown()
async def add(self, item: Item, already: set | None = None, entry: dict | None = None) -> dict[str, str]: async def add(
self, item: Item, already: set | None = None, entry: dict | None = None, playlist: bool = False
) -> dict[str, str]:
""" """
Add an item to the download queue. Add an item to the download queue.
@ -229,12 +233,13 @@ class DownloadQueue(metaclass=Singleton):
item: Item to be added to the queue item: Item to be added to the queue
already: Set of already downloaded items already: Set of already downloaded items
entry: Entry associated with the item (if already extracted) entry: Entry associated with the item (if already extracted)
playlist: Whether the item is part of a playlist
Returns: Returns:
dict[str, str]: Status dict with "status" and optional "msg" keys dict[str, str]: Status dict with "status" and optional "msg" keys
""" """
return await add_impl(queue=self, item=item, already=already, entry=entry) return await add_impl(queue=self, item=item, already=already, entry=entry, playlist=playlist)
async def cancel(self, ids: list[str]) -> dict[str, str]: async def cancel(self, ids: list[str]) -> dict[str, str]:
""" """