Zum Hauptinhalt springen

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

OptionBeschreibung
--dry-runNur Anzeige (gruppiert nach Source), keine Statusänderung

Interne Schritte

  1. Alle Collector-Jobs mit Status leased und lease_expires_at < now() finden
  2. Ältestes abgelaufenes Lease für Diagnose-Output ermitteln
  3. Im Dry-Run: Gruppierung nach Source ausgeben
  4. Per chunkById(500) alle expired Jobs auf queued zurücksetzen, leased_by + lease_expires_at nullen
  5. 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

ParameterBeschreibung
nameName des Collector-Clients (wird in der DB gespeichert)

Interne Schritte

  1. CollectorClient Record in der Datenbank erstellen mit dem angegebenen Namen
  2. Sanctum Token für den Client generieren
  3. 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}/fail verwendet
  • Die Rate-Limiter collector in AppServiceProvider schü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

OptionBeschreibung
--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-runNur Anzeige, keine Statusänderung

Interne Schritte

  1. Fehlgeschlagene Collector-Jobs im Zeitraum laden
  2. Optional nach source, job-type und error-code filtern
  3. Ergebnisse nach Job-Typ und Error-Code gruppiert anzeigen
  4. Bestätigung abfragen (außer bei --dry-run)
  5. Status von failed auf queued zurücksetzen
  6. 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

OptionBeschreibung
--days=30Anzahl Tage die Logs behalten werden (Default: 30)

Interne Schritte

  1. Collector-Jobs mit Status completed oder failed selektieren
  2. Filter: updated_at älter als angegebene Tage
  3. Records löschen
  4. Gelöschte Anzahl auf der Konsole ausgeben

Hinweise

  • queued und leased Jobs werden nie gelöscht
  • Das Retention-Fenster basiert auf updated_at (nicht created_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

OptionTypDefaultBeschreibung
--profile=intEinzelnes Instagram-Profil per ID queuen
--days=int90Stale-Schwelle in Tagen
--limit=int500Maximale Anzahl Profile pro Bulk-Lauf
--forceflagNeuberechnung erzwingen, auch für kürzlich berechnete
--dry-runflagNur 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

  1. Single-Profile Mode (--profile=ID): Lädt Profil, prüft ob instagram, dispatched sofort
  2. Bulk Mode: Filtert nach tracking_enabled, notExcluded(), Stale-Schwelle, sortiert nach Followern
  3. Priorität: Noch nie berechnete Profile zuerst, dann veraltete

Job: FindRelatedInstagramProfiles

PropertyWert
Queueinstagram-related-profiles
Tries3
Unique For3600 (1 Stunde)
Backoff[60, 300, 900] (1min, 5min, 15min)
Max Related25
Max Candidates500

Matching-Score (0-100)

KriteriumPunkteBeschreibung
Parsed Link Match0-30Direkter @handle oder Instagram-URL in Bio (approved: 30, auto: 25, handle: 20)
Popularity Bonus0-20Follower-Tier (1K → 2, 10K → 8, ..., 1M+ → 20)
Keyword Match0-25Gewichtet: Bio=6, Title=5, Handle=3
Follower Similarity0-15Log-Scale (gleiche Größenordnung = 15)
Favorites Bonus0-10Config-Tiers (20+ Favorites → 10)
Name Similarity0-10similar_text() > 30%
Same Profile Type0-10Business/Creator/Private Match
Language/Country0-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

OptionTypDefaultBeschreibung
--chunkint500Anzahl Jobs pro Batch-Update
--dry-runflagNur Anzeige, keine Änderungen

Interne Logik

  1. Queued Instagram Daily-Scrape-Jobs suchen (low_priority = false)
  2. Hoechsten aktuellen priority-Wert in der Queue ermitteln
  3. Alle gefundenen Jobs auf priority = max + 1 setzen (Bulk-Update per Chunk via chunkById)
  4. 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

OptionTypDefaultBeschreibung
--priorityint5Ziel-Priority fuer non-rotation Jobs
--dry-runflagNur Anzeige, keine Änderungen

Interne Logik

Zwei Gruppen werden identifiziert und herabgestuft:

  1. Watcher-Import Jobs: payload->job_type = 'watcher_import' mit Priority > Zielwert
  2. Daily-Scrape Jobs fuer neue Profile: payload->job_type = 'daily_scrape' deren social_profile_id auf ein Profil mit last_daily_scrape_on IS NULL zeigt (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

OptionTypDefaultBeschreibung
--dry-runflagfalseNur Anzeige, keine Jobs dispatchen
--forceflagfalseQuota- und Key-Check ignorieren
--limit=intConfig (10)Override für das tägliche Profil-Limit

Suchstrategie (pro Profil)

Bis zu 3 Suchanfragen werden generiert:

PrioritätTypBeschreibung
S1NameDisplay-Name (Titel) des Instagram-Profils
S2HandleUsername — übersprungen wenn im Titel enthalten
E9VarianteHandle ohne Underscores/Punkte/Trailing-Numbers
S3KeywordsTop AI-Keywords (mit 14-Tage-Cooldown, E4)

Interne Logik

  1. Tageslimit: Cache-Counter ig_yt_research_dispatched_today:{date}
  2. Quota-Check: ResearchQuotaService — Abbruch bei <5% verfügbarer Research-Quota (konfigurierbar)
  3. Live-Key-Check: YouTubeQuotaGuard::canMakeCall('research') — prüft ob aktive Keys vorhanden und Circuit Breaker nicht offen
  4. Profil-Selektion: Prio 1 (nie researcht, nach Followern), Prio 2 (stale > Zyklus-Tage, älteste zuerst)
  5. Job-Dispatch: ProcessInstagramYouTubeResearch pro Profil auf imports-youtube-priority Queue
  6. Ergebnisse: YouTubeResearchQuery-Einträge mit batch_id = ig-yt-auto-{date}-{profileId} (E6)
  7. Import: Gefilterte Channels als WatcherImportRun in Admin-Workspace

Quota-Verbrauch

API-CallKostenPro Profil (max)
search.list100 Units300 Units (3 Suchen)
channels.list1 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-EndpunktMethodeBeschreibung
/api/collector/jobs/leasePOSTNächsten Job leasen
/api/collector/jobs/{id}/completePOSTJob als erfolgreich melden
/api/collector/jobs/{id}/failPOSTJob 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
ConfigEnv-VariableDefaultBeschreibung
collector.max_fail_countCOLLECTOR_MAX_FAIL_COUNT3Max. explizite Failures bevor Job permanent failed
collector.lease_ttl_secondsCOLLECTOR_LEASE_TTL_SECONDS300Lease-Dauer in Sekunden