Admin/YouTubeResearch/Index
YouTube Data API Channel-Suche mit Topic-ID-Filter, Batch-Suche mit Qualitaetsfilter, Keyword-Suggestions, Ergebnis-Archivierung und Import in den Admin-Workspace.
Route: /admin/youtube-research
View: resources/views/livewire/admin/youtube-research/index.blade.php
Location: app/Livewire/Admin/YouTubeResearch/Index.php
Autorisierung: Admin Gate via Middleware
Public Properties
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
$query | string | '' | Suchbegriff (Einzelsuche) |
$topicId | string | '' | YouTube Topic-ID Filter |
$topicOptions | array | [...] | Verfuegbare Topic-IDs (statisch geladen in mount()) |
$batchKeywords | string | '' | Keywords fuer Batch-Suche (eine pro Zeile) |
$batchTopicId | string | '' | Topic-ID fuer Batch |
$batchLanguage | string | '' | Sprache fuer Batch |
$batchRegion | string | '' | Region fuer Batch |
$batchMinSubscribers | int|null | null | Min. Subscriber fuer Batch |
$batchMinVideos | int|null | null | Min. Videos fuer Batch |
$batchProgress | array|null | null | Echtzeit-Fortschritt der laufenden Batch |
$showJsonModal | bool | false | JSON-Payload Modal offen |
$showUrlsModal | bool | false | URL-Export Modal offen |
$showErrorModal | bool | false | Error-Payload Modal offen |
$selectedQueryId | int|null | null | Aktuell ausgewaehlte Query-ID |
$selectedUrls | array | [] | URLs der ausgewaehlten Query |
$selectedPayload | array|null | null | JSON-Payload der ausgewaehlten Query |
$selectedErrorPayload | array|null | null | Error-Payload |
$lastImportedQueryId | int|null | null | Zuletzt importierte Query-ID |
Topic-IDs
Die Component laedt ueber 60 YouTube Topic-IDs in mount(), gruppiert nach Kategorien:
| Kategorie | Beispiel-Topics |
|---|---|
| Music | Music (parent), Pop, Rock, Hip Hop, Jazz, Classical |
| Gaming | Gaming (parent), Action, RPG, Casual, Racing |
| Sports | Sports (parent), Football, Basketball, Tennis, MMA |
| Entertainment | Entertainment (parent), Movies, TV Shows, Humor |
| Lifestyle | Lifestyle (parent), Fashion, Fitness, Food, Beauty |
| Society | Society (parent), Business, Health, Politics, Religion |
| Knowledge | Knowledge |
Actions
| Method | Beschreibung |
|---|---|
runSearch(YouTubeDataApiClient $api) | Einzelsuche ausfuehren, Ergebnisse in DB speichern |
startBatchSearch() | Batch-Suche starten: Keywords parsen, ProcessYouTubeResearchBatch-Job dispatchen |
openJsonModal(int $queryId) | Raw JSON-Response anzeigen |
openUrlsModal(int $queryId) | URL-Liste anzeigen |
openErrorModal(int $queryId) | Error-Payload anzeigen |
closeModals() | Alle Modals schliessen |
importUrls(int $queryId, AdminWorkspaceManager $manager) | URLs in Admin-Workspace importieren |
importBatchUrls(string $batchId, AdminWorkspaceManager $manager) | Alle URLs einer Batch importieren |
getKeywordSuggestions() | AI-Keywords aus Profilen laden (mit Cooldown) |
fetchYouTubeSuggestions(string $partial) | YouTube Autocomplete-Vorschlaege laden |
Einzelsuche
runSearch()
→ validate(query: required|min:2|max:200, topicId: nullable)
→ fetchYouTubeChannels()
→ rawGetWithKeyPool('search', params, 'research')
→ Paginierung ueber nextPageToken (max 1000 Ergebnisse)
→ Channel-URLs extrahieren
→ YouTubeResearchQuery::create(...)
→ resetPage()
Die Suche nutzt rawGetWithKeyPool() mit dem Pool 'research' fuer dedizierten API-Key-Verbrauch. Paginierung ueber nextPageToken sammelt bis zu 1000 Channel-URLs.
Batch-Suche
graph TD
A[Admin gibt Keywords ein] --> B[startBatchSearch]
B --> C[Keywords parsen + deduplizieren]
C --> D[YouTubeResearchQuery pro Keyword erstellen]
D --> E[ProcessYouTubeResearchBatch Job dispatchen]
E --> F{Fuer jedes Keyword}
F --> G[YouTube Search API]
G --> H{Qualitaetsfilter?}
H -->|Ja| I[channels.list: Subscriber/Video Check]
H -->|Nein| J[Ergebnisse speichern]
I --> J
J --> K[YouTubeResearchBatchProgress Event]
K --> L[Echtzeit-UI-Update via WebSocket]
F --> M[Batch abgeschlossen]
Batch-Filter
| Filter | Typ | Beschreibung |
|---|---|---|
topic_id | string | YouTube Topic-ID (Music, Gaming, Sports, etc.) |
relevance_language | string | ISO 639-1 Sprache |
region_code | string | ISO 3166-1 Region |
min_subscribers | int | Mindest-Subscriber-Anzahl |
min_videos | int | Mindest-Video-Anzahl |
Batch-Limits
- Max. 100 Keywords pro Batch (dedupliziert, vorher 20)
- Max. 1000 Ergebnisse pro Keyword
- Qualitaetsfilter: 50 Channels pro
channels.list-Chunk (1 Quota-Unit/Call) - Overflow-Erhalt: Keywords ueber dem 100-Limit bleiben im Eingabefeld fuer den naechsten Batch. Info-Toast zeigt: "X Keywords gestartet, Y verbleiben im Feld"
Keyword-Suggestions
Zwei Quellen fuer Keyword-Vorschlaege:
AI-Keywords aus Profilen
YouTubeResearchKeywordSuggester extrahiert die haeufigsten AI-Keywords aus bestehenden YouTube-Profilen. Keywords auf 14-Tage-Cooldown werden uebersprungen. Zeigt Top-20 mit Frequency-Count.
YouTube Suggest API
YouTubeSuggestClient liefert Autocomplete-Vorschlaege von YouTubes oeffentlichem Suggest-Endpoint. Kein API-Key erforderlich. Response-Zeit ca. 200ms.
Import-Ablauf
importUrls($queryId)
→ AdminWorkspaceManager::ensureAdminWorkspace()
→ URLs parsen via YouTubeUrlParser::parse()
→ Deduplizierung (max 1000 unique URLs)
→ WatcherImportRun + WatcherImportItems erstellen (DB Transaction)
→ ProcessWatcherImportRun dispatchen (Queue: imports-youtube-priority)
→ YouTubeResearchQuery::update(['imported_at' => now()])
→ Toast: "X URLs zur Queue hinzugefuegt"
Der Import erstellt einen WatcherImportRun im Admin-Workspace und dispatcht einen einzelnen ProcessWatcherImportRun-Job (statt N+1 einzelne Jobs). Importierte Queries werden visuell markiert (imported_at).
Datenmodell
YouTubeResearchQuery
youtube_research_queries
├── query: string — Suchbegriff
├── topic_id: string|null — YouTube Topic-ID
├── batch_id: uuid|null — Batch-Identifier
├── relevance_language: string — Sprache-Filter
├── region_code: string — Region-Filter
├── min_subscribers: int|null — Min. Subscriber-Filter
├── min_videos: int|null — Min. Video-Filter
├── result_count: int — Anzahl gefundener Channels
├── filtered_count: int|null — Anzahl nach Qualitaetsfilter
├── result_urls: jsonb — Array der Channel-URLs
├── response_payload: jsonb — Raw API Response (alle Seiten)
├── error_payload: jsonb — Error Response bei Fehler
├── error_message: string — Fehlermeldung
├── status: string — pending/running/completed/failed
├── imported_at: timestamp — Wann in Workspace importiert
└── created_at: timestamp
YouTubeResearchKeywordCooldown
youtube_research_keyword_cooldowns
├── keyword: string (unique) — Keyword
├── used_at: timestamp — Letzter Verwendungszeitpunkt
└── created_at: timestamp
Events
| Richtung | Event | Beschreibung |
|---|---|---|
| dispatch | show-toast | Import-Erfolg/Fehler |
| listen | YouTubeResearchBatchProgress | Echtzeit-Fortschritt via WebSocket (admin-Channel) |
Render
Die render()-Methode paginiert YouTubeResearchQuery (100 pro Seite, neueste zuerst) und zeigt nur die Minimal-Felder (id, query, topic_id, result_count, filtered_count, error_message, status, batch_id, imported_at, created_at).