diff --git a/docs/plans/2026-02-14-youtube-operations-hub-extensions-design.md b/docs/plans/2026-02-14-youtube-operations-hub-extensions-design.md new file mode 100644 index 0000000..70aa3f0 --- /dev/null +++ b/docs/plans/2026-02-14-youtube-operations-hub-extensions-design.md @@ -0,0 +1,481 @@ +# YouTube Operations Hub - Erweiterungen Design + +**Datum:** 2026-02-14 +**Status:** Genehmigt +**Geschätzter Aufwand:** ~80 Stunden (4 Phasen) + +## Zusammenfassung + +Implementierung der 4 Erweiterungsmöglichkeiten aus dem YouTube Operations Hub (Abschnitt 11): + +1. **YouTube API Integration** - Metriken-Sync, Video-Upload, Kommentar-Import +2. **Analytics Dashboard** - Performance-Vergleich, Trend-Analyse, ROI +3. **Workflow-Automatisierung** - Status-Änderungen, Deadline-Erinnerungen, Kapazitätsplanung +4. **Content-Kalender** - Visuelle Übersicht, Drag & Drop, Konflikt-Erkennung + +## Bestandsanalyse + +### Vorhandene Infrastruktur +- **YouTubeClient** (`src/lib/integrations/youtube/YouTubeClient.ts`) - nur Comment-Methoden +- **OAuth 2.0** - funktioniert, Scopes: `youtube.readonly`, `youtube.force-ssl`, `youtube` +- **YouTubeContent Collection** - `performance.*` Felder existieren (nie befüllt) +- **YouTubeChannels Collection** - `currentMetrics.*` Felder (nur bei OAuth-Connect aktualisiert) +- **BullMQ** - Email/PDF-Worker laufen, Queue-Infrastruktur vorhanden +- **Recharts 3.6.0** - bereits installiert +- **YouTubeAnalyticsDashboard** - existiert mit 4 Tabs (Daten nur aus CMS) +- **CommentsSyncService** - importiert nur Top-Level-Kommentare + +### Fehlende Komponenten +- `youtube.videos.list()` API-Aufrufe (Metriken) +- `youtube.videos.insert()` API-Aufrufe (Upload) +- `youtubeAnalytics` API v2 (detaillierte Analytics) +- OAuth-Scopes: `youtube.upload`, `yt-analytics.readonly` +- Reply-Thread-Import bei Kommentaren +- Deadline-Scheduler/Cron +- Kalender-Bibliothek (FullCalendar) + +--- + +## Phase 1: YouTube API Integration (~30h) + +### 1.1 Metriken-Sync Service + +**Neue Datei:** `src/lib/integrations/youtube/VideoMetricsSyncService.ts` + +``` +YouTubeClient.getVideoStatistics(videoIds[]) + → youtube.videos.list({ part: 'statistics,snippet', id: videoIds }) + → Batch-Update: YouTubeContent.performance.* Felder +``` + +**Sync-Strategie:** +- Batch-Verarbeitung: Max 50 Video-IDs pro API-Call (YouTube-Limit) +- Inkrementeller Sync: Nur Videos mit Status `published` oder `tracked` +- Frequenz: Alle 6 Stunden via Cron (`/api/cron/youtube-metrics-sync`) +- Quota-Budget: ~300 Einheiten/Tag (bei 150 Videos = 3 Calls x 100 Quota) + +**Befüllte Felder in YouTubeContent:** +| Feld | API-Quelle | Typ | +|------|-----------|-----| +| `performance.views` | `statistics.viewCount` | number | +| `performance.likes` | `statistics.likeCount` | number | +| `performance.comments` | `statistics.commentCount` | number | +| `performance.estimatedMinutesWatched` | Analytics API | number | +| `performance.averageViewDuration` | Analytics API | number | +| `performance.ctr` | Analytics API | number | +| `performance.impressions` | Analytics API | number | +| `performance.subscribersGained` | Analytics API | number | + +**Neue Datei:** `src/lib/integrations/youtube/ChannelMetricsSyncService.ts` + +``` +YouTubeClient.getChannelStatistics(channelId) + → youtube.channels.list({ part: 'statistics', id: channelId }) + → Update: YouTubeChannels.currentMetrics.* Felder +``` + +### 1.2 Erweiterter Kommentar-Import + +**Änderung:** `src/lib/integrations/youtube/CommentsSyncService.ts` + +Erweiterungen: +- Reply-Threads importieren (youtube.comments.list für parentId) +- `parentComment` Relationship-Feld in CommunityInteractions nutzen +- Thread-Tiefe: Max 2 Ebenen (YouTube-Limit) +- Neue Kommentare seit letztem Sync (publishedAfter-Filter) + +### 1.3 Video-Upload Service + +**Neue Datei:** `src/lib/integrations/youtube/VideoUploadService.ts` + +**Upload-Flow:** +``` +Admin UI "Veröffentlichen" Button + → API Route POST /api/youtube/upload + → BullMQ Job in youtube-upload Queue + → VideoUploadWorker: + 1. Media-Datei aus Payload laden + 2. youtube.videos.insert() mit Resumable Upload + 3. YouTubeContent Status → 'published' + 4. YouTube-Video-ID speichern + 5. Notification erstellen +``` + +**Neue Dateien:** +- `src/lib/queue/jobs/youtube-upload-job.ts` +- `src/lib/queue/workers/youtube-upload-worker.ts` +- `src/app/(payload)/api/youtube/upload/route.ts` + +**OAuth-Scope hinzufügen:** +- `src/lib/integrations/youtube/oauth.ts`: Scope `https://www.googleapis.com/auth/youtube.upload` + +**Upload-Metadaten (aus YouTubeContent-Feldern):** +- `youtube.metadata.title` → Video-Titel +- `youtube.metadata.description` → Beschreibung +- `youtube.metadata.tags` → Tags +- `youtube.metadata.categoryId` → Kategorie +- `youtube.metadata.visibility` → public/unlisted/private +- `youtube.metadata.language` → Sprache +- `scheduledPublishDate` → Geplante Veröffentlichung + +**Quota-Tracking:** +- Neue Collection oder Felder in YouTubeChannels: `quotaUsedToday`, `quotaResetAt` +- Video-Upload: ~1.600 Quota-Einheiten pro Upload +- Tägliches Limit: 10.000 Einheiten (Standard) +- Warnung bei >80% Auslastung via NotificationService + +### 1.4 Cron-Jobs + +| Endpoint | Schedule | Beschreibung | +|----------|----------|--------------| +| `/api/cron/youtube-metrics-sync` | `0 */6 * * *` | Video-Metriken alle 6h | +| `/api/cron/youtube-channel-sync` | `0 4 * * *` | Kanal-Statistiken täglich | + +**Änderung:** `vercel.json` - Neue Cron-Einträge + +--- + +## Phase 2: Analytics Dashboard (~16h) + +### 2.1 YouTube Analytics API Client + +**Neue Datei:** `src/lib/integrations/youtube/YouTubeAnalyticsClient.ts` + +```typescript +// Methoden: +getVideoAnalytics(videoId, startDate, endDate, metrics[]) +getChannelAnalytics(channelId, startDate, endDate, metrics[]) +getTopVideos(channelId, metric, limit) +getDemographics(channelId, startDate, endDate) +``` + +**OAuth-Scope hinzufügen:** +- `yt-analytics.readonly` in `src/lib/integrations/youtube/oauth.ts` + +### 2.2 Performance-Vergleich Tab + +**Erweiterung:** `src/components/admin/YouTubeAnalyticsDashboard.tsx` + +Neuer Tab "Vergleich" mit: +- Multi-Select für Videos (max 5) +- Metriken-Auswahl: Views, Likes, CTR, Watch Time +- Zeitreihen-Chart (Recharts LineChart mit mehreren Serien) +- Normalisierte Ansicht (prozentual) für unterschiedliche Größenordnungen +- Tabelle mit Delta-Werten + +### 2.3 Trend-Analyse Tab + +Neuer Tab "Trends" mit: +- Automatische Trend-Erkennung (gleitender Durchschnitt, 7/30 Tage) +- Wachstumsrate pro Metrik (WoW, MoM) +- Prognose-Linie (lineare Regression, nächste 30 Tage) +- Saisonalitäts-Erkennung (Wochentag-Heatmap) +- Best/Worst Performer Ranking + +**Berechnung im Backend:** `src/app/(payload)/api/youtube/analytics/route.ts` +- Neuer Query-Parameter: `tab=trends` +- Berechnungen serverseitig für Performance + +### 2.4 ROI-Berechnung Tab + +Neuer Tab "ROI" mit: +- Neue Felder in YouTubeContent: + - `costs.estimatedProductionHours` (number) + - `costs.estimatedProductionCost` (number, EUR) + - `costs.estimatedRevenue` (number, EUR - manuell oder via Analytics API) +- ROI-Formel: `(Revenue - Cost) / Cost * 100` +- CPV (Cost per View): `Cost / Views` +- Revenue per View: `Revenue / Views` +- Break-Even-Analyse +- Chart: Kosten vs. Einnahmen über Zeit + +**Keine separate Collection** - pragmatischer Ansatz mit Feldern direkt in YouTubeContent. + +### 2.5 API-Erweiterungen + +**Änderung:** `src/app/(payload)/api/youtube/analytics/route.ts` + +Neue Query-Parameter: +- `tab=comparison&videoIds=id1,id2,id3` +- `tab=trends&metric=views&period=30d` +- `tab=roi&channelId=X` + +--- + +## Phase 3: Workflow-Automatisierung (~14h) + +### 3.1 Automatische Status-Übergänge + +**Neue Datei:** `src/hooks/youtubeContent/autoStatusTransitions.ts` + +Regeln (konfigurierbar): +| Bedingung | Aktion | +|-----------|--------| +| Alle Checklisten-Items erledigt | Status → nächste Phase | +| Video auf YouTube veröffentlicht | Status → `published` | +| 7 Tage nach Veröffentlichung | Status → `tracked` | +| Upload-Job abgeschlossen | Status → `published` | + +**Hook-Typ:** `afterChange` auf YouTubeContent +- Prüft Checklisten-Completion via YtTasks +- Erstellt Notification bei automatischem Übergang +- Audit-Log-Eintrag + +### 3.2 Deadline-Erinnerungen + +**Neue Datei:** `src/app/(payload)/api/cron/deadline-reminders/route.ts` + +**Schedule:** `0 9 * * 1-5` (Mo-Fr 9:00 UTC) + +**Prüfungen:** +1. `editDeadline` in 2 Tagen → Erinnerung +2. `editDeadline` überschritten → Warnung (Priorität: high) +3. `reviewDeadline` in 1 Tag → Erinnerung +4. `scheduledPublishDate` in 3 Tagen → Erinnerung +5. Batch-Deadline (`YtBatches.targetCompletionDate`) in 3 Tagen → Erinnerung + +**Notification-Typen:** Nutzt bestehendes `NotificationService` + `YtNotifications`: +- `task_due` - Deadline naht +- `task_overdue` - Deadline überschritten +- Neuer Typ: `deadline_warning` (3 Tage vorher) + +**Zustellung:** +- YtNotifications Collection (Admin-Inbox) +- Optional: E-Mail via AlertService (konfigurierbar pro User) + +### 3.3 Team-Kapazitätsplanung + +**Neue Datei:** `src/app/(payload)/api/youtube/capacity/route.ts` + +**Datenquellen:** +- YtTasks (zugewiesene Aufgaben pro User) +- YouTubeContent (Videos in Produktion pro Assignee) +- YtBatches (geplante Batches) + +**Berechnung:** +``` +Pro Team-Mitglied: + - Aktive Tasks (status: in_progress, pending) + - Geschätzte Stunden (aus Tasks estimatedHours) + - Videos in Pipeline (zugewiesen, nicht abgeschlossen) + - Auslastung = aktive Stunden / verfügbare Stunden (40h/Woche default) +``` + +**UI-Komponente:** Integration in YouTubeAnalyticsDashboard +- Neuer Tab "Team" oder Widget im bestehenden "Pipeline" Tab +- Balkendiagramm: Auslastung pro Person +- Farbcodierung: Grün (<70%), Gelb (70-90%), Rot (>90%) +- Warnung bei Überbelastung via NotificationService + +--- + +## Phase 4: Content-Kalender (~20h) + +### 4.1 Bibliothek & Setup + +**Neue Dependency:** `@fullcalendar/react` + Plugins +- `@fullcalendar/core` +- `@fullcalendar/daygrid` +- `@fullcalendar/timegrid` +- `@fullcalendar/interaction` (Drag & Drop) +- `@fullcalendar/list` + +### 4.2 Kalender API + +**Neue Datei:** `src/app/(payload)/api/youtube/calendar/route.ts` + +**GET** - Events abrufen: +``` +?channelId=X&start=2026-01-01&end=2026-02-28 +``` + +**Response-Format:** +```typescript +{ + events: [{ + id: string, // YouTubeContent ID + title: string, // Video-Titel + start: string, // scheduledPublishDate (ISO) + end: string, // scheduledPublishDate + 1h + color: string, // Channel branding.primaryColor + extendedProps: { + status: string, + contentType: 'video' | 'short', + channelName: string, + seriesName?: string, + hasConflict: boolean, + } + }] +} +``` + +**PATCH** - Datum per Drag & Drop ändern: +``` +{ contentId: string, newDate: string } +``` +- Validierung: Nur Videos mit Status vor `published` +- Konflik-Prüfung vor Speicherung +- Audit-Log-Eintrag + +### 4.3 Content-Kalender Komponente + +**Neue Datei:** `src/components/admin/ContentCalendar.tsx` + +Features: +- Monats-, Wochen-, Tages-, Listen-Ansicht +- Farbcodierung nach Kanal (`branding.primaryColor`) +- Status-Icons auf Events (Draft, Review, Ready, Published) +- Tooltip mit Video-Details bei Hover +- Click → Navigation zum YouTubeContent-Dokument + +**Neue Datei:** `src/components/admin/ContentCalendar.module.scss` +- BEM-Konventionen +- Payload CSS-Variablen für Dark/Light Mode +- Responsive Layout + +### 4.4 Drag & Drop + +FullCalendar's eingebautes `interaction` Plugin: +- Event-Verschiebung → PATCH API-Call +- Bestätigungsdialog vor Speicherung +- Undo-Möglichkeit (vorheriges Datum merken) +- Nur für Videos vor Status `published` + +### 4.5 Konflikt-Erkennung + +**Neue Datei:** `src/lib/youtube/ConflictDetectionService.ts` + +**Regeln:** +1. **Zeitkonflikt:** Zwei Videos am selben Tag auf demselben Kanal +2. **Serien-Konflikt:** Serien-Folge vor vorheriger Folge geplant +3. **Frequenz-Konflikt:** Mehr Videos als `publishingSchedule.longformPerWeek` / `shortsPerWeek` +4. **Wochenend-Warnung:** Video am Wochenende geplant (konfigurierbar) + +**Anzeige:** +- Rote Markierung auf Kalender-Events mit Konflikten +- Warnung-Banner im Kalender +- Konflikte als Tooltip auf betroffenen Events +- API-Response enthält `hasConflict: boolean` pro Event + +### 4.6 Admin View Registrierung + +**Änderung:** `src/payload.config.ts` + +Neuer Custom View: `/admin/content-calendar` +- Registrierung analog zu `YouTubeAnalyticsDashboard` +- Navigation-Link via `afterNavLinks` + +--- + +## Technische Querschnittsthemen + +### OAuth-Scope-Erweiterungen + +**Datei:** `src/lib/integrations/youtube/oauth.ts` + +Neue Scopes: +```typescript +const SCOPES = [ + 'https://www.googleapis.com/auth/youtube.readonly', + 'https://www.googleapis.com/auth/youtube.force-ssl', + 'https://www.googleapis.com/auth/youtube', + 'https://www.googleapis.com/auth/youtube.upload', // NEU + 'https://www.googleapis.com/auth/yt-analytics.readonly', // NEU +] +``` + +**Hinweis:** Bestehende OAuth-Tokens müssen nach Scope-Änderung neu autorisiert werden. Re-Auth-Flow in Admin UI einbauen. + +### Quota-Management + +YouTube Data API v3 Quota: 10.000 Einheiten/Tag (Standard) + +| Operation | Kosten | Häufigkeit | Tägliche Kosten | +|-----------|--------|------------|-----------------| +| videos.list (50 IDs) | 100 | 4x/Tag | 400 | +| channels.list | 100 | 1x/Tag | 100 | +| commentThreads.list | 100 | 4x/Tag | 400 | +| comments.list (Replies) | 100 | 4x/Tag | 400 | +| videos.insert (Upload) | 1.600 | ~2/Tag | 3.200 | +| **Gesamt** | | | **~4.500** | + +Budget: ~45% des Tageslimits. Spielraum für manuelle API-Calls. + +### Migration + +Neue Felder in YouTubeContent (Phase 2 ROI): +```sql +ALTER TABLE youtube_content + ADD COLUMN IF NOT EXISTS "costs_estimated_production_hours" numeric, + ADD COLUMN IF NOT EXISTS "costs_estimated_production_cost" numeric, + ADD COLUMN IF NOT EXISTS "costs_estimated_revenue" numeric; +``` + +### Error Handling + +Alle neuen Services folgen bestehendem Pattern: +- Try/Catch mit strukturiertem Logging (SafeLogger) +- YouTube API-Fehler: Retry mit exponential Backoff (max 3) +- Quota-Exceeded (403): Job delayed auf nächsten Tag +- Token-Expired (401): Auto-Refresh via TokenRefreshService +- Upload-Fehler: Resumable Upload mit Checkpoint + +--- + +## Abhängigkeiten zwischen Phasen + +``` +Phase 1 (API) ──────────► Phase 2 (Analytics) + │ │ + │ Metriken-Daten │ Dashboard-Patterns + │ verfügbar │ + ▼ ▼ +Phase 3 (Workflow) ──────► Phase 4 (Kalender) + │ │ + │ Status-Logik │ Scheduling-Daten + │ Deadline-System │ Konflikt-Erkennung + └──────────────────────────┘ +``` + +Phase 1 muss zuerst abgeschlossen werden (Datengrundlage für Analytics). +Phasen 3 und 4 können teilweise parallel entwickelt werden. + +--- + +## Neue Dateien (Übersicht) + +### Phase 1 +- `src/lib/integrations/youtube/VideoMetricsSyncService.ts` +- `src/lib/integrations/youtube/ChannelMetricsSyncService.ts` +- `src/lib/integrations/youtube/VideoUploadService.ts` +- `src/lib/queue/jobs/youtube-upload-job.ts` +- `src/lib/queue/workers/youtube-upload-worker.ts` +- `src/app/(payload)/api/youtube/upload/route.ts` +- `src/app/(payload)/api/cron/youtube-metrics-sync/route.ts` +- `src/app/(payload)/api/cron/youtube-channel-sync/route.ts` + +### Phase 2 +- `src/lib/integrations/youtube/YouTubeAnalyticsClient.ts` + +### Phase 3 +- `src/hooks/youtubeContent/autoStatusTransitions.ts` +- `src/app/(payload)/api/cron/deadline-reminders/route.ts` +- `src/app/(payload)/api/youtube/capacity/route.ts` + +### Phase 4 +- `src/components/admin/ContentCalendar.tsx` +- `src/components/admin/ContentCalendar.module.scss` +- `src/lib/youtube/ConflictDetectionService.ts` +- `src/app/(payload)/api/youtube/calendar/route.ts` + +### Geänderte Dateien +- `src/lib/integrations/youtube/YouTubeClient.ts` (neue Methoden) +- `src/lib/integrations/youtube/oauth.ts` (neue Scopes) +- `src/lib/integrations/youtube/CommentsSyncService.ts` (Reply-Threads) +- `src/collections/YouTubeContent.ts` (ROI-Felder) +- `src/app/(payload)/api/youtube/analytics/route.ts` (neue Tabs) +- `src/components/admin/YouTubeAnalyticsDashboard.tsx` (neue Tabs) +- `src/lib/queue/queue-service.ts` (youtube-upload Queue) +- `src/payload.config.ts` (Content Calendar View) +- `vercel.json` (neue Cron-Jobs) +- `package.json` (FullCalendar Dependencies)