Instagram Commands
Commands für das Instagram Collector-System: Token-Management, Retry-Logik und Log-Wartung. Instagram-Scrapes laufen nicht über Laravel Queues, sondern über das Collector-System: Browser-Extensions leasen Jobs via API, scrapen Profile und melden Ergebnisse zurück.
collector:requeue-expired-leases
Reclaimed verwaiste Collector-Jobs, deren Lease abgelaufen ist. Wenn die Collector-API ausfällt oder ein Client abstürzt, bleiben geleaste Jobs verwaist — kein Client pollt /lease um sie zurückzuholen. Dieser Command stellt sie automatisch wieder in die Queue.
WICHTIG: Lease-Ablauf ist KEIN Fehler. Das Profil hat kein Problem — nur der Client/die API hat nicht reagiert. Jobs werden immer ohne Penalty requeued. Nur explizite /fail-Calls zählen zum fail_count.
collector:requeue-expired-leases
{--dry-run : Vorschau ohne Änderungen}
Beispiele
# Abgelaufene Leases requeuen
php artisan collector:requeue-expired-leases
# Vorschau: was würde requeued?
php artisan collector:requeue-expired-leases --dry-run
Optionen
| Option | Beschreibung |
|---|---|
--dry-run | Nur Anzeige (gruppiert nach Source), keine Statusänderung |
Interne Schritte
- Alle Collector-Jobs mit Status
leasedundlease_expires_at < now()finden - Ältestes abgelaufenes Lease für Diagnose-Output ermitteln
- Im Dry-Run: Gruppierung nach Source ausgeben
- Per
chunkById(500)alle expired Jobs aufqueuedzurücksetzen,leased_by+lease_expires_atnullen - Systemic-Outage-Warnung loggen wenn >500 Jobs requeued (API-Ausfall wahrscheinlich)
Systemic Outage Detection
Wenn mehr als 500 Jobs in einem Lauf requeued werden, wird eine Warning geloggt mit:
- Anzahl requeued Jobs
- Alter des ältesten abgelaufenen Lease (für Dauer der Störung)
Schedule
Alle 5 Minuten (everyFiveMinutes()), withoutOverlapping(5), Heartbeat collector_requeue.
Location: app/Console/Commands/RequeueExpiredCollectorLeases.php
collector:token
Erstellt einen neuen Collector-Client und gibt den Sanctum API-Token aus. Der Token wird von Browser-Extensions für die Authentifizierung an der Collector-API verwendet.
collector:token {name}
Beispiele
# Token für Chrome-Extension erstellen
php artisan collector:token "Chrome Collector"
# Token für Firefox-Extension erstellen
php artisan collector:token "Firefox Collector v2"
Optionen
| Parameter | Beschreibung |
|---|---|
name | Name des Collector-Clients (wird in der DB gespeichert) |
Interne Schritte
CollectorClientRecord in der Datenbank erstellen mit dem angegebenen Namen- Sanctum Token für den Client generieren
- Plaintext-Token auf der Konsole ausgeben (zum Kopieren/Einfügen in die Extension)
Hinweise
- Der Token wird nur einmal im Klartext angezeigt; danach ist nur noch der Hash gespeichert
- Jeder Collector-Client kann mehrere aktive Sessions haben
- Tokens werden für die API-Endpunkte
/api/collector/jobs/lease,/api/collector/jobs/{id}/complete,/api/collector/jobs/{id}/failverwendet - Die Rate-Limiter
collectorinAppServiceProviderschützt die API-Endpunkte
Schedule
Manuell (einmalige Einrichtung pro Extension).
Location: routes/console.php (Artisan::command)
collector:retry-failed
Setzt fehlgeschlagene Instagram Collector-Jobs in einem Zeitraum zurück auf queued.
Anders als YouTube-Jobs (in failed_jobs) werden Instagram-Jobs über die collector_jobs Tabelle verwaltet.
collector:retry-failed
{--from= : Start-Zeitpunkt (erforderlich)}
{--to= : End-Zeitpunkt (erforderlich)}
{--source= : Collector-Source Filter (Default: instagram)}
{--job-type= : Job-Typ Filter (daily_scrape, watcher_import)}
{--error-code= : Error-Code Filter}
{--dry-run : Vorschau ohne Änderungen}
Beispiele
# Alle fehlgeschlagenen Jobs in einem Zeitraum retrien
php artisan collector:retry-failed --from="2026-02-01 00:00:00" --to="2026-02-01 23:59:59"
# Nur Daily-Scrape-Jobs retrien
php artisan collector:retry-failed --from="2026-02-01 00:00:00" --to="2026-02-01 23:59:59" --job-type=daily_scrape
# Nur Rate-Limited Jobs retrien
php artisan collector:retry-failed --from="2026-02-01 00:00:00" --to="2026-02-01 23:59:59" --error-code=rate_limited
# Vorschau
php artisan collector:retry-failed --from="2026-02-01 00:00:00" --to="2026-02-01 23:59:59" --dry-run
Optionen
| Option | Beschreibung |
|---|---|
--from= | Start-Zeitpunkt (Pflicht) |
--to= | End-Zeitpunkt (Pflicht) |
--source= | Collector-Source (Default: instagram) |
--job-type= | daily_scrape oder watcher_import |
--error-code= | z.B. rate_limited, not_found, timeout |
--dry-run | Nur Anzeige, keine Statusänderung |
Interne Schritte
- Fehlgeschlagene Collector-Jobs im Zeitraum laden
- Optional nach
source,job-typeunderror-codefiltern - Ergebnisse nach Job-Typ und Error-Code gruppiert anzeigen
- Bestätigung abfragen (außer bei
--dry-run) - Status von
failedaufqueuedzurücksetzen - Jobs werden beim nächsten Collector-Lease automatisch wieder vergeben
Schedule
Manuell (bei Bedarf nach API-Ausfällen oder Rate-Limiting).
Location: app/Console/Commands/RetryFailedCollectorJobs.php
collector:prune-logs
Löscht abgeschlossene und fehlgeschlagene Collector-Job-Logs nach dem Retention-Fenster.
Nur Jobs mit Status completed oder failed werden gelöscht; offene Jobs bleiben erhalten.
collector:prune-logs
{--days=30 : Aufbewahrungsdauer in Tagen}
Beispiele
# Standard: Logs älter als 30 Tage löschen
php artisan collector:prune-logs
# Kürzere Retention: Logs älter als 14 Tage löschen
php artisan collector:prune-logs --days=14
# Langzeit-Retention: Logs älter als 7 Tage löschen (Schedule-Default)
php artisan collector:prune-logs --days=7
Optionen
| Option | Beschreibung |
|---|---|
--days=30 | Anzahl Tage die Logs behalten werden (Default: 30) |
Interne Schritte
- Collector-Jobs mit Status
completedoderfailedselektieren - Filter:
updated_atälter als angegebene Tage - Records löschen
- Gelöschte Anzahl auf der Konsole ausgeben
Hinweise
queuedundleasedJobs werden nie gelöscht- Das Retention-Fenster basiert auf
updated_at(nichtcreated_at) - Der Schedule nutzt
--days=7, der Command-Default ist--days=30
Schedule
Alle 6 Stunden (03:15, 09:15, 15:15, 21:15 UTC) mit --days=7.
Location: app/Console/Commands/PruneCollectorLogs.php
instagram:queue-related
Berechnet Related Instagram Profiles für ein einzelnes Profil oder in Bulk. Findet verwandte Instagram-Profile basierend auf Keyword-Matching und Multi-Faktor Relevance-Scoring. Nutzt nur lokale Daten — keine externen API-Calls, keine Quota-Kosten.
instagram:queue-related
{--profile= : Einzelnes Profil per ID queuen}
{--days= : Nur Profile nicht berechnet in letzten N Tagen (Default: 90)}
{--limit=500 : Max Profile pro Run}
{--force : Neuberechnung erzwingen}
{--dry-run : Vorschau ohne Jobs zu dispatchen}
Optionen
| Option | Typ | Default | Beschreibung |
|---|---|---|---|
--profile= | int | – | Einzelnes Instagram-Profil per ID queuen |
--days= | int | 90 | Stale-Schwelle in Tagen |
--limit= | int | 500 | Maximale Anzahl Profile pro Bulk-Lauf |
--force | flag | – | Neuberechnung erzwingen, auch für kürzlich berechnete |
--dry-run | flag | – | Nur Anzeige, keine Jobs dispatchen |
Beispiele
# Einzelnes Profil manuell berechnen
php artisan instagram:queue-related --profile=12345
# Bulk: alle Instagram-Profile mit veralteten Related-Daten
php artisan instagram:queue-related
# Force Recalculation, kleiner Batch
php artisan instagram:queue-related --force --limit=50
# Vorschau: welche Profile wären betroffen?
php artisan instagram:queue-related --dry-run
# Nur Profile die 30+ Tage nicht berechnet wurden
php artisan instagram:queue-related --days=30 --limit=100
Interne Logik
- Single-Profile Mode (
--profile=ID): Lädt Profil, prüft obinstagram, dispatched sofort - Bulk Mode: Filtert nach
tracking_enabled,notExcluded(), Stale-Schwelle, sortiert nach Followern - Priorität: Noch nie berechnete Profile zuerst, dann veraltete
Job: FindRelatedInstagramProfiles
| Property | Wert |
|---|---|
| Queue | instagram-related-profiles |
| Tries | 3 |
| Unique For | 3600 (1 Stunde) |
| Backoff | [60, 300, 900] (1min, 5min, 15min) |
| Max Related | 25 |
| Max Candidates | 500 |
Matching-Score (0-100)
| Kriterium | Punkte | Beschreibung |
|---|---|---|
| Parsed Link Match | 0-30 | Direkter @handle oder Instagram-URL in Bio (approved: 30, auto: 25, handle: 20) |
| Popularity Bonus | 0-20 | Follower-Tier (1K → 2, 10K → 8, ..., 1M+ → 20) |
| Keyword Match | 0-25 | Gewichtet: Bio=6, Title=5, Handle=3 |
| Follower Similarity | 0-15 | Log-Scale (gleiche Größenordnung = 15) |
| Favorites Bonus | 0-10 | Config-Tiers (20+ Favorites → 10) |
| Name Similarity | 0-10 | similar_text() > 30% |
| Same Profile Type | 0-10 | Business/Creator/Private Match |
| Language/Country | 0-10 | +5 pro Match (Sprache, Land) |
Schedule
Manuell. Für automatischen Ausbau nutze cross-platform:auto-fill-related (das auch Instagram → YouTube abdeckt).
Location: app/Console/Commands/QueueRelatedInstagramProfiles.php
instagram:prioritize-rotation
Priorisiert alle queued Instagram Daily-Scrape-Jobs (nicht low_priority), sodass sie vor Import-Jobs verarbeitet werden.
Sinnvoll wenn sich nach einem Collector-Ausfall viele Jobs aufgestaut haben und die taeglichen Rotation-Scrapes Vorrang haben sollen.
instagram:prioritize-rotation
{--chunk=500 : Anzahl Jobs pro Batch}
{--dry-run : Vorschau ohne Änderungen}
Beispiele
# Alle queued Daily-Scrape-Jobs priorisieren
php artisan instagram:prioritize-rotation
# Vorschau: was würde priorisiert?
php artisan instagram:prioritize-rotation --dry-run
# Kleinere Batches bei hoher DB-Last
php artisan instagram:prioritize-rotation --chunk=100
Optionen
| Option | Typ | Default | Beschreibung |
|---|---|---|---|
--chunk | int | 500 | Anzahl Jobs pro Batch-Update |
--dry-run | flag | – | Nur Anzeige, keine Änderungen |
Interne Logik
- Queued Instagram Daily-Scrape-Jobs suchen (
low_priority = false) - Hoechsten aktuellen
priority-Wert in der Queue ermitteln - Alle gefundenen Jobs auf
priority = max + 1setzen (Bulk-Update per Chunk viachunkById) - Dadurch werden diese Jobs vor allen anderen queued Jobs geleased
Hinweis: Kein ->oldest() im Query — chunkById() iteriert intern mit WHERE id > $lastId ORDER BY id ASC. Ein zusaetzliches ORDER BY created_at wuerde chunkById dazu bringen, Records zu ueberspringen und nach wenigen Tausend abzubrechen.
Schedule
Manuell (bei Queue-Stau oder nach Collector-Ausfall).
Location: app/Console/Commands/PrioritizeInstagramRotation.php
instagram:deprioritize-non-rotation
Einmal-Command zum Korrigieren bestehender Queue-Prioritaeten. Setzt alle queued non-rotation Jobs auf eine niedrige Priority, damit taegliche Rotation-Scrapes Vorrang bekommen.
instagram:deprioritize-non-rotation
{--priority=5 : Ziel-Priority fuer non-rotation Jobs}
{--dry-run : Vorschau ohne Änderungen}
Beispiele
# Vorschau: welche Jobs wuerden geaendert?
php artisan instagram:deprioritize-non-rotation --dry-run
# Alle non-rotation Jobs auf Priority 5 setzen (Default)
php artisan instagram:deprioritize-non-rotation
# Kombination mit prioritize-rotation (empfohlene Reihenfolge):
php artisan instagram:deprioritize-non-rotation
php artisan instagram:prioritize-rotation
Optionen
| Option | Typ | Default | Beschreibung |
|---|---|---|---|
--priority | int | 5 | Ziel-Priority fuer non-rotation Jobs |
--dry-run | flag | – | Nur Anzeige, keine Änderungen |
Interne Logik
Zwei Gruppen werden identifiziert und herabgestuft:
- Watcher-Import Jobs:
payload->job_type = 'watcher_import'mit Priority > Zielwert - Daily-Scrape Jobs fuer neue Profile:
payload->job_type = 'daily_scrape'derensocial_profile_idauf ein Profil mitlast_daily_scrape_on IS NULLzeigt (nie gescraped)
Beide Gruppen erhalten die Ziel-Priority (Default: 5). Da der Collector low_priority=false Jobs immer zuerst bedient, landen diese Jobs effektiv hinter der regulaeren Rotation.
Schedule
Manuell (einmalige Korrektur nach Deployment oder nach Massen-Import).
Location: app/Console/Commands/DeprioritizeNonRotationJobs.php
instagram:queue-youtube-research
Dispatcht YouTube Research Jobs für Instagram-Profile. Durchsucht die YouTube Data API nach passenden Channels basierend auf Instagram-Profilname, Handle und AI-Keywords. Gefundene Channels werden nach Abonnenten/Video-Anzahl gefiltert und in den Admin-Workspace importiert.
instagram:queue-youtube-research
{--dry-run : Nur anzeigen, nicht dispatchen}
{--force : Quota-Check ignorieren}
{--limit= : Override tägliches Limit}
Beispiele
# Standard: bis zu 10 Profile recherchieren
php artisan instagram:queue-youtube-research
# Vorschau: welche Profile würden gewählt?
php artisan instagram:queue-youtube-research --dry-run
# Mehr Profile als das Tageslimit
php artisan instagram:queue-youtube-research --limit=50
# Quota-Check ignorieren (z.B. für manuelle Tests)
php artisan instagram:queue-youtube-research --force
Optionen
| Option | Typ | Default | Beschreibung |
|---|---|---|---|
--dry-run | flag | false | Nur Anzeige, keine Jobs dispatchen |
--force | flag | false | Quota- und Key-Check ignorieren |
--limit= | int | Config (10) | Override für das tägliche Profil-Limit |
Suchstrategie (pro Profil)
Bis zu 3 Suchanfragen werden generiert:
| Priorität | Typ | Beschreibung |
|---|---|---|
| S1 | Name | Display-Name (Titel) des Instagram-Profils |
| S2 | Handle | Username — übersprungen wenn im Titel enthalten |
| E9 | Variante | Handle ohne Underscores/Punkte/Trailing-Numbers |
| S3 | Keywords | Top AI-Keywords (mit 14-Tage-Cooldown, E4) |
Interne Logik
- Tageslimit: Cache-Counter
ig_yt_research_dispatched_today:{date} - Quota-Check:
ResearchQuotaService— Abbruch bei <5% verfügbarer Research-Quota (konfigurierbar) - Live-Key-Check:
YouTubeQuotaGuard::canMakeCall('research')— prüft ob aktive Keys vorhanden und Circuit Breaker nicht offen - Profil-Selektion: Prio 1 (nie researcht, nach Followern), Prio 2 (stale > Zyklus-Tage, älteste zuerst)
- Job-Dispatch:
ProcessInstagramYouTubeResearchpro Profil aufimports-youtube-priorityQueue - Ergebnisse: YouTubeResearchQuery-Einträge mit
batch_id = ig-yt-auto-{date}-{profileId}(E6) - Import: Gefilterte Channels als WatcherImportRun in Admin-Workspace
Quota-Verbrauch
| API-Call | Kosten | Pro Profil (max) |
|---|---|---|
search.list | 100 Units | 300 Units (3 Suchen) |
channels.list | 1 Unit | ~3 Units |
| Gesamt | ~303 Units/Profil |
Bei 10 Profilen/Tag: ~3.030 Units = ~30% eines Research-Keys (10.000 Units/Tag).
Konfiguration
# Tägliches Profil-Limit (Default: 10)
INSTAGRAM_YOUTUBE_RESEARCH_DAILY_LIMIT=10
# Zyklus in Tagen bevor ein Profil erneut researcht wird (Default: 365)
INSTAGRAM_YOUTUBE_RESEARCH_CYCLE_DAYS=365
# Max. YouTube-Suchanfragen pro Profil (Default: 3)
INSTAGRAM_YOUTUBE_RESEARCH_MAX_SEARCHES=3
# Min. verfügbare Quota in % bevor Research pausiert (Default: 5)
INSTAGRAM_YOUTUBE_RESEARCH_MIN_QUOTA_PERCENT=5
# Min. Abonnenten für Channel-Import (Default: 1000)
INSTAGRAM_YOUTUBE_RESEARCH_MIN_SUBSCRIBERS=1000
# Min. Videos für Channel-Import (Default: 10)
INSTAGRAM_YOUTUBE_RESEARCH_MIN_VIDEOS=10
# Keyword-Cooldown in Tagen (Default: 14)
INSTAGRAM_YOUTUBE_RESEARCH_KEYWORD_COOLDOWN_DAYS=14
Schedule
Täglich 10:00 UTC, withoutOverlapping(120), runInBackground(), Heartbeat ig_yt_research.
Location: app/Console/Commands/QueueInstagramYouTubeResearch.php
Job: app/Jobs/YouTube/ProcessInstagramYouTubeResearch.php
Collector-System Architektur
Das Collector-System unterscheidet sich fundamental von den YouTube-Queues:
┌──────────────────┐ ┌──────────────┐ ┌──────────────────┐
│ queue-daily- │────>│ collector_ │<────│ Browser │
│ instagram │ │ jobs (DB) │ │ Extension │
│ (Artisan Command) │ │ │ │ (Sanctum Auth) │
└──────────────────┘ │ queued ──────>│────>│ /lease │
│ leased ──────>│ │ /complete │
│ completed │<────│ /fail │
│ failed │ └──── ──────────────┘
└──────────────┘
| API-Endpunkt | Methode | Beschreibung |
|---|---|---|
/api/collector/jobs/lease | POST | Nächsten Job leasen |
/api/collector/jobs/{id}/complete | POST | Job als erfolgreich melden |
/api/collector/jobs/{id}/fail | POST | Job als fehlgeschlagen melden |
Die Lease-TTL beträgt standardmäßig 300 Sekunden (config('collector.lease_ttl_seconds')).
Abgelaufene Leases werden automatisch für das nächste Lease verfügbar.
Auto-Recovery
Bei API-Ausfällen oder Client-Crashes werden verwaiste Jobs automatisch reclaimed:
graph TD
A["collector_jobs<br/>status: leased<br/>lease_expires_at < now()"] -->|"collector:requeue-expired-leases<br/>(alle 5 Min)"| B["status: queued<br/>leased_by: null<br/>lease_expires_at: null"]
B -->|"Browser Extension pollt /lease"| C["status: leased<br/>(neuer Client)"]
C -->|"/complete"| D["status: completed"]
C -->|"/fail"| E["fail_count++"]
E -->|"fail_count < max"| B
E -->|"fail_count >= max"| F["status: failed<br/>(permanent)"]
style A fill:#e74c3c,color:#fff
style B fill:#3498db,color:#fff
style D fill:#27ae60,color:#fff
style F fill:#7f8c8d,color:#fff
| Config | Env-Variable | Default | Beschreibung |
|---|---|---|---|
collector.max_fail_count | COLLECTOR_MAX_FAIL_COUNT | 3 | Max. explizite Failures bevor Job permanent failed |
collector.lease_ttl_seconds | COLLECTOR_LEASE_TTL_SECONDS | 300 | Lease-Dauer in Sekunden |