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

Getid3 Laravel Package

james-heinrich/getid3

getID3 is a PHP library for reading and parsing metadata from audio and video files. It extracts ID3, APE and Lyrics3 tags and analyzes formats like MP3, FLAC, Ogg, AAC, MP4, WAV, AVI and Matroska.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer — the package is a plain library, not a Laravel package, so there is no service provider, config file, or facade to publish:

composer require james-heinrich/getid3

The class lives in the global namespace (getID3), which means you must escape it inside your own namespaced code:

// app/Services/AudioMetadata.php
namespace App\Services;

class AudioMetadata
{
    public function read(string $absolutePath): array
    {
        $getID3 = new \getID3();
        $getID3->setOption(['encoding' => 'UTF-8']);

        return $getID3->analyze($absolutePath);
    }
}

Three things to read before writing any code:

  1. vendor/james-heinrich/getid3/demos/demo.basic.php — the one-file happy path.
  2. vendor/james-heinrich/getid3/structure.txt — the canonical shape of the returned array (playtime, bitrate, audio, video, comments, tags, error, warning, …).
  3. demos/demo.browse.php — run it locally against files you actually care about. The returned structure varies a lot per format, so never code against assumptions; code against the demo output.

First use case: an upload endpoint that accepts an MP3/MP4, extracts title, artist, album, playtime, bitrate, sample_rate, and mime_type, and persists them so your UI can render a player and a duration without touching the file again.

$path = $request->file('track')->store('tracks', 'local'); // disk-relative
$info = app(AudioMetadata::class)->read(\Storage::disk('local')->path($path));

if (! empty($info['error'])) {
    \Storage::disk('local')->delete($path);
    abort(422, implode('; ', (array) $info['error']));
}

License check before you ship. getID3 ships multiple licenses (GPLv1–3, LGPLv3, MPLv2). GPL is viral; if your Laravel app is closed-source, you want MPL/LGPL — not GPL. The commercial license is no longer sold, but existing ones remain valid. There is no composer.json license metadata to lean on here (NOASSERTION), so record your choice explicitly in your project docs or NOTICE file.

A good "day two" addition is an artisan command that backfills metadata for existing rows:

// php artisan media:scan --disk=public --path=tracks
foreach (\Storage::disk($disk)->allFiles($path) as $file) { /* analyze + upsert */ }

Implementation Patterns

Bind a singleton and wrap it. new getID3 is cheap-ish but the library accumulates options and state; more importantly, you do not want \getID3 sprinkled through controllers. One service, one DTO:

// app/Providers/AppServiceProvider.php
$this->app->singleton(\getID3::class, fn () => new \getID3());
final class AudioMetadataService
{
    public function __construct(private \getID3 $getID3) {}

    public function analyze(string $path, array $options = []): MediaInfo
    {
        $this->getID3->setOption($options + ['encoding' => 'UTF-8']);
        $raw = $this->getID3->analyze($path);

        return new MediaInfo(
            durationSeconds: (float) ($raw['playtime_seconds'] ?? 0),
            bitrate:         (int) ($raw['bitrate'] ?? 0),
            format:          $raw['fileformat'] ?? null,
            tags:            $raw['tags'] ?? [],
            error:           $raw['error'] ?? null,
            warnings:        (array) ($raw['warning'] ?? []),
        );
    }
}

Persist both normalized columns and the raw payload. Store duration_seconds, bitrate, format, mime_type as indexed columns for querying/sorting, plus a metadata JSON column holding the full getID3 array. When a format quirk shows up six months later, you will not need to re-scan the filesystem.

Push analysis into a queued job. Analysis parses the real file off disk and can take seconds on large video; never do it inline in the upload request for anything beyond small audio.

// after successful store
AnalyzeMediaFile::dispatch($media->id)->onQueue('media');

In the job, re-resolve the path from the disk and guard against the file having been deleted (is_readable() first, then analyze; release with backoff if the file is not yet visible on a network/S3-FUSE mount).

Never pass a PHP stream from Storage::readStream() directly. getID3 wants a local filesystem path. For S3/remote disks, copy to a temp file first — the README's HTTP example is exactly this pattern:

$tmp = tempnam(sys_get_temp_dir(), 'gid3');
file_put_contents($tmp, \Storage::disk('s3')->get($key));
try { $info = $service->analyze($tmp); } finally { @unlink($tmp); }

