Skip to content

Commit 97bf37b

Browse files
authored
Merge pull request #2 from sumitake/codex/executive-summary-readme
[codex] Expand executive README
2 parents 5a4cf52 + 3b850bc commit 97bf37b

2 files changed

Lines changed: 238 additions & 67 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Changelog
2+
3+
All notable project changes are recorded here. The README shows only the most recent entry.
4+
5+
## 2026-07-06 - Executive Summary Documentation
6+
7+
- Expanded the README into an executive summary covering architecture, runtime process flow, operational commands, recovery behavior, and caveats.
8+
- Added this standalone changelog so README history stays concise.
9+
10+
## 2026-07-06 - Initial Generic AutoEncoder
11+
12+
- Added the generic `YouTube AutoEncoder` package with `youtube-autoencoder`, `youtube-autoencoder-api`, and `youtube-autoencoder-test-pattern`.
13+
- Added source probing, FFmpeg supervision, retry behavior, YouTube Live lifecycle automation, and API-managed visible test broadcasts.
14+
- Added example configuration, systemd units, Raspberry Pi deployment notes, and secret-focused ignore rules.

README.md

Lines changed: 224 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,147 @@
11
# YouTube AutoEncoder
22

3-
YouTube AutoEncoder is a headless live-stream bridge for unattended cameras and other RTSP-style sources. It runs FFmpeg under systemd and can manage the YouTube Live broadcast lifecycle through the YouTube Data API.
3+
YouTube AutoEncoder is a headless, self-recovering live-stream bridge for unattended RTSP-style cameras and YouTube Live. It is designed for small Linux hosts such as Raspberry Pi systems where a full OBS desktop stack is too heavy, but where the stream still needs to recover from camera power loss, encoder crashes, network interruptions, host reboots, and YouTube broadcast lifecycle edge cases.
44

5-
The intended deployment target is a small Linux host such as a Raspberry Pi that should survive:
5+
The project runs FFmpeg under systemd, optionally reuses OBS profile data for camera and stream-key compatibility, and can manage YouTube Live broadcasts through the YouTube Data API.
66

7-
- camera power loss
8-
- encoder process crashes
9-
- host reboots
10-
- network interruptions
11-
- YouTube broadcasts ending when ingest stops
7+
## Executive Summary
128

13-
## What It Does
9+
YouTube AutoEncoder turns a dedicated Linux device into an unattended YouTube streaming encoder. The core service performs three jobs:
1410

15-
- Probes the source before creating a YouTube broadcast.
16-
- Creates a new YouTube broadcast only after the source is reachable.
17-
- Binds the broadcast to a reusable YouTube live stream.
18-
- Starts FFmpeg ingest.
19-
- Waits for YouTube to report ingest as active.
20-
- Transitions the broadcast through `testing` and then `live`.
21-
- Completes the broadcast when ingest exits.
22-
- Retries from the top until the source returns.
11+
- Validate the camera source before creating public-facing YouTube objects.
12+
- Push the stream with FFmpeg using either low-CPU video copy mode or explicit transcode mode.
13+
- Create, bind, transition, complete, and recreate YouTube Live broadcasts as needed.
2314

24-
## Repository Layout
15+
The intended outcome is an appliance-like encoder that can be deployed on a Raspberry Pi, left headless, and managed remotely through normal Linux tools. When the camera is offline, the service waits without creating new broadcasts. When the camera returns, it prepares YouTube, starts ingest, transitions the broadcast live, and keeps watching FFmpeg until the next interruption.
16+
17+
## Current Status
18+
19+
- Project maturity: early operational package.
20+
- Primary target: Debian/Raspberry Pi OS with systemd.
21+
- Runtime dependencies: Python 3.11 or newer, FFmpeg, FFprobe.
22+
- Python dependencies: standard library only.
23+
- YouTube integration: OAuth device flow plus YouTube Data API v3 live-streaming endpoints.
24+
25+
## Architecture
2526

