simplefe

Log | Files | Refs

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.