Skip to content

About

No description or website provided.

Topics

Resources

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

Mediaforce

Mediaforce reclaims space on a media library by re-encoding it to AV1 video and Opus audio with minimal, acceptable quality loss.

The Director's overall direction comes first, followed by this repository's DIRECTION.md. Those files set purpose, work order, and stop boundaries and take precedence over this README. AGENTS.md is the repository's agent-instruction entry point; this README describes the current implementation and how to use it.

Scope

  • Source roots: taken from checked-in defaults plus runtime settings
  • Staging root: taken from checked-in defaults plus runtime settings
  • Only configured library roots are scanned; a library path may not contain or sit inside the working (transcode) folder

The current checked-in defaults point at /Volumes/media/movies, /Volumes/media/tv, and /Volumes/media/transcode, but those are config defaults rather than product-level invariants.

Status

The current implementation covers:

  • discovery and inventory into SQLite, refreshed in the background
  • the Mac app: TV, Movies, and Other libraries, Activity, Finished, and Settings, talking to the local server's API
  • “Needs you” gathers every item counted by the Dock badge, with links to sample decisions, kind approvals, seasons, computers, and original-backup cleanup. Opening the app from the Dock, its waiting menu-bar notice, or a notification shows the list while anything waits
  • the Mac Media table fills the available window below its filters, with one scrollbar for its rows and a visible column header while browsing
  • per-file sample, encode, and machine validation across configured encode hosts, with work-window schedules
  • measured evidence (motion pattern, media fingerprint) checked automatically for files production is waiting on
  • side-by-side compare clips for sample review
  • automatic per-file checking and publishing under a current approval, with original-file archival under the transcode root, and user-approved cleanup of those rollback copies

Approving a live-action kind’s representative sample approves the kind across its shows. The kind keeps the watched sample as its evidence. Choose “Approve only this season” for a season-only decision. Live-action seasons can follow one approval for their kind of content. Older unapproved season samples stop asking separately when they finished before the kind was approved or before its newest sample was requested. Newer samples, uncertain timing, and existing folder approvals keep their own decision. While the kind waits for a choice, its newest sample stays available, even after that season finishes. A ready sample puts the kind in Needs you and opens its approval there. Seasons missing original measurements wait for measuring without a sample decision, even when an older sample is saved. After measuring, they follow their kind; measured seasons without a reusable kind keep their own sample decisions. A blocked measurement names its reason on the season and sample page; it does not ask for approval of the old sample. Saved sample files are retained. Finished production files are checked and published automatically one at a time under their current approval. Manual checking and publishing remain explicit overrides. Rollback copies stay until the user approves cleanup.

Remote computers reconnect missing shared storage in the background. After an uncertain connection, the same mount helper checks that computer again before retrying. It waits for a running connection request, clears an old unused lock, and refuses to mount beside a leftover folder. If it cannot prove that, it shows what it found and keeps checking. A laptop identified from its last-seen Mac model stays quiet while away; a desktop missing from the network for a long while notifies once. Other computers keep working, and returning computers rejoin automatically. Models are learned when a computer answers a check.

Runtime state

Runtime artifacts now live outside the repo by default:

  • durable state: ~/Library/Application Support/mediaforce/
  • disposable review clips: ~/Library/Caches/mediaforce/review/
  • runtime settings: ~/Library/Application Support/mediaforce/runtime-settings.json
  • learned memory artifacts: ~/Library/Application Support/mediaforce/learned-memory/

That keeps the repository focused on code and policy while allowing the local catalog, manifests, scan jobs, and calibration artifacts to survive repo moves or fresh clones.

Database schema changes are now managed through SQLAlchemy 2.x plus Alembic. Opening the app against a database will auto-apply Alembic migrations, and legacy pre-Alembic databases are normalized to the initial revision before later revisions run. Encode artifacts also persist richer telemetry now: source size and path at encode time, host and worker metadata, wall-clock encode duration, and append-only item events for encode start, completion, and failure.

For migration authoring and review workflow, see docs/development/database-tooling.md.