2627
```text
27-
bin/youtube-autoencoder Production FFmpeg + lifecycle supervisor
28-
bin/youtube-autoencoder-api YouTube OAuth and Live Streaming API helper
29-
bin/youtube-autoencoder-test-pattern Temporary moving test-pattern stream
30-
config/youtube-autoencoder.env.example
31-
systemd/youtube-autoencoder@.service System service template
32-
systemd/user/youtube-autoencoder.service User service template
33-
docs/raspberry-pi.md Raspberry Pi deployment notes
28+
camera / RTSP source
29+
|
30+
v
31+
ffprobe source probe
32+
|
33+
source reachable? no -> wait and retry
34+
|
35+
yes
36+
|
37+
v
38+
+------------------------------------------------+
39+
| youtube-autoencoder |
40+
| |
41+
| - loads config and OBS compatibility files |
42+
| - prepares YouTube lifecycle when enabled |
43+
| - starts and supervises FFmpeg |
44+
| - redacts sensitive values in service logs |
45+
| - completes broadcast on exit when configured |
46+
+---------------------+--------------------------+
47+
|
48+
invokes | starts
49+
v
50+
+-------------------+ +----------------------+
51+
| youtube- | | FFmpeg |
52+
| autoencoder-api | | |
53+
| | | RTSP input |
54+
| OAuth refresh | | synthetic audio |
55+
| stream lookup | | copy or transcode |
56+
| broadcast create | | RTMPS/RTMP output |
57+
| bind/transition | +----------+-----------+
58+
+---------+---------+ |
59+
| v
60+
v YouTube ingest
61+
YouTube Data API |
62+
| v
63+
+------------------ YouTube Live broadcast
64+
```
65+
66+
### Components
67+
68+
| Component | Path | Responsibility |
69+
| --- | --- | --- |
70+
| Supervisor | `bin/youtube-autoencoder` | Main loop, source probing, FFmpeg supervision, retry behavior, broadcast completion on exit. |
71+
| API helper | `bin/youtube-autoencoder-api` | OAuth device authorization, token refresh, YouTube stream lookup/create, broadcast create/bind/transition/complete, visible test workflow. |
72+
| Test pattern | `bin/youtube-autoencoder-test-pattern` | FFmpeg-generated moving video and tone for end-to-end YouTube ingest testing. |
73+
| Example config | `config/youtube-autoencoder.env.example` | Service environment variables for source selection, FFmpeg mode, YouTube lifecycle, retry timing, and transcode settings. |
74+
| System service | `systemd/youtube-autoencoder@.service` | System-level service template for a dedicated encoder user. |
75+
| User service | `systemd/user/youtube-autoencoder.service` | User-level service alternative. |
76+
| Pi runbook | `docs/raspberry-pi.md` | Raspberry Pi deployment notes. |
77+
78+
### Persistent Local State
79+
80+
By default the runtime expects service-owned files under:
81+
82+
```text
83+
~/.config/youtube-autoencoder/
84+
```
85+
86+
Important files:
87+
88+
- `youtube-autoencoder.env`: private service configuration.
89+
- `google-oauth-client.json`: Google OAuth client configuration.
90+
- `youtube-token.json`: OAuth access and refresh token cache.
91+
- `youtube-live-state.json`: last prepared broadcast and stream IDs.
92+
93+
OBS compatibility mode can also read and update:
94+
95+
```text
96+
~/.config/obs-studio/basic/profiles/YouTube_AutoEncoder/service.json
97+
~/.config/obs-studio/basic/scenes/Untitled.json
3498
```
3599

