You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
YouTube AutoEncoder is a headlesslive-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.
4
4
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.
6
6
7
-
- camera power loss
8
-
- encoder process crashes
9
-
- host reboots
10
-
- network interruptions
11
-
- YouTube broadcasts ending when ingest stops
7
+
## Executive Summary
12
8
13
-
## What It Does
9
+
YouTube AutoEncoder turns a dedicated Linux device into an unattended YouTube streaming encoder. The core service performs three jobs:
14
10
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.
23
14
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
25
26
26
27
```text
27
-
bin/youtube-autoencoder Production FFmpeg + lifecycle supervisor
28
-
bin/youtube-autoencoder-api YouTube OAuth and Live Streaming API helper
| 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.
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
37
132
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.
43
134
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.
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
+
83
216
## YouTube Authorization
84
217
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:
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:
124
253
125
254
```bash
126
-
systemctl status youtube-autoencoder@encoder.service
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:
138
259
139
260
```bash
140
261
youtube-autoencoder-test-pattern 900
141
262
```
142
263
143
-
For a complete visible API-managed test:
264
+
Run a complete visible API-managed test broadcast:
- 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.
150
299
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
0 commit comments