Transient calibration artifacts are also cleaned up automatically. By default, Mediaforce purges cached review clips that no kind approval still references, temporary calibration manifests, and /Volumes/media/transcode/_calibration/ scratch outputs after 14 days. While the server or CLI is in active use, it also retries that cleanup sweep at most once per hour so stale files get another chance to disappear if an earlier pass raced or only cleaned up partially. Run plans containing original source trait measurements are retained regardless of age or job status; unreadable or malformed run plans are also retained. The same sweep keeps only the newest two folders under the support directory's deployments/ (each holds a full database copy for rollback) and removes operations/ evidence folders whose files are older than 30 days. Age comes from the files inside a folder, not the folder's own modified time, which Finder changes just by opening it. Set kept_deploy_backups and operation_evidence_retention_days under [state.cleanup] to change these.

Completed calibration jobs also clean up their own temporary manifest and scratch encode directory right away after compare clips are generated, so only the review clips and saved calibration summary remain.

The Mac sample page shows the coverage saved with the sample beside its size facts and approval controls: how many files were tested and which seasons or picture formats were not. It uses that sample's record, even when today's remaining files differ. Older samples without recorded coverage say so. Every production file still passes its own quality and final-output checks.

Layout

  • CLI entry points: mediaforce, mediaforce-web
  • bin/mediaforce.py: Python entry point
  • config/defaults.toml: checked-in encode defaults and policy defaults
  • mediaforce/: internal Python package
  • macos/: the Mac app (SwiftUI). scripts/build-mac-app.sh test builds and tests it; it needs Xcode and xcodegen.
  • runtime state: stored under ~/Library/Application Support/mediaforce/ and ~/Library/Caches/mediaforce/review/

Commands

On macOS, Mediaforce now prefers Homebrew's ffmpeg-full and ffprobe from /opt/homebrew/opt/ffmpeg-full/bin when present so VMAF support survives PATH changes and normal formula upgrades. You can override either binary with MEDIAFORCE_FFMPEG or MEDIAFORCE_FFPROBE.

Library in the Mac sidebar combines TV, Movies and Other. Expand Library to browse TV or Movies on their own; Other appears when it contains media. Each uses the same storage summary and state shelves, opening the existing show, movie or folder page. TV's finished seasons, approved samples and biggest unstarted shows remain available in Library and TV. Browse all opens the complete Media list for the selected library, and a shelf heading opens that state. The separate Media sidebar entry shows everything. State, search and table/poster controls stay in Media, without a second library picker. All summaries show current size and how much remains original; the combined Library also links to total space saved. Storage totals and computer status remain visible when a list cannot load.

The sidebar keeps the last kinds received while retrying an unanswered refresh.

The compression list counts current file questions. Published files and files settled by a later run stop asking for attention; their saved compression-job history stays intact.

API reads only report persisted catalog and job state; opening or polling a page does not start scans, host probes, or media analysis. Catalog inventory refreshes in the background every media.catalog_refresh_hours (default 6; 0 turns it off). Save a library-path change in Settings or run mediaforce scan to reconcile sooner. A full scan also refreshes configured Plex and TMDB metadata; provider failures leave the last successful metadata in place and surface a warning instead of blocking the catalog scan.

Scans update inventory with ffprobe metadata only. Cadence and media fingerprint analysis is remembered as canonical evidence and refreshed through bounded evidence batches. A background worker runs unpaused batches, such as the checks production is waiting on; a batch prepared by hand starts paused, and a catalog scan never starts analysis. For a small folder pilot:

uv run mediaforce evidence start "tv/Futurama/Season 1" --limit 10
uv run mediaforce evidence status
uv run mediaforce evidence resume
uv run mediaforce evidence run --max-items 1

uv run mediaforce evidence restore-source reports historical original-trait recovery from retained run plans without reading original media. Add --apply to append eligible historical-probable records. Those advisory traits stay separate from current-file evidence and cannot grant approval or cleanup authority.

Repeat the final command to resume durable progress. evidence pause prevents the next claim, while evidence cancel terminates the active managed process and cancels the remainder. See docs/architecture/evidence-worker.md.

The TV library (/ or /folders) lists shows. Open a show for show-wide actions, or a season for season actions.

The Mac app's back button shows a chevron and the name of the page you came from, then returns to it, including Media, Space saved, a kind, or another show, season or sample. Changing the selection in the sidebar starts a new browsing path. Opening a page from a notification or the menu bar keeps a way back to the page already open in the window.

In the Mac app, a show's seasons follow the player and show choices, so loading the season list leaves the player in place. The file list highlights the file shown in the player, including the first file chosen when the page opens.