Extract embedded cover art while you have the file open — it is the reason many people reach for this library in the first place:

$picture = $raw['comments']['picture'][0] ?? null;
if ($picture) {
    $bytes = $picture['data'];
    $mime  = $picture['image_mime'] ?? 'image/jpeg';
    \Storage::disk('public')->put("covers/{$media->id}.jpg", $bytes);
}

Write tags through a dedicated method, not ad hoc. Writing is supported for ID3v1/v2.3/v2.4, VorbisComment (OggVorbis and FLAC), and APE v2 — but not for MP4, WMA, or ASF. Always feature-detect by format and validate before writing:

private const WRITABLE = ['mp3', 'ogg', 'flac', 'ape'];

$this->getID3->setOption(['encoding' => 'UTF-8']);
$result = $this->getID3->WriteTags([
    'title'  => ['New Title'],
    'artist' => ['New Artist'],
]);
if (! empty($result['error'])) { /* surface to user, do not silently swallow */ }

Validate uploads on content, not extension. After analysis, compare $info['fileformat'] / $info['mime_type'] against your allowed list. This catches renamed files that mimes:mp3 validation misses, and it lets you reject truncated/corrupt media before it reaches a transcode queue.

Cache by content hash for repeated reads. getID3 does no caching of its own — every analyze() re-reads the file. If you serve metadata on a hot endpoint, store it once (JSON column or Cache::remember), keyed by path + filesize + filemtime.

Gotchas and Tips

error vs warning is the whole error model in 1.x. analyze() does not throw; it returns an array. If $info['error'] is populated, the file was not correctly parsed and the data is unreliable. If only $info['warning'] is populated, the data is generally fine — getID3 worked around a known bug in another tool. Write your guard as if (!empty($info['error'])), never as if (!$info). Note that getID3 2.x changes this to thrown exceptions; if you pin 2.x, wrap in try/catch instead.

Memory is the most common production failure. Analysis loads chunks, not whole files, but 4MB is the documented floor and 12MB with all modules loaded. Enable options like md5_data or md5_data_source and md5_file and you are hashing the entire file — on a 500MB video that is your problem, not getID3's. Bump memory_limit inside the job (ini_set('memory_limit', '256M')) and never enable MD5 options globally.

Options are sticky. setOption() mutates the instance. If your singleton enables md5_file for one caller, the next caller inherits it. Set the full option set at the start of every analysis, or use a fresh new \getID3 per call (cheap and safer).

32-bit PHP cannot fully parse files over 2GB. ID3v1, Lyrics3, and APE tags live at end-of-file and will fail. Fine on 64-bit; check PHP_INT_SIZE if you run mixed infrastructure.

Some fields are simply missing for some formats. MIDI playtime is sometimes inaccurate. Raw AAC and raw WavPack cannot be identified. MPEG video with VBR data has no bitrate. MP4s generate a pile of "Unknown QuickTime atom type" warnings that are noise. Always ?? null your reads and decide on a fallback (e.g., derive duration from the player side).

Class name collision risk. The getID3 class is global. If another dependency bundles a copy, Composer's classmap will pick one and you may get a version mismatch at runtime. Check composer why james-heinrich/getid3 and avoid loading a second copy from a legacy include-based codebase.

Writing tags is the risky part. The README itself says the author is not liable for corrupted files, "especially within the writing functions." Back up before your first production write, and if you rewrite tags on upload, do it on a copy then swap atomically. Also note ID3v1 is ISO-8859-1 only — non-Latin titles silently degrade unless you write ID3v2.

analyze() needs a readable local path with correct permissions. On Laravel Cloud/ECS/Lambda with a read-only or ephemeral filesystem, Storage::path() may point at a stream wrapper. Use Storage::disk('local')->path() with a real local disk, or copy to sys_get_temp_dir() and clean up in a finally.

Debugging workflow: when a specific file returns unexpected data, do not guess — drop it into vendor/james-heinrich/getid3/demos/demo.browse.php locally and inspect the full tree, then diff against structure.txt. Keep one fixture per format you support in tests/fixtures and assert on the fields you actually consume (not the whole array — it changes between releases).

Extension points are file-based, not Laravel-based. Custom format support means adding files under getid3/module.* directories and touching getid3.php's module list — which you cannot do cleanly inside vendor/. If you need it, fork the package, register your fork with a VCS repository in composer.json, and pin the branch. Do not patch vendor/ directly.

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.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json