<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>simplefe, branch HEAD</title>
<subtitle></subtitle>
<entry>
<id>8782aff9309305c22058f6222bc9fd972273cdbb</id>
<published>2026-07-19T20:56:06Z</published>
<updated>2026-07-19T20:56:06Z</updated>
<title>Update TODO: confirm PIN direction, scope the encode-only feature</title>
<link rel="alternate" type="text/html" href="commit/8782aff9309305c22058f6222bc9fd972273cdbb.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 8782aff9309305c22058f6222bc9fd972273cdbb
parent 7105732a28f5708f9a5860b68ff95984ea40522f
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Sun, 19 Jul 2026 15:56:06 -0500

Update TODO: confirm PIN direction, scope the encode-only feature

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;

</content>
</entry>
<entry>
<id>7105732a28f5708f9a5860b68ff95984ea40522f</id>
<published>2026-07-19T20:48:33Z</published>
<updated>2026-07-19T20:48:33Z</updated>
<title>Rebuild simplefe as a dedicated rip/encode app</title>
<link rel="alternate" type="text/html" href="commit/7105732a28f5708f9a5860b68ff95984ea40522f.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 7105732a28f5708f9a5860b68ff95984ea40522f
parent 1907d13e63d964f4d1de25f6e51938092863134f
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Sun, 19 Jul 2026 15:48:33 -0500

Rebuild simplefe as a dedicated rip/encode app

Replace the generic jobs.toml + pexpect script runner with a
purpose-built pipeline: query disc info via MakeMKV robot mode
(structured parsing instead of scraping human-readable text),
show it on a page with an editable movie name and a duplicate-rip
warning, run rip -&gt; encode -&gt; notify -&gt; eject as a background job
with a live status page. This fixes real gaps found testing on
actual hardware this week -- the old system only knew about one
of rip&#39;s two interactive prompts and would hang indefinitely on
the second one (duplicate-rip confirmation), and none of the
orchestration logic was testable without a real disc in a real
drive.

- settings.py: env-driven ffmpeg/hardware config, replacing hardcoded
  values in .scripts/encode and .scripts/rip
- makemkv.py: disc query/parsing (ported from the exact CINFO/TINFO
  fields already proven in .scripts/rip), duplicate-check via real
  parameterized SQL instead of hand-escaped strings, rip execution
- encode.py / notify.py: native reimplementations of
  .scripts/encode and .scripts/notify&#39;s logic
- pipeline.py: orchestrates the full run as a background thread with
  a shared status object, no task queue needed for a single-job tool
- app.py: GET /rip, POST /rip/start, GET /rip/status, plus a
  GET/POST /setup page for Jellyfin/Pushover credentials that
  applies immediately without a restart
- install.py: fixed target of /opt/simplefe regardless of where the
  script is run from, synced via stdlib shutil (rsync isn&#39;t
  guaranteed present on a fresh machine -- learned the hard way on
  ser6), and no longer destructively regenerates .env on every
  redeploy
- Mobile-friendly styling via a shared base template (missing
  viewport meta tag was causing the tiny-text rendering)
- auth.py and the Bearer/Basic enforce_auth wrapper are untouched

Co-Authored-By: Claude Sonnet 5 &lt;noreply@anthropic.com&gt;

</content>
</entry>
<entry>
<id>1907d13e63d964f4d1de25f6e51938092863134f</id>
<published>2026-07-17T15:12:02Z</published>
<updated>2026-07-17T15:12:02Z</updated>
<title>Load secrets from .env via python-dotenv</title>
<link rel="alternate" type="text/html" href="commit/1907d13e63d964f4d1de25f6e51938092863134f.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 1907d13e63d964f4d1de25f6e51938092863134f
parent ba532710a4d069f459d29c35855364192d21a5b5
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Fri, 17 Jul 2026 10:12:02 -0500

Load secrets from .env via python-dotenv

Closes the pre-exposure gate: hardcoded Jellyfin/Pushover secrets in .scripts/rip and .scripts/notify now read from environment variables, sourced from a gitignored project-root .env loaded at app startup.

</content>
</entry>
<entry>
<id>ba532710a4d069f459d29c35855364192d21a5b5</id>
<published>2026-07-17T14:06:14Z</published>
<updated>2026-07-17T14:06:14Z</updated>
<title>Add shared-token auth, gated on SIMPLEFE_TOKEN</title>
<link rel="alternate" type="text/html" href="commit/ba532710a4d069f459d29c35855364192d21a5b5.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit ba532710a4d069f459d29c35855364192d21a5b5
parent 711c3f2365611c33374430fd49a3efa62b299e73
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Fri, 17 Jul 2026 09:06:14 -0500

Add shared-token auth, gated on SIMPLEFE_TOKEN

Fails closed at startup if no token is configured, checks Authorization: Bearer &lt;token&gt; via timing-safe compare on every route except the health check.