Folder calibration now uses a size-first review flow by default. The checked-in defaults aim for roughly 300 MB per 45-minute episode at up to 1080p, then use sampled metrics as guardrails and representative picture-and-sound clips as the user decision point. The comparison can open in a focused full-screen workspace with side-by-side and instant Original/New views, shared playback, and actual-size inspection. Technical encoding evidence remains under Details, and approval stays on the calm folder page. The first size note is measured before it becomes a ceiling; once a follow-up target lands above the band, the next sample draft carries the learned size ceiling forward instead of repeating the oversized run. The current fast sample engine is still ab-av1; scene-aware engine work is tracked separately so host orchestration and review workflow can stay stable while that bakeoff happens. When a sample re-encodes the audio, Mediaforce also checks the sound: a ViSQOL score against the original, plus loudness, dialogue, peak and clipping guards. ViSQOL runs from a local Docker image; build it once with docker build -t mediaforce-visqol:38d0b01 docker/visqol. See docs/architecture/sample-sound-check.md. Once a sample is approved, the show or season page offers the compress action (for example Compress the season), which queues the real encode from that approval rather than from an unsaved preview.

For this personal workflow, source-resolution 1080p AV1 around 200–300 MB per 45 minutes is an established user-approved baseline, including conventional and dark or stylized TV material. Direct user instructions and accepted visual samples outrank generic bitrate guidance; real sample evidence decides whether a particular folder needs adjustment.

video.max_crf is the initial quality-search range, not a hidden veto on an approved size goal. Size-directed tests may expand in measured steps up to video.target_search_max_crf (63 by default) while still enforcing the metric floor and requiring user review. Saved jobs created before that ceiling was recorded replay their original CRF range exactly; make a fresh test to use the new search contract.

For scene-aware engine research, generate a repeatable bakeoff plan from an existing manifest instead of replacing the production engine path directly:

uv run mediaforce bakeoff path/to/run-manifest.json --all \
  --output ~/Desktop/mediaforce-bakeoff.json

The bakeoff plan carries the same size-first defaults and per-item resolved policy used by the TV season page, then lays out candidate commands and tool requirements for the current ab-av1 path plus Av1an, Xav, and Auto-Boost. Use the plan to collect output size, runtime, selected CRF or quantizer, metric score, and review artifacts before choosing a production engine migration.

Run Mediaforce through the project entrypoint with uv:

uv run mediaforce report --limit 10

Inspect read-only quality-memory readiness, active observations, concurrent holdouts, and safety evidence without starting media work:

uv run mediaforce quality-memory
uv run mediaforce quality-memory --prefix "tv/Show/Season 1" --json

See which kind of content each season still waiting on a sample belongs to:

uv run mediaforce content-kinds

This read-only JSON report groups each waiting season by number ranges of its originals' measured traits: darker live action, bright live action, cartoon, 4K, mixed, or unmeasured. It reads only proven or probable original traits, so published episodes vote with the original they were made from, and output measurements never count. Seasons with a folder approval are only counted. Cartoons come from the series' TMDB genres.

Inspect a target-size default proposal using a current visual boundary observation ID from the configured database:

uv run mediaforce target-defaults cibo1_example

This read-only JSON report explains item, folder, and measured-content scope, evidence counts, confidence, and fallback. It does not apply a target or start media work. Exact-item Studio pages also expose this evidence when the saved review still matches the current source, calibration, intent and budget. Suggestions remain review-only; sample approvals do not establish promoted production evidence. See target-default rules for the evidence thresholds and required user confirmation.

Inspect the separately captured production lineage for an approved boundary:

uv run mediaforce target-production-evidence cibo1_example

This read-only report links the exact reviewed item to its validated, promoted output and explains current evidence eligibility. It never adopts a default or backfills historical promotions. Output identity uses sampled content bytes, size, and metadata, not a full-file digest. See the production lineage contract.

Record approvals that never reached the learning tables, rebuilt from the saved approval files:

uv run mediaforce backfill-approval-boundaries
uv run mediaforce backfill-approval-boundaries --apply

Without --apply it only reports what it would record and why each other approval is skipped. See backfill.

Run a sample scan:

uv run mediaforce scan --limit 25

Scan a specific show or folder:

