TODO.md (4448B)
1 # TODO 2 3 ## Mobile login is painful 4 5 The `SIMPLEFE_TOKEN` used for Basic Auth is a long random string (`secrets.token_urlsafe(32)`), 6 which is awkward to type on a mobile keyboard. 7 8 Deferred for later. Leaning towards a **short PIN + lockout**: swap the long token for a short 9 PIN (e.g. 6-8 digits, numeric keypad entry), paired with a basic lockout after N failed attempts 10 to compensate for the lower entropy -- reasonable trade-off given the threat model is "random 11 device on the home LAN," not internet-facing. Alternative considered and set aside for now: a 12 "remember this device" cookie, which keeps the full-strength token but only requires typing it 13 once per device -- doesn't weaken the credential, but reintroduces session/cookie machinery that 14 was deliberately skipped earlier in favor of stateless Basic Auth. 15 16 ### ~~Token was churning every redeploy, wiping other secrets too~~ (fixed) 17 18 Turned out to be compounding the above: `install.py`'s `write_env_file()` asked "overwrite 19 .env?" on every run where it already existed, and answering yes didn't just rotate 20 `SIMPLEFE_TOKEN` -- it wrote a **brand new `.env` with only that one line**, silently deleting 21 `JELLYFIN_TOKEN`/`PUSHOVER_TOKEN`/`PUSHOVER_USER` too. Routine redeploys during testing were 22 losing the login and all notification config every time. 23 24 Fixed: `write_env_file()` no longer prompts at all. It checks whether `SIMPLEFE_TOKEN=` is 25 already present in `.env` -- if so, the file is left completely untouched; if not (first-time 26 setup, or the line was manually removed), it appends a freshly generated token without 27 disturbing anything else already in the file. 28 29 ## ~~`/rip` can hang a second device while a rip is running~~ (fixed) 30 31 Reported: with the page open on a desktop, it wouldn't load on mobile at the same time. 32 33 Root cause: `rip_index()` (`GET /rip`) unconditionally called `makemkv.query_disc()`, which 34 shells out to `makemkvcon -r info` and talks to the physical optical drive directly. If a rip 35 was actively in progress and a second request hit `/rip` (not `/rip/status`), it tried to query 36 the *same busy drive* -- MakeMKV likely serializes/blocks that, hanging the second request. Not 37 a generic "can't handle concurrent connections" issue -- waitress serves multiple clients fine 38 (default 4-thread pool). 39 40 Fixed: `rip_index()` now checks `app.config["JOB"]["running"]` first and skips 41 `query_disc()`/`find_existing_rip()` entirely when a job is already active, just rendering the 42 "a rip is already in progress" branch without ever touching the drive. Covered by 43 `test_rip_index_skips_disc_query_while_job_running` in `tests/test_app.py`. 44 45 ## Feature: pick files from a directory and just run the encode step 46 47 For files that already exist on disk (ripped some other way, or re-encoding with different 48 settings) -- skip the whole rip pipeline and just run `encode.py` against chosen files directly. 49 50 Scoped, not yet built: select **multiple** files from a directory listing, encode them **one at 51 a time in sequence** (not concurrently -- shared hardware, and avoids fighting the existing rip 52 pipeline's own encode step for CPU/GPU). 53 54 Comparatively small lift, smaller than the original rebuild, similar in size to the `/setup` 55 page work -- `encode.py`'s `encode()` is already a fully standalone, fully tested function with 56 zero dependency on MakeMKV or the disc. New pieces needed: 57 58 - `GET /encode` -- list `.mkv` files in `settings.output_dir` (filter out ones already ending 59 `_x265.mkv`, same convention `.scripts/rip` already uses), render as a multi-select pick-list. 60 - Path-traversal validation on whatever file(s) get submitted -- `Path.resolve()` + containment 61 check against the allowed directory, not trusted as-is from the form. 62 - A new orchestration function (probably alongside `pipeline.run()`) that loops over the 63 selected files, calling `encode_module.encode()` on each in turn, updating the shared 64 `app.config["JOB"]` status per file as it works through the queue. Reusing the *same* status 65 dict/background-thread pattern as the rip pipeline also means an encode-only job can't start 66 while a rip is using the hardware, and vice versa, for free. 67 - A small new template; `status.html` likely needs no changes since it already just reflects 68 whatever's in the shared status dict regardless of which job populated it. 69 - Tests: file listing, path-traversal rejection, the "already running" guard -- similar scope to 70 the `/setup` tests.