feat: add independent CLI launches and preserve shared video bookmarks
This commit is contained in:
@@ -43,12 +43,14 @@ The declared app executable stays running after preflight and loads `Contents/Fr
|
||||
.build/terebi 'https://youtu.be/jNQXAC9IVRw'
|
||||
.build/terebi --url 'https://www.youtube.com/watch?v=jNQXAC9IVRw'
|
||||
.build/terebi -- -video.mp4
|
||||
.build/terebi --new-instance
|
||||
.build/terebi --new-instance --url 'https://www.youtube.com/watch?v=jNQXAC9IVRw'
|
||||
.build/terebi --help
|
||||
```
|
||||
|
||||
One local file (including a local `file://` URL) or YouTube video URL is accepted. Paths are relative to the shell's current directory; quote spaces and URL query strings. `--` treats the following argument as a filename. With no arguments, the CLI opens or activates the player without replacing its media. To use `terebi` by name, add the build directory to your shell's PATH: `export PATH="$PWD/.build:$PATH"`.
|
||||
One local file (including a local `file://` URL) or YouTube video URL is accepted. Paths are relative to the shell's current directory; quote spaces and URL query strings. `--` treats the following argument as a filename. With no arguments, the CLI opens or activates the player without replacing its media. Put **`--new-instance` before the media arguments** to launch a separate player process/window instead: it works without media, with a local file or positional YouTube URL, and with `--url` or `--`. Existing playback stays untouched. To use `terebi` by name, add the build directory to your shell's PATH: `export PATH="$PWD/.build:$PATH"`.
|
||||
|
||||
The launcher finds `terebi.app` beside its own executable, following symlinks, and uses macOS application/URL delivery to reuse the running player and its existing window. Keep the executable and app bundle together; this is not a standalone distribution. Media opens use the same autoplay and saved-progress behavior as the UI. Relaunch an older running build once to load the new external-open handling.
|
||||
The launcher finds `terebi.app` beside its own executable, following symlinks. By default, macOS application/URL delivery reuses a running player and its existing window. With `--new-instance`, one fresh application launch carries any media URL as a startup argument, so it reaches that process without a second app launch or changing another player's media. Keep the executable and app bundle together; this is not a standalone distribution. Media opens use the same autoplay and saved-progress behavior as the UI. Instances share preferences and saved progress; each has independent playback and a window. Quit all running players after installing this update so every process uses the updated startup/history handling.
|
||||
|
||||
Help exits with status **0** without opening the GUI or loading FFmpeg. Invalid arguments, unsupported URLs, missing files, and directories report to stderr and exit **64**; unavailable FFmpeg libraries and application-launch failures exit **69**. A successful launch exits **0** immediately after macOS accepts the request, not when playback finishes. Later extraction/decoding failures appear in the player window; this is a GUI launcher, not a headless player or remote playback-control interface.
|
||||
|
||||
@@ -82,6 +84,8 @@ Reopening a local video or YouTube video automatically resumes its saved positio
|
||||
|
||||
Progress is stored locally in UserDefaults, checkpointed every five seconds of playback progress, and saved on pause, close, replacement, and normal quit. Only authoritative, acknowledged positions are saved—not an unfinished seek or slider drag. Finished videos clear their bookmark and reopen from the beginning; a bookmark beyond the current video's duration also restarts at the beginning. Failed opens preserve existing bookmarks. Live/non-seekable inputs and audio-only media do not record progress.
|
||||
|
||||
Each video's bookmark has its own UserDefaults key, so separate player processes cannot erase unrelated bookmarks by rewriting a cached history dictionary. Existing bookmarks migrate automatically from the old dictionary. If two instances play the same video, the most recent checkpoint wins; progress is shared, not a separate bookmark per window.
|
||||
|
||||
## Playback UI
|
||||
|
||||
The layout follows [IINA's video-first window](https://iina.io/img/screenshot.png) and the supplied screenshots: a 32-point frosted titlebar with native traffic lights and file icon, plus a dark frosted 440 × 68-point floating controller. AppKit materials blur the video beneath the chrome; muted icons, an orange volume track, a narrow seek thumb, and zero-padded time labels follow the reference. Shared vertical padding and an optical offset for symbol/slider insets balance the controller's visible top and bottom margins in both play and pause states. The controller starts centered with a four-percent bottom inset and keeps its relative position when the window changes size.
|
||||
@@ -152,15 +156,15 @@ Generates short fixtures and exercises the real playback interface and native ou
|
||||
|
||||
`make check-quality` runs deterministic policy checks with explicit monotonic clock values: downward/upward hysteresis, sparse available resolutions, the 1080p cap, cooldown, pause/seek/open/local/default-off suppression, recorded EOF, live/unknown reserve, frozen progress, and failure backoff. `make check-adaptive` exercises the actual app model with gated loopback 1080p/720p media and offline yt-dlp selection: default-off behavior, a sustained-stall downshift, healthy upshift, position preservation, current/default manual overrides, and opt-in persistence. The quality clock is injected; tests use isolated preferences and muted playback. Both targets are included in `make check`; the native adaptive portion is skipped if yt-dlp is absent. No external network is required.
|
||||
|
||||
`make check-progress` verifies persisted fractional positions, independent local/YouTube identities, symlink aliases, completion clearing, invalid-position rejection, and isolated preference domains. It is included in `make check` and does not touch real playback history.
|
||||
`make check-progress` verifies persisted fractional positions, independent local/YouTube identities, symlink aliases, completion clearing, invalid-position rejection, isolated preference domains, independently initialized writers preserving each other's videos, and migration of existing bookmarks. It is included in `make check` and does not touch real playback history.
|
||||
|
||||
`make check-cli` checks relative/escaped paths, option-like filenames, YouTube identity, argument cardinality, unknown options, missing files/directories, non-local file URLs, and unsupported stream URLs. It is included in `make check`.
|
||||
`make check-cli` checks relative/escaped paths, option-like filenames, YouTube identity, default reuse versus explicit `--new-instance`, independent empty/local/YouTube launches, help without launch, argument cardinality, unknown/duplicate options, missing files/directories, non-local file URLs, and unsupported stream URLs. It is included in `make check`.
|
||||
|
||||
`make check-runtime` exercises missing/invalid dependency metadata, unavailable ABI libraries, existing but unloadable dylibs, omitted library families, and successful native library loading using isolated app metadata. It is included in `make check`; no installed FFmpeg files are modified.
|
||||
|
||||
`make check-media` exercises actual playback through the app model, idempotent play/pause, paused seeking and start-boundary clamping, non-seekable state transitions, immediate hiding of a stale buffered snapshot on accepted close, and Now Playing cleanup on close/error/shutdown. It also checks default-off buffer readout, persisted opt-in/explicit disable, and retained-range independence from the display preference. It is included in `make check` and uses isolated playback history with muted audio. Native MediaPlayer command dispatch was also smoke-checked; physical media-key routing and macOS's choice among competing media apps still need a keyboard check in the running app.
|
||||
|
||||
`make check-startup` launches an isolated app bundle through LaunchServices using the production bootstrap and a check-only player library with actual native playback. It verifies that the playing process retains a real registered app PID and bundle identity—the prerequisite for third-party Now Playing discovery. It is included in `make check`; it does not require Vorssaint, show a playback window, or touch real playback history. Cold Finder/CLI opens, warm local/YouTube opens, saved-position restoration, same-window reuse, and Vorssaint's adapter discovery were additionally smoke-checked through the real GUI.
|
||||
`make check-startup` launches an isolated app bundle through LaunchServices using the production bootstrap and a check-only player library with actual native playback. It verifies the registered app PID/bundle identity, then runs the real CLI against the production SwiftUI scene and delegate: cold/default reuse, independent empty/local/YouTube launches, exactly one window per PID, media delivered only to the new process, original playback retained, help without launch, and warm default replacement. YouTube uses gated-capable loopback media and actual offline yt-dlp selection; that portion requires yt-dlp and a supported JS runtime but no external network. It is included in `make check`; windows are transparent, audio muted, and bundle/preferences isolated. It does not require Vorssaint or touch real playback history. Finder/Space behavior and Vorssaint discovery still need a check in the visible app.
|
||||
|
||||
Continuous audio uses resampled sample counts for its output timeline; packet timestamp quantization and rounded resampler delay must not insert silence or drop samples between blocks.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user