uv run mediaforce scan \
  --prefix "tv/Futurama"

Inspect a folder and print a suggested override block:

uv run mediaforce inspect-folder "tv/Suits"

Start a folder campaign in one command:

uv run mediaforce campaign \
  "tv/Suits/Season 5"

The Mac app is the normal way to run Mediaforce. For a manual one-off or debugging run from the CLI, start a run instead:

uv run mediaforce run \
  "tv/Suits/Season 5" \
  --play

campaign will:

  • rescan that folder prefix
  • print the folder summary and suggested override block
  • write a run manifest for the matching items in that folder
  • print the first item plan in plain English

run will do the same setup work and then immediately:

  • encode item 0
  • validate item 0
  • render compare clips for harder/high-complexity parts of the source
  • optionally play the first compare clip
  • print the next approval step

After a campaign, the rest of the commands default to the latest manifest, so you do not need to paste the run path each time.

Review the first item from the latest run:

uv run mediaforce review --play

Approve the reviewed item from the latest run:

uv run mediaforce approve

Report the best current candidates:

uv run mediaforce report --limit 15

Generate a reviewable run manifest:

uv run mediaforce plan \
  --prefix "movies" \
  --limit 10

Run manifests are written under ~/Library/Application Support/mediaforce/runs/ by default and contain:

  • source file path
  • resolved policy for that file
  • recommendation bucket and score
  • staging output path under /Volumes/media/transcode
  • audio/subtitle summaries for review

Encode one or more items from a run manifest:

uv run mediaforce encode \
  --index 0

Encode every item from the latest manifest:

uv run mediaforce encode --all

Run machine validation against staged outputs:

uv run mediaforce validate \
  --index 0

Validate every staged item from the latest manifest:

uv run mediaforce validate --all

Inspect staged-output integrity for one explicit scope without changing media, runtime state, or database rows. Add --details to perform bounded discovery of untracked and temporary files under configured staging roots:

uv run mediaforce staged-integrity "tv/Show/Season 1" --details

Mediaforce checks finished production files and publishes each passing file automatically, retaining the original in the Cleanup folder. Temporary holds retry; unknown failures retry with a delay before pausing for the manual check or publish override. Changed approvals, failed checks, and file conflicts stay with that file. Reasons appear in its staged-integrity details. Sample outputs never publish automatically. Older outputs without an unambiguous recorded production origin use the manual override, so sample files cannot be mistaken for full replacements.

Manually publish a checked file as an explicit override:

uv run mediaforce promote \
  --index 0

Manually try every checked file from the latest manifest:

uv run mediaforce promote --all

Promotion is decided file by file, for TV seasons and shows as for movies and exact files. An episode publishes when its staged output is locally available, unchanged, validated, made under an approved policy (the current approval or the approval its run recorded), not being written by an active encode, and not in conflict with an existing library file. Every other episode waits and is reported with its reason; it never holds back the rest. Promotion stops for the whole scope only when Mediaforce cannot judge files safely: an incomplete integrity report, an unavailable policy check, or an active encode whose files cannot be read.

Generate side-by-side approval clips from the source and staged outputs:

uv run mediaforce compare \
  --index 0

Generate review clips for all items from the latest manifest:

uv run mediaforce compare --all --play

Without explicit timestamps, compare now tries to pick scene-change moments from the source automatically and falls back to evenly spaced review points if scene analysis does not yield useful candidates.

For episodes short enough to scan, it tries high-complexity moments before scene changes. You can override the choice with explicit timestamps, for example:

uv run mediaforce compare \
  ~/Library/Application\ Support/mediaforce/runs/run-abc123.json \
  --index 0 \
  --timestamp 120 \
  --timestamp 640 \
  --timestamp 1100 \
  --play

Policy model

Source checkouts use config/defaults.toml for runtime encode defaults. Installed packages use the separate install-safe resource mediaforce/package_defaults/defaults.toml; see Package Builds. Machine-specific libraries, transcode roots, and remote hosts should live in runtime settings instead of repo-tracked config. Mediaforce resolves settings in this order:

  1. Global defaults
  2. Matching per-folder overrides from config/folder-defaults.toml in declaration order
  3. Matching local folder overrides saved into ~/Library/Application Support/mediaforce/runtime-settings.json
  4. Runtime environment overrides from ~/Library/Application Support/mediaforce/runtime-settings.json

