Skip to content

oscnord/get-video-resolution

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Get video resolution

CI npm

Get resolution, codec, audio tracks, subtitles, bit depth, rotation, and more from any video source. Supports local files (MP4, MOV, WebM, MKV, AVI), HLS streams, DASH manifests, and binary input (Buffer/Blob).

Zero dependencies. No ffmpeg required. Browser-compatible for URL/Blob sources.

Only reads file headers (not the full file), so it works efficiently on files of any size. For remote URLs, uses HTTP Range requests to fetch just the first 1MB.

Install

npm install @oscnord/get-video-resolution

Usage

Basic usage

import { getVideoResolution } from "@oscnord/get-video-resolution";

// Local file
const info = await getVideoResolution("/path/to/video.mp4");
console.log(info.width, info.height); // 1920 1080

// HLS stream
const hls = await getVideoResolution("https://example.com/stream/master.m3u8");

// DASH manifest
const dash = await getVideoResolution("https://example.com/stream/manifest.mpd");

VideoInfo return type

Every call returns a VideoInfo object:

const info = await getVideoResolution("/path/to/video.mp4");
// {
//   width: 1920,
//   height: 1080,
//   duration: 120.5,
//   codec: "avc1.640028",
//   framerate: 29.97,
//   bitrate: undefined,       // available for HLS/DASH variants
//   aspectRatio: "16:9",
//   hdr: false,
//   rotation: 0,              // degrees (0, 90, 180, 270)
//   bitDepth: 8,              // 8, 10, or 12
//   encrypted: undefined,     // true when DRM detected (HLS/DASH)
//   audioTracks: [
//     { codec: "mp4a.40.2", language: "en", channels: 2 }
//   ],
//   subtitleTracks: undefined  // available for HLS/DASH
// }

HLS/DASH variant metadata

For streaming sources, each variant includes manifest-level metadata:

const variants = await getVideoResolution(
  "https://example.com/stream/master.m3u8",
  { pick: "all" },
);

// Each variant includes:
// - audioTracks: available audio languages and codecs
// - subtitleTracks: available subtitle languages
// - encrypted: true if DRM detected
console.log(variants[0].audioTracks);
// [{ codec: "mp4a.40.2", language: "en", channels: 2 },
//  { codec: "mp4a.40.2", language: "sv", channels: 2 }]

Get lowest resolution

const lowest = await getVideoResolution(
  "https://example.com/stream/master.m3u8",
  { pick: "lowest" },
);

URL content-type sniffing

When a URL has no recognizable extension, enable sniff to send a HEAD request and detect the content type:

const info = await getVideoResolution("https://cdn.example.com/video/12345", {
  sniff: true,
});

Custom fetch with auth headers

Pass a custom fetch function for authenticated or proxied requests:

const info = await getVideoResolution(
  "https://api.example.com/stream/master.m3u8",
  {
    fetch: (url, init) =>
      globalThis.fetch(url, {
        ...init,
        headers: { Authorization: "Bearer token" },
      }),
  },
);

Timeout and AbortSignal

// Timeout in milliseconds
const info = await getVideoResolution("https://example.com/video.mp4", {
  timeout: 5000,
});

// Or use an AbortSignal for manual cancellation
const controller = new AbortController();
const info = await getVideoResolution("https://example.com/video.mp4", {
  signal: controller.signal,
});

Buffer input

Pass a Buffer, Blob, or ReadableStream directly:

import { readFile } from "node:fs/promises";

const buffer = await readFile("/path/to/video.mp4");
const info = await getVideoResolution(buffer);

API

getVideoResolution(source, options?)

function getVideoResolution(
  source: string | Buffer | Blob | ReadableStream,
  options: GetVideoResolutionOptions & { pick: "all" },
): Promise<VideoInfo[]>;

function getVideoResolution(
  source: string | Buffer | Blob | ReadableStream,
  options?: GetVideoResolutionOptions,
): Promise<VideoInfo>;

When pick is "all", returns VideoInfo[]. Otherwise returns a single VideoInfo.

VideoInfo

interface VideoInfo {
  width: number;
  height: number;
  duration?: number;      // seconds
  codec?: string;         // e.g. "avc1.640028", "hev1.1.6.L150"
  framerate?: number;     // frames per second
  bitrate?: number;       // bits per second (HLS/DASH only)
  aspectRatio?: string;   // e.g. "16:9", "4:3"
  hdr?: boolean;          // true for HDR codecs (HLG, HDR10, Dolby Vision)
  rotation?: number;      // degrees (0, 90, 180, 270)
  bitDepth?: number;      // 8, 10, or 12
  encrypted?: boolean;    // DRM detected (HLS/DASH only)
  audioTracks?: AudioTrack[];
  subtitleTracks?: SubtitleTrack[];  // HLS/DASH only
}

interface AudioTrack {
  codec?: string;      // e.g. "mp4a.40.2", "opus", "ac-3"
  language?: string;   // e.g. "en", "sv"
  channels?: number;   // e.g. 2, 6
}

interface SubtitleTrack {
  language?: string;   // e.g. "en", "sv"
  codec?: string;      // e.g. "wvtt", "stpp"
}

GetVideoResolutionOptions

interface GetVideoResolutionOptions {
  timeout?: number;                  // milliseconds
  signal?: AbortSignal;              // manual abort
  fetch?: typeof globalThis.fetch;   // custom fetch implementation
  pick?: "highest" | "lowest" | "all"; // variant selection (default: "highest")
  sniff?: boolean;                   // HEAD-request content-type detection
}

Auto-detection

The input type is detected automatically by file extension:

Extension Parser
.m3u8 HLS manifest parser
.mpd DASH manifest parser
Everything else Built-in file parser (MP4, MOV, WebM, MKV, AVI)

When sniff: true and the URL has no recognized extension, a HEAD request detects the content type.

Error handling

All errors extend VideoResolutionError, so you can catch them with instanceof:

import {
  getVideoResolution,
  VideoResolutionError,
  NetworkError,
  ManifestParseError,
  UnsupportedSourceError,
  MediaParseError,
  type AudioTrack,
  type SubtitleTrack,
  type VideoInfo,
} from "@oscnord/get-video-resolution";

try {
  const info = await getVideoResolution(source);
} catch (error) {
  if (error instanceof NetworkError) {
    // fetch failed, timeout, etc.
  } else if (error instanceof ManifestParseError) {
    // invalid HLS/DASH manifest
  } else if (error instanceof UnsupportedSourceError) {
    // invalid source path or URL
  } else if (error instanceof MediaParseError) {
    // file parsing failed
  } else if (error instanceof VideoResolutionError) {
    // catch-all for any library error
  }
}
Error class When
NetworkError HTTP request failed, timed out, or was aborted
ManifestParseError HLS/DASH manifest could not be parsed or has no resolution
UnsupportedSourceError Source string is not a valid path or URL
MediaParseError File could not be parsed or has no video track

CommonJS

const { getVideoResolution } = require("@oscnord/get-video-resolution");

const info = await getVideoResolution("/path/to/video.mp4");

Development

Requires Bun.

bun install
bun test
bun run build

License

MIT

About

Zero-dependency video metadata for Node.js and the browser. Read resolution, codec, HDR, audio/subtitle tracks, and DRM from MP4, MOV, WebM, MKV, AVI, HLS (.m3u8), and DASH (.mpd). No ffmpeg / ffprobe required.

Topics

Resources

License

Stars

Watchers

Forks

Contributors