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

Wistia Bundle Laravel Package

markup/wistia-bundle

Symfony bundle for integrating Wistia video hosting into your application, offering Twig helpers and services to embed Wistia videos and configure player options with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: markup/wistia-bundle is a Symfony 2 bundle, not a Laravel package. Don't composer require it into a Laravel app — expect dependency conflicts on Symfony 2 components, PSR-4/PSR-11 drift, and PHP version pins from the Symfony 2.x era (EOL since 2018). Install it only in a throwaway Symfony 2 sandbox if you want to poke at it, or just read the source on GitHub.

Read the source in this order (30–45 minutes, and the highest-value thing you'll get from this package):

  1. DependencyInjection/Configuration.php — the real config surface. You'll find api_key and probably a base URL/version node. This tells you the auth model: Wistia's Data API uses HTTP Basic with the API token as the username and an empty password, or api_password for uploads.
  2. Resources/config/services.xml — the service ID to inject (something like markup_wistia.client). Note how the API key is injected: constructor arg or setter.
  3. The client class (WistiaClient or similar) — this is the entire useful payload. Expect thin GET/POST wrappers: getProjects(), getProjectMedias($projectId), getMedia($hashedId), maybe upload(). Copy the endpoint paths and response-shape assumptions into your own code.
  4. MarkupWistiaExtension.php — confirms the wiring. Usually nothing surprising.

Your first use case — port the smallest possible surface to Laravel and prove it end-to-end:

composer require guzzlehttp/guzzle   # or use the built-in Http facade
// app/Services/Wistia/WistiaClient.php
namespace App\Services\Wistia;

use Illuminate\Support\Facades\Http;

class WistiaClient
{
    public function __construct(
        private string $token,
        private string $baseUrl = 'https://api.wistia.com/v1',
    ) {}

    public function medias(int $projectId, int $page = 1, int $perPage = 100): array
    {
        return Http::withBasicAuth($this->token, '')
            ->baseUrl($this->baseUrl)
            ->retry(3, 200, throw: false)
            ->get("/projects/{$projectId}/medias.json", [
                'page' => $page,
                'per_page' => $perPage,
            ])
            ->throw()
            ->json();
    }
}

Bind it in a service provider, then embed:

<iframe src="https://fast.wistia.net/embed/iframe/{{ $video->wistia_hashed_id }}"
        allowfullscreen></iframe>

If that one call returns a media list and the iframe plays, you've extracted ~90% of the bundle's value in an afternoon.

Implementation Patterns

Wrap the client, don't leak it. Even if you did adopt the bundle, callers should never depend on its method names. Define a contract and a Laravel implementation so a provider swap or a bundle port touches one class:

interface VideoProvider
{
    public function listForProject(int $projectId): array;
    public function find(string $hashedId): ?Media;
    public function embedUrl(string $hashedId, array $options = []): string;
}

Store the hashed ID on your own table. This is the single most important schema decision. videos.wistia_hashed_id (nullable, indexed) alongside your own content record keeps Wistia from being the system of record — a future move to Mux/Vimeo/S3 becomes a backfill plus a player swap rather than a CMS rewrite.

Sync to a local read model. Run a queued job or php artisan wistia:sync on a schedule that pages through projects → medias and upserts hashed_id, name, duration, thumbnail_url, project_id. Page rendering then reads from your DB and only hits the API on a cache miss. Note: Wistia's API is metered-ish and slow; this decouples your page latency from theirs.

Cache metadata, not embed URLs. getMedia($hashedId) results change rarely — cache with a TTL keyed by hashed ID (Cache::remember("wistia:media:{$id}", 3600, ...)). Embed URLs are pure string interpolation and don't need caching at all.

Two-step uploads, streaming only. Wistia uploads go to a different host (https://upload.wistia.com) with api_password, file, project_id, and name — not the Data API host. Don't buffer video in PHP memory. Stream from a temporary path or push the upload to a queued job with a generous timeout:

Http::withBasicAuth($this->token, '')
    ->timeout(600)
    ->attach('file', fopen($path, 'r'), basename($path))
    ->post('https://upload.wistia.com', [
        'project_id' => $projectId,
        'name' => $title,
    ]);

Config per environment. .env only — never commit the token. Use separate Wistia projects (or subaccounts) per environment so staging traffic doesn't pollute production engagement stats.

Blade component over raw iframes. Wistia supports query params (?videoFoam=true, ?wmode=opaque, ?seo=true). Centralize them in a <x-wistia-embed :hashed-id="$id" :options="[...]" /> component so nobody is hand-writing iframe markup and drifting.

Retry on 429/5xx, not on 4xx. Wistia rate-limits. Retry with exponential backoff on 429 and 5xx; fail fast on 401 (bad token) and 404 (bad hashed ID). Don't retry uploads blindly unless you can confirm idempotency — a duplicate POST can create duplicate media.

Gotchas and Tips

Assume incompatibility until proven otherwise. Symfony 2 bundles do not map onto Laravel's container, config loader, or package discovery. Even a successful Composer install leaves you with a service container you can't idiomatically resolve. Treat the repo as a spec, not a dependency — MIT license permits lifting the endpoint logic cleanly.

Hashed ID ≠ media ID. The Wistia Data API returns both a numeric id and a string hashed_id. Embeds and public URLs use the hashed ID. Mixing them up produces 404 iframes that look like a player issue. Store the hashed ID; keep the numeric ID only if you need management calls.

Uploads live on a different host and auth. upload.wistia.com is not api.wistia.com. Auth uses api_password (the same token, different convention), and the request is multipart. A wrapper that only covers the Data API will look complete and then silently fail the first time someone tries to upload.

Per-viewer analytics are not in the Data API. Wistia gives you media-level engagement stats. Attribution to a known user comes from how you embed and track (a ?wvideo= param or your own event layer), not from the API. If a roadmap item says "video analytics," scope the tracking work, not the integration.

Pagination is easy to get wrong. Check whether the bundle paginates or just returns page 1. Most thin wrappers don't. Your own client should loop until a short page or a null/empty response, with a hard page cap to avoid infinite loops on a malformed API response.

Webhooks and chunked/resumable uploads are probably absent. Verify before designing around them. If you need "video ready" notifications, plan a polling fallback (check status on the media record) or call the webhook registration endpoint yourself.

Error handling is likely silent. Symfony 2-era wrappers often return decoded arrays and swallow transport errors. Whatever you port, normalize failures into exceptions with request context (endpoint, status, hashed ID) and log them — you'll otherwise debug "why is the thumbnail blank" for an hour.

Rate limits dominate scale, not the wrapper. Queue media operations (sync, upload, metadata refresh) rather than calling in the request path. Cache the project list — it changes approximately never and is the most common call in an admin UI.

No maintainer, no security response, zero dependents. If your org has a dependency policy, this won't pass. That's fine — the integration is 1–3 dev days for the common read surface. Own the code, own the failures.

Extension points if you do fork: the bundle's value is the config node and service definition, both of which you'll discard. Port the client class and its response-shape assumptions with tests against recorded fixtures; leave the DI wiring, the services.xml, and the Symfony kernel registration behind.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package