Codec and quality recommendations still rank and label rather than silently excluding media. TV lifecycle policy is a separate eligibility gate: protected seasons remain visible with their hold reason, do not enter automatic manifests, and can only be bypassed through an explicit season-level confirmation. The underlying lifecycle status and the existing runnable queue order do not change.

The checked-in video defaults are intentionally tuned to the user's taste, not a near-transparent archival preset. The baseline AV1 policy uses a size-first review model: 300 MB per 45-minute episode, VMAF 85 with an 80 floor as a guardrail, and max 1080p output unless the user explicitly asks for another resolution. Raise the metric floors or add a folder override when a class needs a more conservative pass; use an explicit scale request when downsampling is desired.

report, encode, and validate all surface source-vs-staged size deltas so you can see the storage win before promotion.

Folder defaults

config/folder-defaults.toml holds shared per-folder starting points and ships with one sample block (tv/Suits). Shows normally follow the global defaults and the Director's approval; do not pin per-show or per-season tuning there.

Bench-approved drafts are saved locally in runtime settings so future runs on that machine can reuse them without changing the tracked repo defaults.

Plex posters are requested at display-sized dimensions to avoid downloading full-size artwork.

Use the Mac app's Settings for ordered typed library roots, the transcode folder, remote host definitions, the Plex server URL, and Plex-to-Mediaforce path mappings so those environment details stay off the checked-in repo. Library labels are editable while root IDs remain stable. TV, Movies, and Other can run in Production; 3D/VR stays Browse only until its workflow and safety plans land. Changing an existing type requires an explicit compatibility preview and never moves media files. See docs/architecture/typed-library-settings.md for the durable config contract.

Library lifecycle policy

TV series use a per-series current-season mode:

  • Auto protects the highest positive numbered season while cached TMDB status says the series is active. Missing or stale status is treated conservatively.
  • On protects the highest numbered season regardless of provider status.
  • Off disables current-season protection for that series.

Specials and Season 0 never identify the current season. A protected current season releases when a higher numbered season appears or after 365 days without a newly added or replaced episode. Independently, every season waits 30 days after its newest addition or replacement before automatic encoding. A manual override applies only to the exact season the user confirms. An explicitly selected episode may use that same parent-season override while its manifest remains bounded to that one file. The resulting manifest records both the hold reasons and the override.

Eligible media is ranked by Plex addedAt, oldest first. When Plex age is not available, Mediaforce records and uses its own discovery timestamp, then the filesystem modification time as the final fallback. Selection provenance is written into the run manifest so retries and recovery do not silently recompute membership under newer policy or metadata.

Plex and TMDB credentials are never written through the Settings UI. Set them in the launch environment instead:

  • MEDIAFORCE_PLEX_TOKEN: Plex server token
  • MEDIAFORCE_TMDB_TOKEN: TMDB API read-access token

The configured SSH account and media paths are unrelated to these credentials. Plex path mappings translate the paths reported by Plex to the corresponding Mediaforce source roots using exact root boundaries. See docs/architecture/library-lifecycle-policy.md for the durable data and selection contract.

mediaforce-web reads optional startup defaults from the repo-local .env. Use that file for machine-specific server launcher settings like bind address, port, and reload mode. A checked-in template lives at .env.example. Startup precedence is explicit CLI arguments, then shell environment variables, then .env, then built-in defaults. Prefer the MEDIAFORCE_WEB_* variable names for local defaults.

The macOS launch item is a long-running user service, not a development reloader. Give it an explicit --no-reload argument even when .env enables reload for an active development session; otherwise Uvicorn StatReload continuously walks the checkout and consumes CPU while the app appears idle.

Manage the local macOS login item through the Mediaforce CLI:

uv run mediaforce service install
uv run mediaforce service enable
uv run mediaforce service status
uv run mediaforce service logs --stderr
uv run mediaforce service disable

The generated ~/Library/LaunchAgents/com.mediaforce.web.plist invokes the repo-local .venv/bin/mediaforce-web entrypoint directly, not uv, and always passes --no-reload. It has no /Volumes dependency and does not mount storage; the running app remains the only owner of SMB recovery. Durable stdout and stderr logs live under ~/Library/Logs/mediaforce/. Use restart to regenerate and reload the item after moving the checkout or recreating .venv, and use uninstall to disable it and remove the plist. See docs/development/macos-login-item.md for verification and raw launchctl fallbacks.

