Commit Graph
14 Commits
Author SHA1 Message Date
peter 3553eac8c4 feat(settings): upload a Google Takeout export in Settings › Account
A "YouTube history" section under YouTube access: the watch history and
the Watch later csv are dropped or picked (one request each, each file's
outcome said on its own), and the section then says what the import did
- only the counts it produced - or that it is running, failed or was cut
off by a restart. While it runs it asks again until it ends; a failed
re-read shows the failure, not the previous answer.

The drop zone the Files upload had inline is now ui/DropZone, used by
both. The e2e seed clears the account's kept Takeout videos.
2026-10-23 18:51:05 +02:00
peter dc4f4f5143 test(e2e): a plain member's shell is held end to end, and a page it cannot open lands on the feed
The suite ran only as an admin, so nothing end to end held what a member sees. A second seeded
account, a member with its own session, now walks every rail page (no admin entry, no admin call,
no error state) and follows links to the admin pages.

It found one: a member's link to an admin page showed the feed with no rail row current, and the
address bar kept naming the admin page — only the content fell back. The navigation state now
derives the page on screen through each module's gate (reachablePage), so the rail, the content,
the tab title and the address bar agree, on a link, a Back, and a role taken away mid-session.
2026-10-20 04:08:11 +02:00
peter d73cdcee97 fix(capture): every process registers every table before its first query
The models now live in their units' packages, and a core model names a unit's by
string (User.subscriptions -> Subscription). The download child imports neither
catalog nor app.tables before db.get, so mapper configuration failed and every
download, clip, edit and upload job died with child_exit. Each entry point now
imports app.tables, and a test imports each one in a clean interpreter.
2026-10-20 00:45:20 +02:00
peter 238e6f8a61 refactor(catalog): the unit's tables move into catalog/models
Channel, Subscription, Video, SearchFind, ExploredChannel and BlockedChannel (and LIVE_OR_UPCOMING)
leave app/models.py for app/catalog/models.py; app.tables registers them. The User↔Subscription
relationship names its other side as a type only. No schema change.
2026-10-19 02:03:14 +02:00
peter a35eece652 fix(e2e): the seed imports Reader's tables from reader/models 2026-10-19 01:33:32 +02:00
peter 24c47d12bd refactor(backend): select() and an inline now everywhere, held by a test
CLAUDE.md's canonical table names SQLAlchemy 2.0 select() and an inline
datetime.now(timezone.utc), and both had drifted: five db.query(...) calls
remained in routes/auth.py and routes/me.py (seven before the demo went),
and four private now helpers stood in sync/videos.py, utils.py (imported
as _now by explore and maintenance), scheduler.py (_now_iso) and the e2e
seed. All are the canonical form now.

Ruff cannot ban a method call on an instance or a function by name, so
tests/test_canonical_patterns.py reads every app module's AST and fails on
a .query(...) call or a now/_now/now_utc helper — seen red on a planted file.
2026-10-05 00:19:39 +02:00
peter 6d8b7be6d8 feat!: remove the shared demo account
The demo account — one shared user that whitelisted emails entered from
the login page, reset on a schedule — goes end to end (owner decision,
E34): demo.py and the reset job, users.is_demo and demo_whitelist
(migration 0098 deletes the demo user, its audit rows, the reset job's
override and notices, then drops the column and table; the downgrade
restores the empty schema), the /auth/demo entry, the admin whitelist and
reset, the Demo tab, the banner, the login page's "Try the demo", and
every is_demo branch in routes, userscope, scheduler and the UI.

require_human only ever refused the demo account, so its 87 uses become
current_user (owner-approved); require_write_scope keeps its own check.
The login page's "or" divider now shows only beside the Google button:
without the demo entry under it, an instance without Google would have
said "or" to nothing.

A contract test (removedFeatures) fails on the word anywhere in the
backend, frontend, e2e or scripts — seen red on a planted hit. Size
ceilings tightened for the modules that shrank; admin.py is back under
the limit.
2026-10-05 00:19:38 +02:00
peter 3c16012524 build(backend): close the lint gaps the E32 audit measured
ruff gains RUF100, PLW1510 and flake8-bugbear, every hit fixed rather than
ignored. B008 stays on with FastAPI's Depends/Query declared immutable.
basedpyright moves to standard mode, pytest to --strict-markers, and a hand
run of pytest now requires the database unless SIFTLODE_TEST_REQUIRE_DB=0.