36-
## Requirements
100+
## Process Flow
101+
102+
### Normal Production Loop
103+
104+
1. systemd starts `youtube-autoencoder`.
105+
2. The supervisor loads configuration from the service environment file.
106+
3. The source URL is resolved from `YTA_SOURCE_URL` or from an OBS scene collection.
107+
4. The YouTube ingest URL is resolved from OBS `service.json`.
108+
5. FFprobe checks the RTSP source when `YTA_SOURCE_PROBE=true`.
109+
6. If the source is unreachable, the service waits `YTA_RESTART_DELAY` and retries without creating a YouTube broadcast.
110+
7. If YouTube lifecycle management is enabled, the API helper locates or creates a reusable YouTube live stream.
111+
8. The API helper creates a broadcast, binds it to the reusable stream, and stores the IDs in `youtube-live-state.json`.
112+
9. The supervisor starts FFmpeg.
113+
10. The API helper waits until YouTube reports active ingest.
114+
11. The broadcast transitions to `testing`, waits `YTA_YOUTUBE_TESTING_DELAY_SEC`, then transitions to `live`.
115+
12. The supervisor streams until FFmpeg exits, the service is stopped, or `YTA_MAX_RUNTIME` rotates the process.
116+
13. On exit, the supervisor completes the YouTube broadcast when `YTA_YOUTUBE_COMPLETE_ON_EXIT=true`.
117+
14. The loop sleeps and restarts from source probing.
118+
119+
### Recovery Behavior
120+
121+
| Failure | Expected behavior |
122+
| --- | --- |
123+
| Camera offline before stream start | Source probe fails; no broadcast is created; service retries. |
124+
| Camera loses power during stream | FFmpeg exits; broadcast is completed; service retries until the camera returns. |
125+
| Host reboots | systemd restarts the service after network-online target; normal loop resumes. |
126+
| FFmpeg crashes | systemd and the internal loop restart the attempt. |
127+
| YouTube ingest does not become active | API wait times out; attempt exits; service retries. |
128+
| OAuth access token expires | API helper refreshes from the stored refresh token. |
129+
| Reusable YouTube stream missing | With `YTA_YOUTUBE_CREATE_STREAM=true`, API helper creates one and writes it to OBS `service.json`. |
130+
131+
### Test Pattern Flow
37132

38-
- Linux with systemd
39-
- Python 3.11 or newer
40-
- FFmpeg and FFprobe
41-
- A YouTube channel with live streaming enabled
42-
- A Google OAuth client JSON file for an installed/device-style app
133+
For visual validation, `youtube-autoencoder-api run-visible-test` creates and binds a broadcast, starts `youtube-autoencoder-test-pattern`, waits for active ingest, transitions to live, and completes the broadcast when the test exits.
43134

44-
No third-party Python packages are required.
135+
This verifies the YouTube account, OAuth token, reusable stream, ingest URL, broadcast transitions, FFmpeg output, and visible end-stream quality without requiring the real camera to be online.
45136

46-
## Quick Install
137+
## Installation
138+
139+
Install runtime packages:
140+
141+
```bash
142+
sudo apt update
143+
sudo apt install -y ffmpeg python3
144+
```
47145

48146
Install the scripts:
49147