Local server development

FastAPI serves the API the Mac app uses, plus review media.

For local server work on macOS and Linux, prepare this checkout with uv sync --locked. Cleanup also needs ps (included on macOS; install procps on minimal Linux systems). Then use scripts/mediaforce-dev.sh with start|stop|restart|status|smoke, for example scripts/mediaforce-dev.sh restart backend. Commands read the repo-local .env. Start reuses an existing owner; use Restart to apply changes to its host, port or configuration.

Concurrent Start commands wait for their competing launchers to exit before reporting the shared owner. Once every Start reports that owner, Stop cannot leave a competing launcher that starts another server.

If Start reaches its listening deadline, it reports failure while the server continues starting under the same watchdog. Use Status to check for readiness or Stop to cancel startup; a crash still triggers cleanup. Startup work is not terminated by the command's deadline. Bind checks use the configured host's usable resolved addresses, including IPv6 loopback and wildcard addresses.

The server has a launcher and a watchdog that owns the server from its first instruction, in a private session and process group. Stop asks that owner to send TERM to the group, then KILL if necessary. The watchdog keeps the leader unreaped until it has exited and consecutive observations find no other group member, then reaps the leader and releases the component lock. It never queries or signals the released group number again, even if completion logging fails. A server crash also triggers cleanup of its orphaned workers. If the command-facing launcher is killed, a private connection closes and the watchdog performs the same cleanup. Start cannot replace a component while its watchdog still holds the lock. Cleanup errors retain that owner. Even if process inventory is unavailable, the watchdog still escalates TERM to KILL for its reserved group; it reports each changed cleanup error once instead of repeating it on every retry. Missing inventory does not prevent Start. Restore ps or resolve the reported inventory error so the watchdog can verify completion and release the lock, then retry Stop. Until verification succeeds, Status stays stopping even when the server and workers have exited. Status distinguishes starting, running, stopping, a failed watchdog and an unknown control-channel response. An unknown response cannot authorize a new server or prove that cleanup finished. A failed watchdog blocks new starts and reports its failure rather than signalling processes discovered from saved PIDs. If the watchdog itself is killed, use the OS process manager to force-quit that component's remaining server, workers and launcher before starting it again. Deleting a lock file or using old PID records does not recover custody. A failed control connection leaves both running and failed owners intact.

Control sockets, locks and component logs live outside the repository at ~/Library/Application Support/mediaforce/development/<checkout hash>/. MEDIAFORCE_DEV_STATE_DIR can select another state directory, including isolated fixtures. The hash uses the physical checkout path, so invoking the helper through a checkout symlink reaches the same owner. Stop uses the prepared Python interpreter and this standalone helper; it does not import the app or run uv sync while cleaning up.

The helper manages only servers it launched. It does not adopt foreground servers, inspect another checkout's processes, unload login items, or change the shared backend runtime lock. An occupied port refuses startup. To move from an older development launch, end it in its original terminal first; if that backend belongs to the login item, use the existing uv run mediaforce service stop command from its service checkout when intentionally switching to dev work. Then start the new launcher. Old PID records and cleanup markers remain untouched and are not authority for signalling or starting a replacement.

The group contract is specific to these dev commands: the backend (including reload children) was checked on both macOS and Linux and stayed in their group's session. A component that deliberately detaches with setsid or setpgid needs OS service-manager containment instead. Production worker supervision keeps its separate native identity and containment rules.

The backend also holds a Python-level singleton lock while running, so a second mediaforce-web process exits instead of binding another port and confusing the local session. Busy startup reports the active owner PID and bind address when that metadata is available. scripts/mediaforce-web-dev.sh remains as a compatibility alias for backend-only actions.

To enforce the local acceptance gate before each commit, point Git at the checked-in hooks once per clone:

git config core.hooksPath .githooks

That pre-commit hook runs scripts/pre-commit-check.sh for CLI smoke. Select focused local tests through the testing guide; the complete backend suite, Mac app tests and macOS custody tests run in CI before ready or merge.

See Local server development for server startup.

Host configuration is now unified too: Mediaforce no longer injects a special synthetic local host. If you want the current machine to participate in sample or encode-host decisions, add it as a normal SSH host entry such as cbusillo@localhost, then set its priority and capabilities in Settings like any other host.

