task 50: one-button publish the local recording (creator ruling 2026-09-28)

This commit is contained in:
2026-09-28 12:04:55 -07:00
parent 967df83925
commit 3637950df5
2 changed files with 102 additions and 0 deletions
+1
View File
@@ -65,6 +65,7 @@
| 47 | Alert box video: built-in/custom alert clip (+ six-animation fallback) with read-time fade in/out + ticker (now in the preview too, 3 display methods) + alert volume in the live mix | ✅ Done (2026-09-26) | [`TASKS/task-47-alert-videos.md`](TASKS/task-47-alert-videos.md) |
| 48 | Distribution & packaging: **MSIX + Store IAP** | 🔶 In progress — **✅ route decided 2026-09-27 (Store MSIX + Store IAP)**; **item 1 (bundle ffmpeg) SHIPPED 2026-09-28** | [`TASKS/task-48-distribution-msix.md`](TASKS/task-48-distribution-msix.md) |
| 49 | Chat profanity filter (local, opt-in, non-persistent, user word list) | ☐ Queued (2026-09-27) | [`TASKS/task-49-chat-profanity-filter.md`](TASKS/task-49-chat-profanity-filter.md) |
| 50 | One-button "Publish this video" (resumable upload of the local recording) | ☐ Queued (2026-09-28) | [`TASKS/task-50-publish-recording.md`](TASKS/task-50-publish-recording.md) |
---
+101
View File
@@ -0,0 +1,101 @@
# TASK 50 — One-button "Publish this video" (resumable upload of the local recording)
> Catalog: [`TASKS.md`](../TASKS.md) — status and requirements live here.
> Carved 2026-09-28 from a creator ruling: *"For me, as a creator, I'd love a one-button
> publish this video feature. Who wouldn't?"*
**Status:** ☐ Queued — creator-ruled, not yet scoped in detail
**Goal:** turn the local REC-side MP4 into a channel video with one button, and stop relying on
YouTube's silent automatic archiving to get a recording onto the channel at all.
**Size:** S–M. Media already exists; the work is a resumable-transfer lifecycle. **Not a quarter.**
---
## Why
Two reasons, and the second is the one that actually justifies it.
**1. It fixes a defect the creator already reported as broken.** On 2026-09-26 the creator could
not get private test recordings saved to YouTube, and used the local recording as the proof of
concept instead. The mechanism is now understood (§ Mechanism below): the archive *is* created,
the app just offers no way to see or reach it. A deliberate upload is the actual fix; the current
behaviour is a side-effect nobody designed.
**2. It is not the heavy feature it looks like.** The instinct is to call upload a big risky
addition because it moves bytes. It doesn't: ffmpeg already moves the media, and the recording
already exists as a finished local MP4. The genuine new work is a **discrete long-running job
with a lifecycle** — resumable, with progress, retry, cancel and quota. RTMP has none of that
because the connection belongs to the encoder process and simply reconnects. Different protocol,
different failure model. That is the whole cost.
**Creator ruling, verbatim:** one button. Not a media manager, not a playlist tool.
---
## Mechanism — why recordings already reach YouTube, and why that's a problem
Verified 2026-09-28 against the code:
- `enableAutomaticArchiving` is **never set** by the app. YouTube's default for
`liveBroadcasts.insert` is `true`, so **every** broadcast the app creates is archived as a VOD
when it ends.
- Broadcasts are inserted with `privacyStatus = "private"`
(`Services/YouTubeStreamService.cs:49`), so those orphans are not public. That is correct and
must stay.
- The app has **no delete path** — nothing in `Services/` deletes a broadcast or a video. The
creator clears each orphan by hand in Studio.
So "dev mode deletes the video immediately" is a **manual Studio step today**, not app behaviour.
### Sequencing constraint — do not flip the flag early
`enableAutomaticArchiving: false` is the correct end state: the app uploads the local file
deliberately, on demand, instead of YouTube archiving silently. **But automatic archiving is
currently the only route a recording takes to YouTube.** Setting it to `false` before publish
ships would leave test streams with nothing on the channel at all and re-create the 2026-09-26
complaint in a worse form.
**Therefore: ship the upload first, flip the flag as part of the same task, never before.**
---
## Design notes
- **Source is the local MP4**, not the YouTube archive. The app already has it (REC side,
`EncoderOptions.RecordPath`); the archived VOD is a duplicate and an orphan.
- **Resumable upload is mandatory, not optional.** Stream-length recordings are large (hours of
4–8 Mbps video); a plain single POST is not viable. YouTube's `videos.insert` supports
resumable sessions, and the failure mode that matters is a drop at 80%.
- **Default visibility must be `private` or `unlisted`, never `public`.** A bad take going public
is unrecoverable, and this app is used for test streams by definition. The creator picks, and
the default is the safe one.
- **A title still has to come from somewhere.** One button, not zero decisions: default to the
recording's filename, allow an edit, do not block on it.
- **Quota:** `videos.insert` costs 1600 units against a 10,000/day project allowance — roughly
six uploads per day. Fine for a creator, but it is a real ceiling and the app should surface a
quota error rather than retrying into it.
- Scope already permits it: the single `auth/youtube` scope covers upload. No scope change needed.
---
## Open question to settle before coding
**Microsoft Store 11.12 (User-Generated Content).** The standing answer is that the app hosts no
user content and does not interact with third-party UGC — chat is read-only and never persisted,
which is load-bearing for that answer. A publish feature makes the app *push* creator content to
a third-party platform, which touches the same area. It does not obviously break the answer (the
destination is YouTube, which has its own mature moderation, and the app stores nothing), but it
**must be re-checked against
[`research-store-certification.md`](research-store-certification.md) §11.12 and the IARC answers
in [`task-48`](task-48-distribution-msix.md) before submission** — do not let the IARC answers go
to certification describing an app that cannot publish.
---
## Precedence
Belongs to **TASK 48** (MSIX/Store), but does **not** block the Store submission: the app is
fully shippable without it. Recommend MSIX submission first, publish second — unless the creator
reverses that, in which case it moves ahead of the remaining TASK 48 items.