Files is the one library: every done file, the ones shared with me, or one folder's, picked in a side rail (sources, own folders, the storage meter) and mirrored to ?folder=. One table with every kind of value in its own column; Copy link, Share and Save in sight, every action on a right click: play, cut, rename in the row, move to an own folder or back home, keep, details, re-download, delete. The Downloads page keeps only the queue until the tray replaces it. The old /api/downloads library routes are gone: list shared, upload, the details PATCH, delete, the file and the usage now live under /api/files, and a listed file says which source it came from. The job's "expired" flag is gone with the state it described.
68 lines
2.7 KiB
Python
68 lines
2.7 KiB
Python
"""Which HTML page the server hands a path (main.py), DB-lane.
|
|
|
|
The frontend builds two entries: the app (index.html) and a shared link's player alone (watch.html), so
|
|
the friend a /watch link was sent to never downloads the app. Every /watch/ path must get the player's
|
|
page — with the link's Open Graph tags when the token names a playable link — and nothing else may.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from app import main
|
|
from app.config import settings
|
|
from app.downloads import probe as probemod
|
|
|
|
_PAGE = """<!doctype html><html><head>
|
|
<!--OG:START--><meta property="og:title" content="Siftlode" /><!--OG:END-->
|
|
</head><body><script src="/assets/{entry}.js"></script></body></html>"""
|
|
|
|
|
|
@pytest.fixture
|
|
def pages(tmp_path, monkeypatch):
|
|
index, watch = tmp_path / "index.html", tmp_path / "watch.html"
|
|
index.write_text(_PAGE.format(entry="app"), encoding="utf-8")
|
|
watch.write_text(_PAGE.format(entry="watch"), encoding="utf-8")
|
|
monkeypatch.setattr(main, "INDEX_HTML", index)
|
|
monkeypatch.setattr(main, "WATCH_HTML", watch)
|
|
|
|
|
|
@pytest.fixture
|
|
def link_token(as_user, make_user, tmp_path, monkeypatch, upload_headers):
|
|
monkeypatch.setattr(settings, "files_root", str(tmp_path / "media"))
|
|
video = {"codec_type": "video", "codec_name": "h264", "pix_fmt": "yuv420p"}
|
|
audio = {"codec_type": "audio", "codec_name": "aac"}
|
|
data = {"streams": [video, audio], "format": {"format_name": "mov,mp4", "duration": "5"}}
|
|
monkeypatch.setattr(probemod, "probe_json", lambda _p: data)
|
|
owner = as_user(make_user())
|
|
job = owner.post(
|
|
"/api/files/upload",
|
|
content=b"fake-video",
|
|
headers=upload_headers("clip.mp4", display_name="Holiday clip"),
|
|
)
|
|
assert job.status_code == 200, job.text
|
|
link = owner.post(f"/api/downloads/{job.json()['id']}/links", json={})
|
|
assert link.status_code == 200, link.text
|
|
return link.json()["token"]
|
|
|
|
|
|
@pytest.mark.parametrize("path", ["/", "/feed", "/downloads/42"])
|
|
def test_an_app_path_gets_the_app(pages, client, path):
|
|
r = client.get(path)
|
|
assert r.status_code == 200
|
|
assert "/assets/app.js" in r.text
|
|
|
|
|
|
@pytest.mark.parametrize("path", ["/watch/no-such-token", "/watch/no-such-token/extra"])
|
|
def test_every_watch_path_gets_the_player_alone(pages, client, path):
|
|
r = client.get(path)
|
|
assert r.status_code == 200
|
|
assert "/assets/watch.js" in r.text and "/assets/app.js" not in r.text
|
|
assert r.headers["cache-control"] == "no-cache"
|
|
|
|
|
|
def test_a_live_link_unfurls_from_the_players_page(pages, client, link_token):
|
|
r = client.get(f"/watch/{link_token}")
|
|
assert r.status_code == 200
|
|
assert "/assets/watch.js" in r.text
|
|
assert 'content="Holiday clip"' in r.text
|
|
assert '<meta property="og:title" content="Siftlode" />' not in r.text # replaced, not appended
|