168 lines
5.7 KiB
Python
168 lines
5.7 KiB
Python
import logging
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
import anyio
|
|
from aiohttp import web
|
|
from aiohttp.web import Request, Response
|
|
|
|
from app.library.config import Config
|
|
from app.library.Download import Download
|
|
from app.library.DownloadQueue import DownloadQueue
|
|
from app.library.router import route
|
|
from app.library.Utils import is_downloaded, remove_from_archive
|
|
from app.library.YTDLPOpts import YTDLPOpts
|
|
|
|
if TYPE_CHECKING:
|
|
from library.Download import Download
|
|
|
|
LOG: logging.Logger = logging.getLogger(__name__)
|
|
|
|
|
|
@route("DELETE", r"api/archive/{id}", "archive.remove")
|
|
async def archive_remove(request: Request, queue: DownloadQueue, config: Config) -> Response:
|
|
"""
|
|
Remove an item from the archive.
|
|
|
|
Args:
|
|
request (Request): The request object.
|
|
queue (DownloadQueue): The download queue instance.
|
|
config (Config): The configuration instance.
|
|
|
|
Returns:
|
|
Response: The response object.
|
|
|
|
"""
|
|
item = None
|
|
try:
|
|
data: dict | None = await request.json()
|
|
except Exception:
|
|
data = {}
|
|
|
|
title: str = ""
|
|
|
|
url: str | None = data.get("url", None) if data else None
|
|
|
|
if not url:
|
|
id: str = request.match_info.get("id")
|
|
if not id:
|
|
return web.json_response(data={"error": "id is required."}, status=web.HTTPBadRequest.status_code)
|
|
|
|
try:
|
|
item: Download | None = queue.done.get_by_id(id)
|
|
if not item:
|
|
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
|
|
|
url = item.info.url
|
|
title = f" '{item.info.title}'"
|
|
except KeyError:
|
|
return web.json_response(data={"error": f"item '{id}' not found."}, status=web.HTTPNotFound.status_code)
|
|
|
|
if config.manual_archive:
|
|
remove_from_archive(archive_file=Path(config.manual_archive), url=url)
|
|
|
|
archive_file: Path | None = Path(config.archive_file) if config.keep_archive else None
|
|
if item:
|
|
params: YTDLPOpts = YTDLPOpts.get_instance().preset(name=item.info.preset)
|
|
if item.info.cli:
|
|
params.add_cli(item.info.cli, from_user=True)
|
|
|
|
params = params.get_all()
|
|
if user_file := params.get("download_archive", None):
|
|
archive_file = Path(user_file)
|
|
|
|
if not archive_file:
|
|
return web.json_response(
|
|
data={
|
|
"error": "Archive file is not configured." if not config.keep_archive else "Archive file is not set."
|
|
},
|
|
status=web.HTTPBadRequest.status_code,
|
|
)
|
|
|
|
if not archive_file.exists():
|
|
return web.json_response(
|
|
data={"error": f"Archive file '{archive_file}' does not exist."},
|
|
status=web.HTTPNotFound.status_code,
|
|
)
|
|
|
|
if not remove_from_archive(archive_file=archive_file, url=url):
|
|
return web.json_response(
|
|
data={"error": f"item{title} not found in '{archive_file}' archive."},
|
|
status=web.HTTPNotFound.status_code,
|
|
)
|
|
|
|
return web.json_response(
|
|
data={"message": f"item{title} removed from '{archive_file}' archive."},
|
|
status=web.HTTPOk.status_code,
|
|
)
|
|
|
|
|
|
@route("POST", r"api/archive/{id}", "archive.item")
|
|
async def archive_item(request: Request, queue: DownloadQueue, config: Config):
|
|
"""
|
|
Manually mark an item as archived.
|
|
|
|
Args:
|
|
request (Request): The request object.
|
|
queue (DownloadQueue): The download queue instance.
|
|
config (Config): The configuration instance.
|
|
|
|
Returns:
|
|
Response: The response object.
|
|
|
|
"""
|
|
id: str = request.match_info.get("id")
|
|
|
|
if not id:
|
|
return web.json_response(data={"error": "id is required."}, status=web.HTTPBadRequest.status_code)
|
|
|
|
try:
|
|
item: Download | None = queue.done.get_by_id(id)
|
|
if not item:
|
|
return web.json_response(data={"error": "item not found."}, status=web.HTTPNotFound.status_code)
|
|
except KeyError:
|
|
return web.json_response(data={"error": "item not found."}, status=web.HTTPNotFound.status_code)
|
|
|
|
if config.manual_archive:
|
|
manual_archive = Path(config.manual_archive)
|
|
if manual_archive.exists():
|
|
exists, idDict = is_downloaded(manual_archive, item.info.url)
|
|
if exists is False and idDict.get("archive_id"):
|
|
async with await anyio.open_file(manual_archive, "a") as f:
|
|
await f.write(f"{idDict['archive_id']} - at: {datetime.now(UTC).isoformat()}\n")
|
|
|
|
params: YTDLPOpts = YTDLPOpts.get_instance().preset(name=item.info.preset)
|
|
if item.info.cli:
|
|
params.add_cli(item.info.cli, from_user=True)
|
|
|
|
params = params.get_all()
|
|
|
|
user_file: str | None = params.get("download_archive", None)
|
|
archive_file: Path = Path(user_file) if user_file else Path(config.archive_file)
|
|
if not archive_file.exists():
|
|
return web.json_response(
|
|
data={"error": f"Archive file '{archive_file}' does not exist."},
|
|
status=web.HTTPNotFound.status_code,
|
|
)
|
|
|
|
exists, idDict = is_downloaded(archive_file, item.info.url)
|
|
item_id: str | None = idDict.get("archive_id")
|
|
if not item_id:
|
|
return web.json_response(
|
|
data={"error": "item does not have an archive ID."}, status=web.HTTPBadRequest.status_code
|
|
)
|
|
|
|
if exists is True:
|
|
return web.json_response(
|
|
data={"error": f"item '{item_id}' already archived in file '{archive_file}'."},
|
|
status=web.HTTPConflict.status_code,
|
|
)
|
|
|
|
async with await anyio.open_file(archive_file, "a") as f:
|
|
await f.write(f"{item_id}\n")
|
|
|
|
return web.json_response(
|
|
data={"message": f"item '{item_id}' archived in file '{archive_file}'."},
|
|
status=web.HTTPOk.status_code,
|
|
)
|