Update PVideoDl to support custom download strategies and enhance cookie handling. Added new downloader registration system, updated README for clarity on downloaders, and introduced cookies configuration in settings. Frontend now includes a dedicated tab for download strategies and API endpoints for listing them.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
"""Расширения-экстракторы для сайтов с непрямыми ссылками.
|
||||
|
||||
Каждый модуль здесь регистрирует свою стратегию декоратором @register из
|
||||
app.services.downloader. Добавить сайт = положить сюда один файл; ядро не
|
||||
трогаем. load_extractors() импортирует все модули пакета — её зовут на старте
|
||||
приложения (lifespan в app/main.py).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import pkgutil
|
||||
|
||||
logger = logging.getLogger("pvideodl.extractors")
|
||||
|
||||
|
||||
def load_extractors() -> list[str]:
|
||||
"""Импортировать все модули пакета — при импорте они себя регистрируют.
|
||||
|
||||
Идемпотентно: при повторном вызове модули уже в sys.modules, их тело (а с ним
|
||||
и @register) заново не выполняется — реестр не задваивается.
|
||||
"""
|
||||
loaded: list[str] = []
|
||||
for info in pkgutil.iter_modules(__path__, __name__ + "."):
|
||||
if info.name.rsplit(".", 1)[-1].startswith("_"):
|
||||
continue
|
||||
importlib.import_module(info.name)
|
||||
loaded.append(info.name)
|
||||
if loaded:
|
||||
logger.info("Загружено экстракторов: %d (%s)", len(loaded), ", ".join(loaded))
|
||||
return loaded
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Пример расширения: файлы Google Drive по ссылке-«просмотру».
|
||||
|
||||
Образец для своих экстракторов. Здесь резолв чисто строковый (URL -> прямая
|
||||
ссылка), без скрейпинга — поэтому тестируется офлайн.
|
||||
|
||||
https://drive.google.com/file/d/<ID>/view
|
||||
-> https://drive.google.com/uc?export=download&id=<ID>
|
||||
|
||||
Оговорка: для больших файлов Drive отдаёт HTML-страницу с подтверждением
|
||||
антивирусной проверки (нужны confirm-токен и cookie) — здесь не покрыто;
|
||||
для небольших файлов прямая ссылка работает сразу.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from app.services.downloader import Resolved, SiteExtractor, register
|
||||
|
||||
_FILE_ID = re.compile(r"drive\.google\.com/file/d/([\w-]+)")
|
||||
|
||||
|
||||
@register
|
||||
class GoogleDriveExtractor(SiteExtractor):
|
||||
"""Google Drive: ссылка-просмотр файла -> прямая загрузка."""
|
||||
|
||||
priority = 100
|
||||
label = "Google Drive"
|
||||
|
||||
@classmethod
|
||||
def matches(cls, url: str) -> bool:
|
||||
return bool(_FILE_ID.search(url))
|
||||
|
||||
async def resolve(self, url: str) -> Resolved:
|
||||
match = _FILE_ID.search(url)
|
||||
if not match: # matches() уже проверил, но без него resolve честно падает
|
||||
raise ValueError(f"Не ссылка на файл Google Drive: {url}")
|
||||
file_id = match.group(1)
|
||||
return Resolved(
|
||||
download_url=f"https://drive.google.com/uc?export=download&id={file_id}"
|
||||
)
|
||||
Reference in New Issue
Block a user