</content>
</entry>
<entry>
<id>711c3f2365611c33374430fd49a3efa62b299e73</id>
<published>2026-07-17T00:15:01Z</published>
<updated>2026-07-17T00:15:01Z</updated>
<title>Add POST /jobs/&lt;name&gt;/run for the override path</title>
<link rel="alternate" type="text/html" href="commit/711c3f2365611c33374430fd49a3efa62b299e73.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 711c3f2365611c33374430fd49a3efa62b299e73
parent ac40eef32632dbd71c44d2f9b81b49301d274309
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Thu, 16 Jul 2026 19:15:01 -0500

Add POST /jobs/&lt;name&gt;/run for the override path

Wires run_job() into the app: looks up the job (reusing the
same 404 behavior as the GET route), then spawns it via
pexpect using the job&#39;s prompt pattern and a response supplied
in the request body. Response text is per-request data, not
config, since it&#39;s inherently different every call (e.g. rip&#39;s
movie name) - see jobs.toml.example for the prompt field shape.

Does not yet handle the &quot;no response, let the script&#39;s own
60s timeout apply&quot; case, or rip&#39;s second (duplicate-rip)
prompt - both deliberately deferred as separate next steps.

</content>
</entry>
<entry>
<id>ac40eef32632dbd71c44d2f9b81b49301d274309</id>
<published>2026-07-16T23:55:15Z</published>
<updated>2026-07-16T23:55:15Z</updated>
<title>updated pyproject.toml</title>
<link rel="alternate" type="text/html" href="commit/ac40eef32632dbd71c44d2f9b81b49301d274309.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit ac40eef32632dbd71c44d2f9b81b49301d274309
parent c7dc80d87532774b6e9f62ad0de8a70b2859cf21
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Thu, 16 Jul 2026 18:55:15 -0500

updated pyproject.toml

</content>
</entry>
<entry>
<id>c7dc80d87532774b6e9f62ad0de8a70b2859cf21</id>
<published>2026-07-16T23:54:16Z</published>
<updated>2026-07-16T23:54:16Z</updated>
<title>Add pexpect-based job runner, proven against a fake fixture</title>
<link rel="alternate" type="text/html" href="commit/c7dc80d87532774b6e9f62ad0de8a70b2859cf21.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit c7dc80d87532774b6e9f62ad0de8a70b2859cf21
parent 8daba43d772530360f9eea4b8e35a9caf2490c5c
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Thu, 16 Jul 2026 18:54:16 -0500

Add pexpect-based job runner, proven against a fake fixture

run_job() spawns a command, waits for a known prompt, sends a
response, then waits for the process to finish and returns its
output. Tested against tests/fixtures/fake_job.py rather than
the real .scripts/rip, since rip needs real hardware and isn&#39;t
part of this repo (see .gitignore).

</content>
</entry>
<entry>
<id>8daba43d772530360f9eea4b8e35a9caf2490c5c</id>
<published>2026-07-15T09:51:38Z</published>
<updated>2026-07-15T09:51:38Z</updated>
<title>Add GET /jobs/&lt;name&gt; with 404 for unknown jobs</title>
<link rel="alternate" type="text/html" href="commit/8daba43d772530360f9eea4b8e35a9caf2490c5c.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 8daba43d772530360f9eea4b8e35a9caf2490c5c
parent 589ab4c6b10d05f1e8da4ea989afd43854b3f9f0
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Wed, 15 Jul 2026 04:51:38 -0500

Add GET /jobs/&lt;name&gt; with 404 for unknown jobs

This is the lookup step the eventual job-triggering endpoint
will need anyway, split out on its own since it&#39;s read-only
(a safe HTTP method) and doesn&#39;t require pexpect at all.
Triggering a job will be a separate POST endpoint, since
running rip has real side effects and shouldn&#39;t be a GET.

</content>
</entry>
<entry>
<id>589ab4c6b10d05f1e8da4ea989afd43854b3f9f0</id>
<published>2026-07-14T10:10:07Z</published>
<updated>2026-07-14T10:10:07Z</updated>
<title>updated app.py</title>
<link rel="alternate" type="text/html" href="commit/589ab4c6b10d05f1e8da4ea989afd43854b3f9f0.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 589ab4c6b10d05f1e8da4ea989afd43854b3f9f0
parent 89cca4cc057c8e428afe7c245b2940261fa31302
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Tue, 14 Jul 2026 05:10:07 -0500

updated app.py

</content>
</entry>
<entry>
<id>89cca4cc057c8e428afe7c245b2940261fa31302</id>
<published>2026-07-14T10:05:08Z</published>
<updated>2026-07-14T10:05:08Z</updated>
<title>Wire jobs.toml into the app factory via dependency injection</title>
<link rel="alternate" type="text/html" href="commit/89cca4cc057c8e428afe7c245b2940261fa31302.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 89cca4cc057c8e428afe7c245b2940261fa31302
parent cccf96bcfbaa1769c6a9acaa996905a63b3e54a9
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Tue, 14 Jul 2026 05:05:08 -0500

