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

Get Id3 Laravel Package

nass600/get-id3

nass600/get-id3 is a Laravel/PHP wrapper around the getID3 library that makes it easy to extract metadata and ID3 tags from audio and video files. Use it to read duration, bitrate, codecs, artwork, and other media details in Laravel apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up

composer require nass600/get-id3

On Laravel ≤5.4 (or if package discovery fails), register manually in config/app.php:

'providers' => [
    Nass600\GetID3\GetID3ServiceProvider::class,
],
'aliases' => [
    'getID3' => Nass600\GetID3\Facades\GetID3::class,
],

Where to look first

  • Nass600\GetID3\Facades\GetID3 — the facade you'll call day-to-day.
  • Nass600\GetID3\GetID3 — the service class; it is a thin wrapper that extends/wraps the upstream getid3 library, so its public API is essentially the upstream analyze() method plus getID3's option setters.
  • The upstream getID3 source — the wrapper barely documents anything; this is your real reference for the returned array structure.

First use case: tag an uploaded MP3

use Nass600\GetID3\Facades\GetID3;

$info = GetID3::analyze(
    storage_path('app/public/tracks/song.mp3')
);

$title    = $info['tags']['id3v2']['title'][0] ?? null;
$artist   = $info['tags']['id3v2']['artist'][0] ?? null;
$duration = $info['playtime_seconds'] ?? null;
$bitrate  = $info['bitrate'] ?? null;

That is the whole point of the package: one static call returns a deep array describing format, tags, duration, bitrate, sample rate, and stream info for basically any audio/video container getID3 understands.

Implementation Patterns

Inject the service, don't sprinkle facades

The facade is convenient for one-off scripts, but in application code type-hint the underlying class so you can test around it:

use Nass600\GetID3\GetID3;

class TrackMetadataExtractor
{
    public function __construct(private GetID3 $getId3) {}

    public function extract(string $path): array
    {
        return $this->getId3->analyze($path);
    }
}

If you need to swap it in tests, wrap it in your own interface — the package ships no contract to bind against.

Normalize the output — never trust the raw array

getID3 returns very different shapes per container/codec. ID3v2 tags live under tags.id3v2.title[0], ogg tags under tags.ogg.title[0], MP4 under tags.mp4.title[0], and quicktime atoms under quicktime.*. Build a small normalizer:

final class AudioMetadata
{
    public static function fromArray(array $info): self
    {
        $tags = array_merge(
            $info['tags']['id3v2'] ?? [],
            $info['tags']['id3v1'] ?? [],
            $info['tags']['ogg']   ?? [],
            $info['tags']['mp4']   ?? [],
        );

        return new self(
            title:    $tags['title'][0]    ?? null,
            artist:   $tags['artist'][0]   ?? null,
            album:    $tags['album'][0]    ?? null,
            duration: $info['playtime_seconds'] ?? null,
            bitrate:  $info['audio']['bitrate'] ?? null,
            mimeType: $info['mime_type'] ?? null,
            isAudio:  isset($info['audio']),
            isVideo:  isset($info['video']),
        );
    }
}

Bridge Laravel Storage to a real path

getID3 needs a local file path; it will not read S3 URLs. Resolve the disk path first:

$path = Storage::disk('public')->path($relativePath);
$info = GetID3::analyze($path);

For non-local disks, either download to storage_path('app/tmp') first or call the underlying library's stream capabilities (rarely worth it).

Push analysis to a queue

analyze() is CPU- and IO-heavy on video. Pattern:

ProcessMediaMetadata::dispatch($media->id)->onQueue('media');

Then in the job, analyze, persist to a metadata JSON column, and cache the derived fields the UI needs (title/artist/duration) so you never parse the file twice.

Cache aggressively by file hash

Key a cache entry on sha1_file($path) or the storage object's etag. Metadata never changes for a given byte stream — re-parsing on every request is a classic self-inflicted DoS.

Use it for validation, not just tagging

A cheap and sturdy gate against malicious uploads:

$info = GetID3::analyze($tmpPath);

if (! isset($info['audio']) || ($info['error'] ?? [])) {
    throw new ValidationException('Not a valid audio file.');
}

if (($info['playtime_seconds'] ?? 0) > 3600) {
    throw new ValidationException('Audio exceeds one hour.');
}

This catches renamed .mp3 files and container/codec mismatches that mime_content_type() alone misses.

Tune the underlying options before big jobs

GetID3::setOption('option_max_4GB', true);   // required for >4GB video
GetID3::setOption('option_tag_id3v1', true);  // read legacy tags too

Because the service is registered as a singleton, options set once leak across the request — reset them in a finally if you toggle per-file.

Gotchas and Tips

License is GPL-2.0 — read this before you ship. This is the single most important gotcha. Both the wrapper and upstream getID3 are GPL-2.0 (not LGPL). Distributing a closed-source Laravel app that links this library is legally problematic. For commercial SaaS backends it's usually fine (no distribution), but for a shipped desktop/SaaS-on-prem product you need counsel or a permissive alternative (e.g. getID3 has no MIT fork worth using — consider ffprobe via php-ffmpeg instead). Treat this as a hard architecture constraint, not a footnote.

Package health. ~58 stars, 0 dependents, and a wrapper around a library whose canonical home is JamesHeinrich/getID3. The wrapper adds nothing you can't trivially replicate with 10 lines of container binding. The pragmatic move is often to require james-heinrich/getid3 directly and register it yourself — you get the same license concern but a maintained upstream and no stale-wrapper risk.

No exception on bad files. analyze() returns an array with an error key rather than throwing:

$info = GetID3::analyze($path);
if (! empty($info['error'])) {
    throw new RuntimeException(implode('; ', $info['error']));
}

Wrap the call in your own try/catch anyway — getID3 can still emit warnings if given a path it cannot open.

Large files will blow up memory and time. For anything over a few hundred MB, set set_time_limit(0) and ini_set('memory_limit', '512M') inside the job, and always run it on a queue worker, never a web request.

Encoding. ID3v1 and some legacy tags are Latin-1. The library generally returns UTF-8 for ID3v2 but not always; if you render titles in Blade, run them through mb_convert_encoding($title, 'UTF-8', 'ISO-8859-1') only when mb_check_encoding($title, 'UTF-8') fails. Don't blanket-convert — you'll mangle good UTF-8.

Metadata is per-format, not per-file. $info['audio']['bitrate'] may appear, $info['bitrate'] may appear, or neither. playtime_seconds is the most reliable duration key, but even that can be missing on truncated streams. Always use ?? null when extracting.

Paths must be local and readable. Storage::url() returns a URL — passing it to analyze() will fail silently with an error entry. Use Storage::path() or ->getDriver()->path(...).

Singleton option leakage. If you call setOption() in one job and don't reset it, subsequent jobs in the same worker reuse the same singleton instance with those options. Reset in a finally, or bind the service as scoped/transient in a service provider of your own.

Debugging. Dump the full $info array to a log (it's JSON-serializable) rather than trying to read it in dd() — for a well-tagged MP3 it's hundreds of keys. Store the raw array once per file so you can inspect what getID3 actually returned for a given file after the fact, without re-parsing.

Extension points are minimal by design. There's no config file, no events, no contracts. The realistic extension surface is your own wrapper class implementing a small interface (analyze(string $path): array), which also gives you the escape hatch to swap in ffprobe later if the license forces your hand.

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