Each host can now declare its own max_parallel_encodes limit and pick a structured schedule instead of typing profile keys by hand. Always is the built-in default, and you can add named windows when a machine should only run during certain hours, on specific days of the week, or all day on explicit exception days such as Sunday. Never is also built in for temporarily disabling queued encodes on a host without removing its capabilities or setup state. Those windows are evaluated in the local time of the host that is actually running the work. Host probes retain an IANA timezone when the operating system exposes one, use fixed UTC offsets only as a compatibility fallback, and publish exact UTC close and next-open transitions for runtime enforcement and user surfaces.

Work windows can instead allow the current file to finish. Enable that close behavior in Settings → Computers → Work windows. A file admitted before closing finishes its encode, checks and normal publication; no next file starts until the window opens again. This avoids throwing away a partial encode on a computer used for other work during the day. Duration estimates still hold back files unlikely to fit before closing; the option protects a running file when that estimate was too short. Legacy multi-file jobs retain their hard stop; new queue work is one file per job. Manual Bypass scheduler requests still explicitly override the work window.

Bounded host schedules are hard execution windows. A non-bypassed quality search or episode encode receives the selected host's absolute UTC close deadline; controller-side cancellation and a host-side watchdog stop encode CPU at that boundary even if SSH or the server process disappears. Mediaforce discards that episode's partial output and returns it directly to the queue without consuming a failure attempt or applying host cooldown. Completed episodes stay complete, and the interrupted episode restarts from the beginning when a compatible window opens. Mediaforce does not create resumable media or phase checkpoints. Bypass scheduler intentionally omits the deadline.

Before starting non-bypassed work on a bounded host, Mediaforce now estimates the episode's quality-search and full-encode time from its source duration and recent successful runs on that host. Sparse history receives a larger safety margin. If the oldest queued episode cannot safely finish before any compatible host closes, the queue may choose the fitting episode that leaves the least unused window time; normal FIFO order resumes whenever the oldest episode fits. When no episode fits, the host drains without consuming attempts, and an episode that exceeds every compatible configured window receives an actionable waiting reason. The hard close deadline remains the correctness backstop when an estimate is wrong, and Bypass scheduler skips duration admission entirely.

Activity and the show and season pages present those schedule outcomes directly. Worker rows show exact host-local open/close transitions, active episodes show their hard stop time, and draining is distinct from off-schedule or unavailable. An episode stopped at close is labeled Paused until the next work window with its automatic whole-episode restart expectation, while an explicit bypass is labeled Not limited by the work window and a job that cannot fit any configured window links to the work-window settings that need attention. A waiting reason that quotes a runtime estimate names the computer whose estimate it used.

A computer the queue holds out after repeated failures shows Cooling down with when Mediaforce tries it again; Prepare clears the hold at once. Time estimates stay blank until one exists: quality search and an unmeasured job never show 0s. Queue and season estimates cover only work that will run on its own and say how many items they leave out because they wait for you or did not finish.

Transient SSH transport failures, including a remote host reboot or an OpenSSH closed by remote host disconnect, enter bounded retry backoff instead of requiring a new encode request. Mediaforce preserves unverified remote artifacts while the host is unreachable, removes the interrupted partial output once the host is available again, and then requeues the same episode from the beginning. It does not resume a partial media stream or promote interrupted output. A copy whose automatic checking or publishing stops after its retries, or whose files could not be restored after a publishing failure, appears as waiting for you on the Mac item page. It names the publishing problem while other files continue independently. A measured quality-floor conflict within the automatic allowance retries that one file with an item-local size exception; other deterministic encode and policy failures stop and ask about that file.

Starting a folder again while some of its episodes are still queued, retrying, or encoding never removes that work. Only the episodes that ended are retried, inside the same folder encode. An episode that missed its approved final size, or any ended episode when the approved settings changed after the folder was queued, waits and is planned again with the current settings once the rest of the folder has finished. A folder is never planned twice while any overlapping encode, such as a show-wide one, still has parts of it queued or running.

