Files
QueueCube/ios/SHARE_EXTENSION.md
T
buzzert 8723955705 ios: add share extension to queue and play video links
Embed an iOS and Catalyst share extension using existing discovery and player APIs. Share saved players through an App Group snapshot, preserve the queue, and verify advancing playback before reporting success.

Cover URL provider transport, settings migration, discovery addresses, duplicate handling, cancellation, and partial enqueue/play outcomes with focused tests. Document authentication and signing limits.
2026-10-20 22:08:12 -07:00

6.2 KiB

Share a video to QueueCube

Open QueueCube once after updating to make saved players available to the share extension. In Safari or a video app, share one HTTP(S) link, choose QueueCube, select a saved or nearby player, and tap Queue & Play. The link is appended and played immediately; existing queue entries remain. A successful request shows the selected player and a Done button. Video support depends on the player's existing mpv/yt-dlp support.

Nearby players use the same _queuecube._tcp. Bonjour discovery as Add Server. Resolved IPv4 interface suffixes are removed; scoped IPv6 addresses remain valid. Allow local network access in the containing app first. Players can also be configured manually in QueueCube Settings. The extension handles URL attachments (including UTF-8 data and the absolute-URL property-list form observed from UIKit) and plain text containing one link. URL data keeps the same single-link validation; property lists with a nonempty base or properties dictionary and unrecognized binary forms are rejected. Multiple different links, files, and links with embedded user/password information are rejected.

Data and permissions

The app and net.buzzert.QueueCube.Share extension share group.net.buzzert.QueueCube. The app retains its original UserDefaults and publishes a single versioned settings snapshot on read/save. The extension reads that snapshot without changing the app's selected player or server list. No queued link, credentials store, background transfer, or keychain group is added. The extension has Bonjour/local network declarations and the same HTTP transport compatibility as the existing app; Catalyst adds only app sandbox and outgoing network access.

The repository has no application authentication, login flow, or token store. Requests reuse the existing URLSession client and configured server URLs. Browser login cookies and credentials cached in another process are not shared. Servers behind an authenticated reverse proxy may fail; this change does not introduce a new authentication protocol or bypass access controls.

Existing API sequence and failure behavior

All paths below are relative to the configured API base URL (usually /api). No backend deployment is required.

Request Purpose
GET /playlist Snapshot existing mpv item IDs before append.
POST /playlist with {"url":"https://…"} Append once using the existing append-play operation. Starts only if idle.
GET /playlist Poll for exactly one new ID matching the URL, then obtain its current zero-based index.
POST /skip/{index} Select the new entry while retaining the queue.
POST /play Clear pause, including on an already paused player.
GET /nowplaying Confirm the new ID is current, is not paused, has no reported playback error, and its playback position advances across reads.

The current server may acknowledge mutations before mpv finishes and may return HTTP success even for an mpv failure. The extension verifies reads instead of assuming a 2xx response means playback started. A selected but still loading item does not count as success; playback position must advance for the intended item. Verification polling checks a shared 20-second deadline between operations; each HTTP request has an 8-second timeout. In-flight or sequential mutation requests can extend completion beyond that window. Slow media may remain queued without confirmed playback. This is a point-in-time confirmation; media loading or decoding can fail later.

If preflight fails, no link has been added and the user can retry. Once append has been attempted, any failure is treated as potentially partially completed: the UI says either the link may already be queued or it was queued but playback was not confirmed. It disables repeat submission for that share session and asks the user to check QueueCube before sharing again. It never clears the queue, rolls back another client's changes, or automatically retries a mutation.

Identical URLs already in the queue are distinguished using mpv IDs. Concurrent identical new entries are ambiguous and are not selected. The existing skip API uses an index, so another client can reorder the queue between lookup and skip; verification catches a different resulting item, but cannot make these existing endpoints atomic. No new backend contract is assumed.

Building and validation

Build the QueueCube scheme to embed the extension on iOS and Mac Catalyst:

xcodebuild -project ios/QueueCube.xcodeproj -scheme QueueCube -configuration Debug -destination 'generic/platform=iOS Simulator' -derivedDataPath /tmp/queuecube-build-simulator CODE_SIGNING_ALLOWED=NO build
xcodebuild -project ios/QueueCube.xcodeproj -scheme QueueCube -configuration Debug -destination 'platform=macOS,variant=Mac Catalyst' -derivedDataPath /tmp/queuecube-build-catalyst CODE_SIGNING_ALLOWED=NO build
swift test --package-path ios --scratch-path /tmp/queuecube-share-tests

Unsigned builds validate compilation and embedding; they do not validate App Group provisioning. For simulator UI tests of saved players, use normal local ad-hoc signing (CODE_SIGNING_ALLOWED=YES CODE_SIGN_IDENTITY=- DEVELOPMENT_TEAM=) so Simulator registers the App Group container. This does not use an Apple signing identity or provision a physical device. Before a future signed distribution, register the extension ID and associate both app IDs with the App Group, then update their profiles through the existing signing workflow. Fastlane maps QueueCube AppStore CI and QueueCube Share AppStore CI separately and keeps app/extension versions synchronized. Do not apply a single global bundle ID or provisioning profile override to both targets.

For manual UI validation, use a dedicated simulator or test installation: configure a test player in QueueCube, share a URL from Safari, verify saved and nearby player selection, Queue & Play success, cancellation, invalid text, and an offline/partially failing player. Confirm the old queue is retained and the new item is current and unpaused. On macOS also confirm QueueCube appears in Share extensions and can be invoked from a host app. Record signed-device and live-player gaps explicitly; unit mocks cannot establish those behaviors.