Wire jobs.toml into the app factory via dependency injection

create_app accepts an optional jobs dict rather than always
loading jobs.toml itself, so tests can inject fake job data
directly (see tests/conftest.py) instead of depending on a
real config file on disk.  Falls back to the real load_jobs()
call when nothing is passed, so `flask run` is unaffected.

Adds GET /jobs as a minimal proof the injected/loaded data is
actually reachable from a running app.

</content>
</entry>
<entry>
<id>cccf96bcfbaa1769c6a9acaa996905a63b3e54a9</id>
<published>2026-07-13T16:48:46Z</published>
<updated>2026-07-13T16:48:46Z</updated>
<title>Add minimal flask app factory</title>
<link rel="alternate" type="text/html" href="commit/cccf96bcfbaa1769c6a9acaa996905a63b3e54a9.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit cccf96bcfbaa1769c6a9acaa996905a63b3e54a9
parent a0d7aa0cdb13a11984891c9eafcdc0e93ca567bf
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Mon, 13 Jul 2026 11:48:46 -0500

Add minimal flask app factory

Uses create_app() rather than a module-level app object so
tests can later spin up isolated instances with their own
config, instead of all sharing one app built at import time.
No routes beyond a placeholder yet.  jobs.toml isn&#39;t wired in here.

</content>
</entry>
<entry>
<id>a0d7aa0cdb13a11984891c9eafcdc0e93ca567bf</id>
<published>2026-07-13T12:18:52Z</published>
<updated>2026-07-13T12:18:52Z</updated>
<title>Validate that jobs.toml entries have a required path</title>
<link rel="alternate" type="text/html" href="commit/a0d7aa0cdb13a11984891c9eafcdc0e93ca567bf.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit a0d7aa0cdb13a11984891c9eafcdc0e93ca567bf
parent fd90f77510280e5d90d603402244cb402c058457
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Mon, 13 Jul 2026 07:18:52 -0500

Validate that jobs.toml entries have a required path

Raises a custom ConfigError, not a bare ValueError, so callers
can catch config problems specifically without also swallowing
unrelated errors. Tests assert on the specific exception type
and that the message names the offending job, not just that
some error occurred.

</content>
</entry>
<entry>
<id>fd90f77510280e5d90d603402244cb402c058457</id>
<published>2026-07-13T10:38:19Z</published>
<updated>2026-07-13T10:38:19Z</updated>
<title>updated git ignore</title>
<link rel="alternate" type="text/html" href="commit/fd90f77510280e5d90d603402244cb402c058457.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit fd90f77510280e5d90d603402244cb402c058457
parent 05eac1c2494e5941859d234fbfe12159408e139c
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Mon, 13 Jul 2026 05:38:19 -0500

updated git ignore

</content>
</entry>
<entry>
<id>05eac1c2494e5941859d234fbfe12159408e139c</id>
<published>2026-07-12T23:53:42Z</published>
<updated>2026-07-13T10:33:35Z</updated>
<title>Add jobs.toml to load config Parses [jobs.*] table from a TOML config file and returns it directly, keeping the on-disk &quot;jobs&quot; wrapper key as an implementation detail callers don&#39;t need to know about. Tested against fixture data via tmp_path rather than the real jobs.toml, which is user-specific and gitignored.</title>
<link rel="alternate" type="text/html" href="commit/05eac1c2494e5941859d234fbfe12159408e139c.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 05eac1c2494e5941859d234fbfe12159408e139c
parent 51d4b08de1da2cb3f65656ad5f0f217089c61a4f
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Sun, 12 Jul 2026 18:53:42 -0500

Add jobs.toml to load config
Parses [jobs.*] table from a TOML config file and returns it
directly, keeping the on-disk &quot;jobs&quot; wrapper key as an implementation
detail callers don&#39;t need to know about.
Tested against fixture data via tmp_path rather than the real
jobs.toml, which is user-specific and gitignored.

</content>
</entry>
<entry>
<id>51d4b08de1da2cb3f65656ad5f0f217089c61a4f</id>
<published>2026-07-12T12:41:33Z</published>
<updated>2026-07-12T12:41:33Z</updated>
<title>Scaffold src-layout Python package with pytest</title>
<link rel="alternate" type="text/html" href="commit/51d4b08de1da2cb3f65656ad5f0f217089c61a4f.html" />
<author>
<name>Chris Roberts</name>
<email>chris.roberts@learningunix.net</email>
</author>
<content>commit 51d4b08de1da2cb3f65656ad5f0f217089c61a4f
Author: Chris Roberts &lt;chris.roberts@learningunix.net&gt;
Date:   Sun, 12 Jul 2026 07:41:33 -0500

Scaffold src-layout Python package with pytest

Sets up pyproject.toml (PEP 621 metadata, setuptools backend)
and a src/ layout so tests only pass against the installed
package, not loose source files. Wires in pytest as an
optional &quot;test&quot; dependency group with one placeholder test.

</content>
</entry>
</feed>