@@ -61,17 +159,15 @@ cp config/youtube-autoencoder.env.example ~/.config/youtube-autoencoder/youtube-
61159
chmod 600 ~/.config/youtube-autoencoder/youtube-autoencoder.env
62160
```
63161

64-
Edit the env file for your source and YouTube settings.
65-
66-
Install a service. For a system service running as user `encoder`:
162+
Install the system service for a dedicated user named `encoder`:
67163

68164
```bash
69165
sudo install -m 0644 systemd/youtube-autoencoder@.service /etc/systemd/system/youtube-autoencoder@.service
70166
sudo systemctl daemon-reload
71167
sudo systemctl enable --now youtube-autoencoder@encoder.service
72168
```
73169

74-
For a user service:
170+
For a user service instead:
75171

76172
```bash
77173
mkdir -p ~/.config/systemd/user
@@ -80,79 +176,140 @@ systemctl --user daemon-reload
80176
systemctl --user enable --now youtube-autoencoder.service
81177
```
82178

179+
For Raspberry Pi specific notes, see `docs/raspberry-pi.md`.
180+
181+
## Configuration Model
182+
183+
The service is configured entirely through environment variables. The most important values are:
184+
185+
| Variable | Purpose |
186+
| --- | --- |
187+
| `YTA_SOURCE_URL` | Direct camera/source URL. Preferred for non-OBS deployments. |
188+
| `YTA_OBS_SERVICE_FILE` | OBS service file containing YouTube ingest server and stream key. |
189+
| `YTA_OBS_SCENE_FILE` | OBS scene file used to discover a VLC/RTSP source when `YTA_SOURCE_URL` is not set. |
190+
| `YTA_OBS_SOURCE_NAME` | Optional OBS VLC source name selector. |
191+
| `YTA_MODE` | `copy` for low CPU video copy, or `transcode` for re-encoding. |
192+
| `YTA_YOUTUBE_LIFECYCLE` | Enable or disable API-managed broadcast lifecycle. |
193+
| `YTA_YOUTUBE_PRIVACY` | YouTube broadcast privacy: `public`, `unlisted`, or `private`. |
194+
| `YTA_YOUTUBE_TITLE_PREFIX` | Prefix used for generated broadcast titles. |
195+
| `YTA_YOUTUBE_CREATE_STREAM` | Create a reusable YouTube stream when the configured stream key is not found. |
196+
| `YTA_SOURCE_PROBE` | Probe the camera before creating a broadcast. |
197+
| `YTA_RESTART_DELAY` | Delay between recovery attempts. |
198+
| `YTA_MAX_RUNTIME` | Optional forced FFmpeg rotation interval. `0` disables rotation. |
199+
200+
Minimal direct source configuration:
201+
202+
```text
203+
YTA_SOURCE_URL=rtsp://camera.example.local/stream1
204+
```
205+
206+
OBS compatibility configuration:
207+
208+
```text
209+
YTA_OBS_SERVICE_FILE=/home/encoder/.config/obs-studio/basic/profiles/Stream/service.json
210+
YTA_OBS_SCENE_FILE=/home/encoder/.config/obs-studio/basic/scenes/Untitled.json
211+
YTA_OBS_SOURCE_NAME=Camera RTSP
212+
```
213+
214+
The OBS service file supplies the reusable YouTube RTMPS server and stream key. If YouTube AutoEncoder creates a reusable stream through the API, it updates that service file.
215+
83216
## YouTube Authorization
84217

85-
Create an OAuth client in Google Cloud Console and place the downloaded JSON at:
218+
Create a Google OAuth client and place the downloaded JSON at:
86219

87220
```text
88221
~/.config/youtube-autoencoder/google-oauth-client.json
89222
```
90223

91-
Then run:
224+
Run:
92225

93226
```bash
94227
youtube-autoencoder-api authorize
95228
```
96229

97-
Open the displayed device-flow URL, enter the code, and approve access for the YouTube channel account. The refresh token is stored at:
230+
Open the displayed device-flow URL, enter the code, and approve access for the YouTube channel account. The refresh token is stored in:
98231

99232
```text
100233
~/.config/youtube-autoencoder/youtube-token.json
101234
```
102235

103236
Keep both files private.
104237

105-
## Source Configuration
238+
## Operations
106239

107-
The simplest configuration is a direct RTSP source:
240+
Check service status:
108241

109-
```text
110-
YTA_SOURCE_URL=rtsp://camera.example.local/stream1
242+
```bash
243+
systemctl status youtube-autoencoder@encoder.service
111244
```
112245

113-
Alternatively, the encoder can reuse an OBS profile and scene collection:
246+
Follow logs:
114247