PLW1510 found a real bug: the three ffprobe probes in plex/stream.py never
looked at the exit status, so a probe that failed was read as the file's
truth (an HDR source sent down the HW-safe path, a multi-audio file collapsed
to one track, a subtitle offset taken from garbage) and the warning each
except branch promises never fired. They now run with check=True.
2026-10-03 16:04:06 +02:00
peter b47d22cf3b chore(e2e): no committed default for the E2E account password
The localdev e2e account's password was a hard-coded default in this public repo, so it
was never a credential. E2E_PASSWORD is now required; the ops wrapper exports it from
the secret store, and the seed/setup fail with a clear message when it is missing.
2026-09-23 02:35:19 +02:00
peter 0cff964ff2 refactor(logs): name every logger after its module, and hold it there
25 literal names covered 34 modules. plex/client.py, plex/stream.py,
plex/subtitles.py, plex/sync.py and two Plex route modules all called themselves
`siftlode.plex`; downloads/pipeline.py called itself `siftlode.worker`;
routes/channels.py called itself `siftlode.api`. The `logger` column is in every
stored row so E22.6's console can filter on it, and so an operator can pin a level
to one part of the system — neither works when six modules share a name.

`logs.get_logger(__name__)` derives it: app/plex/sync.py -> siftlode.plex.sync.
One name per module, and it cannot drift from the file it sits in.
tests/test_logger_names.py pins the derivation, that everything stays under the
one `siftlode` tree the handler and the tests attach to, and that no module
hand-rolls a name again unless the line says why (`# logger-name-ok: <why>`).
Two do: get_logger's own call, and `siftlode.http`, which is named for the traffic
it carries rather than the module it lives in — it replaced uvicorn's access log
and is what an operator pins when they want request lines and nothing else.

app/scheduler.py called its logger `logger`; every other module calls it `log`.

ruff gains LOG, G, T20, TRY400, TRY401 and S112. Four findings cleared with them:
worker.py logged `str(exc)[:120]` where the traceback was the whole question, and
three prints/f-strings that are deliberate now say so on the line. S110 and BLE001
stay off until S3 — 23 handlers are outstanding and each is a judgement about one
of them; enabling the rules first would just mean 23 blanket noqas.
2026-09-18 00:57:57 +02:00
peter b1832afd18 style: apply ruff format across backend + scripts (line-length 100)
Mechanical, whole-tree `ruff format` pass — no behaviour change. Recorded in
.git-blame-ignore-revs (next commit) so git blame skips it.
2026-08-23 07:43:06 +02:00
peter 97c584733a fix(seed): mark instance configured in e2e_seed so a prod→dev reseed skips setup
`siftlode seed-dev-from-prod` TRUNCATEs app_state (excluded from the data
allow-list as local/operational state), which dropped localdev back into the
first-run "Setup needed" gate and blocked login/UAT. e2e_seed runs at the end
of both `seed` and `seed-dev-from-prod` and always leaves a loginable admin, so
it now also flips app_state.configured=true (idempotent; the running api re-reads
and promotes its one-way cache, no restart needed).
2026-08-08 01:01:16 +02:00
peter 833aee07d4 test(e2e): make the seeded account admin + cover admin nav pages
The E2E account is now admin (matching the real primary user), so the nav spec
also drives scheduler/config/users/audit — exercising the render ternary's admin
branches, which the upcoming PageShell refactor will restructure.
2026-07-18 02:52:54 +02:00
peter e7091158fd test(e2e): golden-master Playwright net for the Platform refactor
Locks the five flows the refactor must not break (feed, channel, player,
settings, nav) so later sprints can prove they changed nothing. Adds a thin
data-testid layer and a dedicated seeded non-demo account; the suite runs on
the baked :8080 stack (authoritative) or :5173. No product-behaviour change.
2026-07-18 00:35:57 +02:00