npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vsmeta-parser

v1.0.1

Published

Parse Synology DS Video .vsmeta binary metadata files

Downloads

216

Readme

vsmeta-parser

Parse Synology DS Video .vsmeta binary metadata files into structured JavaScript objects.

What is a .vsmeta file?

Synology DS Video (also called Video Station) is the media server application bundled with Synology NAS devices. When DS Video fetches metadata for a video file — title, plot, cast, ratings, posters, and backdrops — it stores that metadata in a sidecar file with the same name as the video but with a .vsmeta extension appended.

For example:

Inception.2010.1080p.mkv
Inception.2010.1080p.mkv.vsmeta   ← sidecar metadata file

The .vsmeta format is a binary encoding that closely follows Protocol Buffers tag-length-value (TLV) conventions, but without a .proto schema file. This library implements a standalone parser for that format, derived from analysis of real .vsmeta files.

Both movies and TV show episodes are supported. TV episode files carry the show-level title alongside per-episode details (season, episode number, air date, episode plot, and thumbnail) in a nested message.

Installation

npm install vsmeta-parser
# or
pnpm add vsmeta-parser
# or
bun add vsmeta-parser

Quick Start

import { parseVsMeta } from 'vsmeta-parser';
import { readFileSync } from 'fs';

const buf = readFileSync('Inception.2010.1080p.mkv.vsmeta');
const meta = parseVsMeta(buf);

console.log(meta.title);        // "Inception"
console.log(meta.year);         // 2010
console.log(meta.rating);       // 8.7
console.log(meta.actors);       // ["Leonardo DiCaprio", "Joseph Gordon-Levitt", ...]
console.log(meta.imdbId);       // "tt1375666"
console.log(meta.posterImage);  // Buffer containing JPEG data

TV Show Episode

const meta = parseVsMeta(readFileSync('Breaking.Bad.S05E14.mkv.vsmeta'));

console.log(meta.contentType);  // 2 (TV show)
console.log(meta.title);        // "Breaking Bad"  (show title)
console.log(meta.season);       // 5
console.log(meta.episode);      // 14
console.log(meta.airDate);      // "2013-09-29"
console.log(meta.episodePlot);  // episode-specific synopsis
console.log(meta.posterImage);  // episode thumbnail JPEG

Skipping images for faster scanning

If you only need text metadata and want to avoid the cost of decoding embedded images, pass skipImages: true:

const meta = parseVsMeta(buf, { skipImages: true });
// meta.posterImage and meta.backdropImage will be undefined

API

parseVsMeta(buf, options?)

Parses a .vsmeta binary buffer and returns a VsMetaData object.

| Parameter | Type | Description | |-----------|------|-------------| | buf | Buffer | Raw contents of the .vsmeta file | | options.skipImages | boolean | When true, skip decoding JPEG images. Default: false |

Throws an Error if the buffer is too small to be a valid .vsmeta file.

VsMetaData

All fields are always present on the returned object; optional fields (?) are only populated when relevant to the content type.

| Field | Type | Description | |-------|------|-------------| | contentType | 1 \| 2 | 1 = movie, 2 = TV show episode | | title | string | Movie title or TV show title | | originalTitle | string | Original language title (often same as title) | | episodeTitle | string | Episode title (TV) or tagline (movies) | | year | number | Release year; for TV shows this comes from the episode air year | | releaseDate | string | Release date "YYYY-MM-DD"; for TV shows this is the episode air date | | locked | boolean | Whether DS Video has locked/frozen this metadata entry | | plot | string | Plot synopsis; for TV shows this is the episode plot | | tmdbId | string | The Movie Database (TMDb) ID | | imdbId | string | IMDb ID, e.g. "tt1375666" | | contentRating | string | Audience content rating, e.g. "R", "PG-13" | | rating | number | Audience rating on a 0–10 scale | | actors | string[] | Actor names | | directors | string[] | Director names | | genres | string[] | Genre names | | writers | string[] | Writer/screenwriter names | | posterImage | Buffer \| undefined | Decoded JPEG poster (movies) or episode thumbnail (TV shows) | | backdropImage | Buffer \| undefined | Decoded JPEG backdrop/fanart | | backdropMd5 | string \| undefined | MD5 hex hash of the backdrop image | | backdropTimestamp | number \| undefined | Unix timestamp (seconds) embedded in the backdrop message | | season | number \| undefined | Season number (TV shows only) | | episode | number \| undefined | Episode number (TV shows only) | | airDate | string \| undefined | Episode air date "YYYY-MM-DD" (TV shows only) | | episodePlot | string \| undefined | Episode-specific synopsis (TV shows only) | | showLocked | boolean \| undefined | Show-level locked flag (TV shows only) |

BackdropData

Exported for advanced use cases where you need the raw backdrop message contents before they are merged into VsMetaData.

| Field | Type | Description | |-------|------|-------------| | image | Buffer \| undefined | Decoded JPEG image | | md5 | string \| undefined | MD5 hex hash | | timestamp | number \| undefined | Unix timestamp in seconds |

Integration tests with real files

The test suite includes integration tests that run against actual .vsmeta files. These tests are automatically skipped when the files are absent, so the unit tests always pass without any fixtures.

The tests expect the following files in an examples/ directory at the package root:

vsmeta-parser/
└── examples/
    ├── Escape.Plan.2.Hades.2018.1080p.BluRay.x264-[YTS.AM].mp4.vsmeta        (movie)
    ├── Fantastic.Beasts.and.Where.to.Find.Them.2016 (high).mp4.vsmeta         (movie)
    └── Alien.Earth.2024.S01E03.Metamorphosis.1080p.HEVC.x265-MeGusta[EZTVx.to].mkv.vsmeta  (TV show)

If you have different .vsmeta files, update the filenames and expected values in test/vsmeta.test.ts under the parseVsMeta (integration — real .vsmeta files) describe block to match your files.

.vsmeta binary format notes

The format uses Protocol Buffer-style tag-length-value (TLV) encoding:

tag = (field_number << 3) | wire_type

Wire types used: 0 = varint, 1 = 64-bit, 2 = length-delimited, 5 = 32-bit.

Key outer fields: 1 (content type), 2 (title), 5 (year), 9 (TMDb JSON), 10 (cast/crew nested), 12 (rating ×10), 17 (poster base64 JPEG), 19 (TV episode nested), 21 (backdrop nested).

The rating field uses MAX_UINT64 (18446744073709551615n) as a sentinel for "unrated". The audience rating in field 12 is stored as an integer multiplied by 10 (e.g., 878.7).

Poster and backdrop images are stored as base64-encoded JPEG strings inside the binary message. The parser decodes them to raw Buffer values and validates the JPEG magic bytes (0xFF 0xD8).

License

MIT