temporary_link feature
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ._shared import *
|
||||
from ..services import torrent_creator
|
||||
from ..services import pdf_preview_links, torrent_creator
|
||||
from ..services.reverse_dns import attach_reverse_dns
|
||||
|
||||
@bp.get("/torrents")
|
||||
@@ -105,7 +105,18 @@ def torrent_file_media_info(torrent_hash: str, file_index: int):
|
||||
return jsonify({"ok": False, "error": "No profile"}), 400
|
||||
try:
|
||||
# Note: The route is additive and keeps all existing file endpoints unchanged.
|
||||
return ok({"media_info": rtorrent.torrent_file_media_info(profile, torrent_hash, file_index)})
|
||||
media_info = rtorrent.torrent_file_media_info(profile, torrent_hash, file_index)
|
||||
if media_info.get("kind") == "pdf":
|
||||
link = pdf_preview_links.create_pdf_preview_link(
|
||||
torrent_hash,
|
||||
file_index,
|
||||
int(profile.get("id") or 0),
|
||||
int(default_user_id() or 0),
|
||||
)
|
||||
# Note: The frontend receives an in-app temporary URL instead of exposing the API download endpoint in the new-tab action.
|
||||
media_info["preview_url"] = url_for("main.pdf_preview", token=link["token"])
|
||||
media_info["preview_expires_in"] = link["expires_in"]
|
||||
return ok({"media_info": media_info})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
|
||||
@@ -199,6 +210,87 @@ def _send_staged_file(profile: dict, path: str, download_name: str, local: bool
|
||||
|
||||
|
||||
|
||||
|
||||
@bp.post("/torrents/<torrent_hash>/files/<int:file_index>/download-link")
|
||||
def torrent_file_download_link(torrent_hash: str, file_index: int):
|
||||
profile = preferences.active_profile()
|
||||
if not profile:
|
||||
return jsonify({"ok": False, "error": "No profile"}), 400
|
||||
try:
|
||||
# Note: The API validates the file selection before returning a short-lived in-app /download URL to the UI.
|
||||
rtorrent.torrent_download_file_info(profile, torrent_hash, file_index)
|
||||
link = pdf_preview_links.create_file_download_link(torrent_hash, file_index, int(profile.get("id") or 0), int(default_user_id() or 0))
|
||||
return ok({"url": url_for("main.temporary_download", token=link["token"]), "expires_in": link["expires_in"]})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
|
||||
|
||||
@bp.post("/torrents/<torrent_hash>/files/download-link")
|
||||
def torrent_file_download_link_from_body(torrent_hash: str):
|
||||
data = request.get_json(silent=True) or {}
|
||||
try:
|
||||
file_index = int(data.get("file_index"))
|
||||
except Exception:
|
||||
return jsonify({"ok": False, "error": "file_index is required"}), 400
|
||||
return torrent_file_download_link(torrent_hash, file_index)
|
||||
|
||||
|
||||
@bp.post("/torrents/<torrent_hash>/files/download.zip/link")
|
||||
def torrent_files_download_zip_link(torrent_hash: str):
|
||||
profile = preferences.active_profile()
|
||||
if not profile:
|
||||
return jsonify({"ok": False, "error": "No profile"}), 400
|
||||
data = request.get_json(silent=True) or {}
|
||||
try:
|
||||
indexes = data.get("indexes") or None
|
||||
# Note: ZIP link creation validates the requested files through the same service used by the direct download endpoint.
|
||||
rtorrent.torrent_download_zip_items(profile, torrent_hash, indexes)
|
||||
link = pdf_preview_links.create_file_zip_download_link(torrent_hash, indexes, int(profile.get("id") or 0), int(default_user_id() or 0))
|
||||
return ok({"url": url_for("main.temporary_download", token=link["token"]), "expires_in": link["expires_in"]})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
|
||||
|
||||
@bp.get("/torrents/<torrent_hash>/torrent-file/link")
|
||||
def torrent_file_export_link(torrent_hash: str):
|
||||
profile = preferences.active_profile()
|
||||
if not profile:
|
||||
return jsonify({"ok": False, "error": "No profile"}), 400
|
||||
try:
|
||||
# Note: Export availability is checked before the UI receives a temporary /download URL.
|
||||
item = rtorrent.export_torrent_file(profile, torrent_hash)
|
||||
_cleanup_staged_file(profile, item["path"], bool(item.get("local")))
|
||||
link = pdf_preview_links.create_torrent_file_download_link(torrent_hash, int(profile.get("id") or 0), int(default_user_id() or 0))
|
||||
return ok({"url": url_for("main.temporary_download", token=link["token"]), "expires_in": link["expires_in"]})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
|
||||
|
||||
@bp.post("/torrents/torrent-files.zip/link")
|
||||
def torrent_files_export_zip_link():
|
||||
profile = preferences.active_profile()
|
||||
if not profile:
|
||||
return jsonify({"ok": False, "error": "No profile"}), 400
|
||||
data = request.get_json(silent=True) or {}
|
||||
hashes = [str(h) for h in (data.get("hashes") or []) if str(h).strip()]
|
||||
if not hashes:
|
||||
return jsonify({"ok": False, "error": "No torrents selected"}), 400
|
||||
try:
|
||||
# Note: Each hash is checked before the temporary ZIP export link is returned to the UI.
|
||||
staged_paths = []
|
||||
try:
|
||||
for h in hashes:
|
||||
item = rtorrent.export_torrent_file(profile, h)
|
||||
staged_paths.append((item["path"], bool(item.get("local"))))
|
||||
finally:
|
||||
for path, is_local in staged_paths:
|
||||
_cleanup_staged_file(profile, path, is_local)
|
||||
link = pdf_preview_links.create_torrent_files_zip_download_link(hashes, int(profile.get("id") or 0), int(default_user_id() or 0))
|
||||
return ok({"url": url_for("main.temporary_download", token=link["token"]), "expires_in": link["expires_in"]})
|
||||
except Exception as exc:
|
||||
return jsonify({"ok": False, "error": str(exc)}), 400
|
||||
|
||||
|
||||
@bp.get("/torrents/<torrent_hash>/files/<int:file_index>/download")
|
||||
def torrent_file_download(torrent_hash: str, file_index: int):
|
||||
profile = preferences.active_profile()
|
||||
|
||||
Reference in New Issue
Block a user