For a blank remote Mac, first turn on Remote Login so SSH answers. Once that is reachable, Settings can finish setup: if the host only needs first-time trust, enter the remote account password once so Mediaforce can install this Mac's SSH public key, then let the prep step create remote paths and install ffmpeg-full plus ab-av1 for sample_calibration hosts when possible. Those sample hosts now verify libvmaf/xpsnr metric support and libsvtav1 before they show as ready. The controller's automatic background recovery reconnects required SMB storage with a saved share mapping through the macOS NetFS API with UIOption=NoUI, using existing Keychain credentials without reading, storing, or transporting passwords. Its background recovery runs even when processing is paused or the work window is closed; reconnecting storage does not unpause work. Clean connection failures retry with bounded backoff; missing mappings and failures that cannot be retried safely, including ambiguous timeouts, wait for user attention. Completed attempts whose share has not appeared yet retry too; a service restart retains backoff and incident state rather than discarding them. Remote Macs also receive background status checks and no-dialog storage recovery without waiting for queued work; that recovery runs in the remote desktop session so it can use the password saved in the login Keychain, and a missing password or login is named in plain words for that computer. The warning below the navigation names unavailable controller storage immediately and computers still unavailable after five minutes (or requiring attention). It stays visible until fresh checks succeed. This feature does not configure external notification delivery. Fresh checks must verify the expected mount path, share identity, and directory access before work starts. See controller storage recovery for the runtime contract and installed acceptance procedure.

While a required controller share is healthy, Mediaforce learns a password-free mount mapping into ~/Library/Application Support/mediaforce/controller-smb-mounts.json. Status reads use that machine-local mapping instead of probing or mutating mount state. For first bootstrap, a private controller_smb_mounts list in runtime settings may supply the same source and /Volumes/... mount_point fields until a healthy mount can be observed and learned.

Remote mounted-media macOS hosts reconnect through Finder before preparation, sampling, or encode dispatch. Explicit Prepare on the controller also uses Finder. These Finder paths require a signed-in console user with the share password saved in the login Keychain. Repeated automatic failures use a bounded cooldown. After a missing desktop session, sign in on that computer and use Prepare to retry; on the controller, attempts remain suppressed until its console login session changes. Each computer keeps at most one Finder request per share: a request Finder has not answered within the attempt, usually because a dialog is open, is left running rather than ended, since ending it would not close the dialog. Later attempts, explicit or automatic, report that request instead of opening another dialog until someone answers or cancels it on that Mac. A failed connection says what Finder or the helper reported: a sign-in failure, an unreachable server, a cancelled dialog, a missing desktop session, a share connected under a suffixed name such as /Volumes/media-1, a helper that could not start, or a timeout with no reason given. Only a sign-in failure asks for one manual Finder connection with the password saved to Keychain.

Sampled calibration and AI note tuning can now run on any configured host with the sample_calibration capability. The folder page uses one AI-guided sample note box instead of separate baseline/tuning actions, lets the user choose the sample host, and still keeps the compress action hostless so the encode queue can dispatch it automatically. For mounted-media remote sample hosts, source and encoded review excerpts are rendered where the selected host can read the media, then copied back as small browser-ready clips; the controller does not need the full source mount for that review path. Runtime settings now carry remote host priority, per-host queue capabilities, explicit schedule selections, and a per-job Bypass scheduler escape hatch for urgent runs.

Each note-driven tuning attempt is now recorded in SQLite, and successful cross-folder learnings are promoted to markdown artifacts under the learned memory directory so future tuning requests can retrieve concise prior guidance.

The current starter profile includes tv/Suits, because it is a high-value AV1 target: large 1080p H.264 episodes with DTS 5.1 audio and low grain.

Promotion moves the original source into /Volumes/media/transcode/_replaced before replacing it with the staged .mkv, which keeps rollback straightforward without leaving the active library in an ambiguous state.

The Mac app's Activity page puts every computer on the same next-24-hours clock. Shaded stretches show work windows; current and estimated files sit on those stretches. Each working computer shows progress and a finish estimate when one is known. The numbered line-up names the file, estimated start and computer; unknown starts remain listed. These forecasts use the existing computer history and window rules, and may change as computers become free. Activity reads its forecast only while the page is open. The next approved season is named below them. “Held back for you” uses the same waiting items and links as Needs you, including original-backup cleanup.

Past attempts still marked as held remain available below the current decisions; Activity does not clear those records or count them as new decisions.

About

No description or website provided.

Topics

Resources

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages