Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Wistia Laravel Package

jeffersongoncalves/laravel-wistia

Laravel Wistia is a lightweight package that integrates Wistia video hosting with your Laravel app, offering a simple API wrapper for uploading, managing, and embedding videos. It helps you add Wistia media features to your projects with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Initial 1.0.0 covers the core Wistia surface: account, projects, medias, stats, and captions. Minimal setup:

  1. Install and publish config:
    composer require <vendor>/laravel-wistia
    php artisan vendor:publish --tag=wistia-config
    
  2. Set credentials in .env:
    WISTIA_API_KEY=your-token
    WISTIA_TIMEOUT=10
    
    If your app already centralizes third-party tokens, you can skip the env vars entirely and set services.wistia.token — the package falls back to it.
  3. Verify the connection before writing any feature code:
    $account = WistiaClient::account();
    
    if ($account === null) {
        // bad token, wrong config cache, or network egress blocked
    }
    

Where to look first: config/wistia.php (timeout and token resolution), then the client class to confirm exact method signatures and parameter names — the release notes list the endpoints but not the argument order, so read the source once before wiring up calls.

Best first use case: a read-only dashboard widget or an artisan command that pulls WistiaClient::accountStats() and WistiaClient::projects() to prove credentials and connectivity work in every environment (local, staging, queue workers) before you build write paths.

Implementation Patterns

Never call the client directly from a controller. Wrap it in a small service or repository so the null-safe contract is handled in one place:

class WistiaMediaRepository
{
    public function find(string $hashedId): ?array
    {
        return WistiaClient::media($hashedId);
    }

    public function update(string $hashedId, array $attributes): bool
    {
        $result = WistiaClient::updateMedia($hashedId, $attributes);

        if ($result === null || $result === false) {
            Log::warning('Wistia media update failed', ['media' => $hashedId]);
        }

        return (bool) $result;
    }
}

Mirror metadata locally, don't proxy live. Media and project lists change slowly. Sync them into your own tables via a scheduled command, then join against local records for search, filters, and admin listings. This is the single biggest day-to-day win: you avoid paginating the API on every page load.

// app/Console/Commands/SyncWistiaMedias.php
$medias = WistiaClient::medias(['project_id' => $projectId, 'per_page' => 100]);

if ($medias === null) {
    $this->error('Wistia unavailable, keeping existing rows.');
    return self::FAILURE;   // let the scheduler/alerting see it
}

foreach ($medias as $media) {
    WistiaMedia::updateOrCreate(
        ['hashed_id' => $media['hashed_id']],
        ['name' => $media['name'], 'duration' => $media['duration'] ?? null, 'synced_at' => now()]
    );
}

Cache stats aggressively. accountStats(), project stats, media stats, and the date/engagement/visitor/event endpoints are the most expensive calls and the least time-sensitive. Cache per resource with a short TTL and refresh out-of-band:

$stats = Cache::remember("wistia:media:{$id}:stats", now()->addMinutes(30), fn () =>
    WistiaClient::mediaStats($id)
);

Use Cache::remember only where a null result is acceptable to cache — see the gotchas below; for stats dashboards you generally want to avoid caching failures, so prefer an explicit get/put.

Queue anything that writes. Create, update, delete, copy, and caption creation belong in jobs, not in the request cycle. This lets you add retry/backoff. But because the client swallows errors, a naive job will "succeed" on failure — make the job throw explicitly so retries actually fire:

public function handle(): void
{
    if (WistiaClient::copyMedia($this->hashedId, $this->destinationProjectId) === null) {
        throw new RuntimeException("Wistia copy failed for {$this->hashedId}");
    }
}

Captions are worth wiring into your upload pipeline: after a media is created or synced, push English captions with createCaption so accessibility isn't a manual afterthought.

Multi-tenant / per-user tokens. If you serve multiple Wistia accounts, don't rely on the singleton resolving the env token once. Set the token immediately before the call and forget the resolved instance:

config(['wistia.api_key' => $tenant->wistia_token]);
app()->forgetInstance(WistiaClient::class);

$projects = WistiaClient::projects();

Testing. Bind a fake client in the container (or use Http::fake() if it's built on the HTTP client) and assert your repository converts null into the domain outcome you expect. Test the failure branch, not just the happy path — that's where this package's design forces you to write code.

Gotchas and Tips

  • null is overloaded. A null return means "network failure or non-2xx" — it never means "empty result." Confirm what an empty collection looks like (likely []) so your if (! $medias) checks don't confuse "no videos in this project" with "API is down." Treat only === null as failure.
  • false on writes is not "not found." A false from delete/update conflates 404 with 500 with DNS failure. If you need to distinguish, you'll have to log or introspect the underlying response — check whether the package exposes it; if not, keep your own logging wrapper.
  • Silent failures in production. Because nothing throws, a revoked token or expired API key manifests as empty dashboards, not error pages. Add a heartbeat (scheduled account() check) and alert on null. Also alert on the rate of nulls, not just a single miss.
  • Timeouts bite on stats. WISTIA_TIMEOUT at 5–10s is fine for account() but the date-range and engagement endpoints can be slower. Raise it for those calls if the package allows per-request overrides; otherwise set it high enough for your slowest endpoint and accept the slower failure path.
  • Config caching. After changing WISTIA_API_KEY or services.wistia.token in production, run php artisan config:cache — an old cached config is the most common "it works locally" cause. Remember the fallback order: env key first, then services.wistia.token.
  • Queue workers hold stale config. Long-running workers won't see config changes until restarted (queue:restart). Relevant if you swap tokens per tenant.
  • Parameter names are undocumented in the release notes. Verify whether filtering uses project_id, per_page, page, and whether media/project identifiers are numeric IDs or hashed IDs per method. Guessing here produces 404s that surface as null and look like outages.
  • Pagination is on you. Listing medias almost certainly returns one page. Loop until a short page if you need completeness; don't assume the first call is the full set.
  • Null-safe ≠ type-safe. Returns may be arrays or objects depending on the endpoint. Normalize with (array) casts or a small DTO before handing data to views.
  • Extension point to watch: the config file and the client resolution are the seams. If you need retries, logging, or metrics, wrap the client rather than editing the package — a decorator bound in a service provider survives upgrades.
  • Missing from 1.0.0 (plan around it): uploads, webhooks, and tagging. If your workflow needs them, you'll be extending the package or calling the API directly; keep that code isolated so a future release can replace it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names