115-
```text
116-
YTA_OBS_SERVICE_FILE=/home/encoder/.config/obs-studio/basic/profiles/Stream/service.json
117-
YTA_OBS_SCENE_FILE=/home/encoder/.config/obs-studio/basic/scenes/Untitled.json
118-
YTA_OBS_SOURCE_NAME=Camera RTSP
248+
```bash
249+
journalctl -u youtube-autoencoder@encoder.service -f
119250
```
120251

121-
The OBS service file supplies the reusable YouTube RTMPS server and stream key. If YouTube AutoEncoder creates a reusable stream through the API, it updates that service file.
122-
123-
## Normal Operation
252+
Check YouTube stream status:
124253

125254
```bash
126-
systemctl status youtube-autoencoder@encoder.service
127-
journalctl -u youtube-autoencoder@encoder.service -f
128255
youtube-autoencoder-api status
129256
```
130257

131-
When the camera is offline, the service stays active but does not run FFmpeg and does not create YouTube broadcasts. Logs will show source-probe failures and retry timing.
132-
133-
When the camera comes back, the service creates and starts a fresh YouTube broadcast automatically.
134-
135-
## Test Pattern
136-
137-
To visually validate YouTube ingest quality:
258+
Run a direct test-pattern stream using the configured OBS service file:
138259

139260
```bash
140261
youtube-autoencoder-test-pattern 900
141262
```
142263

143-
For a complete visible API-managed test:
264+
Run a complete visible API-managed test broadcast:
144265

145266
```bash
146267
youtube-autoencoder-api run-visible-test --duration 900 --privacy unlisted
147268
```
148269

149-
## Security Notes
270+
Complete the last known broadcast manually:
271+
272+
```bash
273+
youtube-autoencoder-api complete
274+
```
275+
276+
## Caveats
277+
278+
- YouTube Live must already be enabled on the channel. New or restricted channels may not be allowed to stream immediately.
279+
- The YouTube Data API flow requires OAuth user consent. A simple API key is not enough for creating, binding, or transitioning live broadcasts.
280+
- Google OAuth app restrictions can block authorization if the app is limited to an organization that does not include the streaming account.
281+
- YouTube API quota, API outages, or account policy restrictions can prevent lifecycle operations even when FFmpeg is healthy.
282+
- Each recovery cycle can create a new broadcast when lifecycle management is enabled. This is intentional for self-recovery, but it can leave multiple completed broadcasts in YouTube Studio after unstable camera or network periods.
283+
- `YTA_MODE=copy` is lowest CPU, but it only works when the camera video stream is compatible with YouTube ingest expectations. H.265 or unusual camera output usually requires `YTA_MODE=transcode`.
284+
- The production encoder adds synthetic silent audio because YouTube ingest generally behaves better with audio present. It does not preserve camera audio today.
285+
- The OBS scene parser is intentionally narrow. It looks for VLC sources and playlist URLs; complex OBS scenes, filters, browser sources, or arbitrary OBS plugins are not reproduced.
286+
- The scripts redact credentials from their own logs, but privileged local users may still see full FFmpeg command-line arguments while a stream is active.
287+
- If the camera flaps after a broadcast is created but before YouTube reports active ingest, the attempt will fail and retry. The incomplete broadcast may remain in YouTube Studio.
288+
- Raspberry Pi Connect screen sharing requires a graphical session. A headless deployment should rely on SSH, remote shell, VPN, or another non-GUI management path.
289+
- This project does not install or configure firewalling, VPN, remote management, OS hardening, or camera power control.
290+
291+
## Changelog
292+
293+
Only the most recent changelog entry is shown here. See `CHANGELOG.md` for full history.
294+
295+
### 2026-07-06 - Executive Summary Documentation
296+
297+
- Expanded the README into an executive summary covering architecture, runtime process flow, operational commands, recovery behavior, and caveats.
298+
- Added a standalone changelog so the README can show only the newest entry.
150299

151-
- Do not commit OAuth client files, refresh tokens, stream keys, or `.env` files.
152-
- The scripts redact RTSP credentials and YouTube stream keys from their own logs.
153-
- System process listings can still expose full FFmpeg command lines to privileged local users while a stream is active.
154-
- Use a dedicated Google OAuth client and a dedicated encoder user where practical.
300+
## Repository Layout
301+
302+
```text
303+
bin/youtube-autoencoder Production FFmpeg and lifecycle supervisor
304+
bin/youtube-autoencoder-api YouTube OAuth and Live Streaming API helper
305+
bin/youtube-autoencoder-test-pattern Temporary moving test-pattern stream
306+
config/youtube-autoencoder.env.example
307+
systemd/youtube-autoencoder@.service System service template
308+
systemd/user/youtube-autoencoder.service User service template
309+
docs/raspberry-pi.md Raspberry Pi deployment notes
310+
CHANGELOG.md Full project changelog
311+
```
155312

156313
## License
157314

158-
MPL-2.0. See [LICENSE](LICENSE).
315+
MPL-2.0. See `LICENSE`.

0 commit comments

Comments
 (0)