Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Change log

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.2] - 2026-02-09

### Changed

- Based on https://github.com/benrr101/node-taglib-sharp/releases/tag/v6.0.1

### Fixed

- ID3v2.3 genres are trimmed: https://github.com/benrr101/node-taglib-sharp/issues/120
- node-taglib-sharp incorrectly parses genres if it starts with numbers: https://github.com/benrr101/node-taglib-sharp/issues/122

## [1.0.1] - 2026-02-09

### Changed

- Based on https://github.com/benrr101/node-taglib-sharp/releases/tag/v6.0.1
- Executed build before publish to NPM.

## [1.0.0] - 2026-02-09

### Changed

- Based on https://github.com/benrr101/node-taglib-sharp/releases/tag/v6.0.1
- First attempt at NPM package.
71 changes: 38 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# TagLib# for Node

| Master | Develop | Latest |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [![Build Status](https://ci.appveyor.com/api/projects/status/7hdfrbc4ecvvruwv/branch/master?svg=true)](https://ci.appveyor.com/project/benrr101/node-taglib-sharp/branch/master) | [![Build Status](https://ci.appveyor.com/api/projects/status/7hdfrbc4ecvvruwv/branch/develop?svg=true)](https://ci.appveyor.com/project/benrr101/node-taglib-sharp/branch/develop) | [![Build Status](https://ci.appveyor.com/api/projects/status/7hdfrbc4ecvvruwv?svg=true)](https://ci.appveyor.com/project/benrr101/node-taglib-sharp) |
| [![Coverage Status](https://coveralls.io/repos/github/benrr101/node-taglib-sharp/badge.svg?branch=master)](https://coveralls.io/github/benrr101/node-taglib-sharp?branch=master) | [![Coverage Status](https://coveralls.io/repos/github/benrr101/node-taglib-sharp/badge.svg?branch=develop)](https://coveralls.io/github/benrr101/node-taglib-sharp?branch=develop) | [![Coverage Status](https://coveralls.io/repos/github/benrr101/node-taglib-sharp/badge.svg?latest)](https://coveralls.io/github/benrr101/node-taglib-sharp) |
## Maintenance Notice

This is a fork of node-taglib-sharp containing extra bug fixes. All credit goes to the original author. The original project can be found here: https://github.com/benrr101/node-taglib-sharp

## Description

TagLib# is a .NET library that has been around for years. It provides a unified interface for
accessing metadata from a vast selection of media files. Until now there hasn't been a port of this
library for Node.js. This project is a mostly wholesale translation of the original TagLib#.
Expand All @@ -15,52 +15,56 @@ is substantially lacking in the variety of media formats that can be handled. Ta
improved on the original TagLib, hence why this project exists.

## Supported Tagging Formats
* [APE](http://wiki.hydrogenaud.io/index.php?title=APE_key): `AAC`, `APE`, `FLAC`, `M2A`, `MP1`, `MP2`, `MP3`
* [Apple QuickTime/iTunes](https://developer.apple.com/documentation/quicktime-file-format/metadata_atoms_and_types) MPEG4: `M4A`, `M4B`, `M4P`, `M4V`, `MP4`
* [ASF](https://docs.microsoft.com/en-us/windows/win32/wmformat/overview-of-the-asf-format): `ASF`, `WMA`, `WMV`
* DIVX: `AVI`, `DIVX`, `WAV`
* [ID3v1](https://id3.org/ID3v1): `AAC`, `FLAC`, `M2A`, `MP1`, `MP2`, `MP3`
* [ID3v2](https://id3.org/Developer%20Information): `AAC`, `AIF`, `AIFF`, `AVI`, `DIVX`, `FLAC`, `M2A`, `MP1`, `MP2`, `MP3`, `WAV`
* [Matroska/WebM](https://www.matroska.org/technical/tagging.html): `MK3D`, `MKA`, `MKS`, `MKV`, `WEBM` _read-only_
* MovieID: `AVI`, `DIVX`, `WAV`
* [RIFF Info](https://www.exiftool.org/TagNames/RIFF.html#Info): `AVI`, `DIVX`, `WAV`
* [Xiph Comment](https://www.xiph.org/vorbis/doc/v-comment.html): `FLAC`, `OGA`, `OGG`, `OGV`, `OPUS`
* ... More coming soon

- [APE](http://wiki.hydrogenaud.io/index.php?title=APE_key): `AAC`, `APE`, `FLAC`, `M2A`, `MP1`, `MP2`, `MP3`
- [Apple QuickTime/iTunes](https://developer.apple.com/documentation/quicktime-file-format/metadata_atoms_and_types) MPEG4: `M4A`, `M4B`, `M4P`, `M4V`, `MP4`
- [ASF](https://docs.microsoft.com/en-us/windows/win32/wmformat/overview-of-the-asf-format): `ASF`, `WMA`, `WMV`
- DIVX: `AVI`, `DIVX`, `WAV`
- [ID3v1](https://id3.org/ID3v1): `AAC`, `FLAC`, `M2A`, `MP1`, `MP2`, `MP3`
- [ID3v2](https://id3.org/Developer%20Information): `AAC`, `AIF`, `AIFF`, `AVI`, `DIVX`, `FLAC`, `M2A`, `MP1`, `MP2`, `MP3`, `WAV`
- [Matroska/WebM](https://www.matroska.org/technical/tagging.html): `MK3D`, `MKA`, `MKS`, `MKV`, `WEBM` _read-only_
- MovieID: `AVI`, `DIVX`, `WAV`
- [RIFF Info](https://www.exiftool.org/TagNames/RIFF.html#Info): `AVI`, `DIVX`, `WAV`
- [Xiph Comment](https://www.xiph.org/vorbis/doc/v-comment.html): `FLAC`, `OGA`, `OGG`, `OGV`, `OPUS`
- ... More coming soon

## Supported File Formats
* Advanced Audio Codec (AAC): `AAC`
* Advanced Systems Format (ASF): `ASF`, `WMA`, `WMV`
* Audio Interchange Format (AIFF): `AIF`, `AIFF`
* Free Lossless Audio Codec (FLAC): `FLAC`
* Matroska: `MK3D`, `MKA`, `MKS`, `MKV`
* MPEG-1/2 Audio: `M2A`, `MP1`, `MP2`, `MP3`
* MPEG-1/2 Video: `M2V`, `MPE`, `MPEG`, `MPG`, `MPV2`
* MPEG4: `M4A`, `M4B`, `M4P`, `M4V`, `MP4`
* Monkey's Audio: `APE`
* Ogg: `OGA`, `OGG`, `OGV`, `OPUS`
* Resource Interchange File Format (RIFF): `AVI`, `DIVX`, `WAV`
* WebM: `WEBM`
* ... More coming soon

- Advanced Audio Codec (AAC): `AAC`
- Advanced Systems Format (ASF): `ASF`, `WMA`, `WMV`
- Audio Interchange Format (AIFF): `AIF`, `AIFF`
- Free Lossless Audio Codec (FLAC): `FLAC`
- Matroska: `MK3D`, `MKA`, `MKS`, `MKV`
- MPEG-1/2 Audio: `M2A`, `MP1`, `MP2`, `MP3`
- MPEG-1/2 Video: `M2V`, `MPE`, `MPEG`, `MPG`, `MPV2`
- MPEG4: `M4A`, `M4B`, `M4P`, `M4V`, `MP4`
- Monkey's Audio: `APE`
- Ogg: `OGA`, `OGG`, `OGV`, `OPUS`
- Resource Interchange File Format (RIFF): `AVI`, `DIVX`, `WAV`
- WebM: `WEBM`
- ... More coming soon

## Installation

```
npm install --save node-taglib-sharp
```

## Getting Started

Getting started with node-taglib-sharp is surprisingly easy. The main entry point into the library
is via the `File` class.

```typescript
import {File} from "node-taglib-sharp";
import { File } from "node-taglib-sharp";

const myFile = File.createFromPath("path/to/my/file.mp3");
```

The `File` class provides factory methods for generating instances of classes that inherit from
`File` to provide implementation specific to a file format (such as `ApeFile` providing support
for Monkey's Audio files). The `File` class has exposes the `properties` and `tag` properties to
allow manipulation of the tagging information and reading audio/video properties.
allow manipulation of the tagging information and reading audio/video properties.

See the docs for [the File class](docs/classes/File.md) for complete details of the
available properties.
Expand All @@ -75,7 +79,7 @@ object. Set tag properties as needed and they will be stored in a tagging format
by the file type. The changes can be easily written back to the file with `save()`.

See the docs for [the Tag class](docs/classes/Tag.md) for complete details of the fields
supported by the format-agnostic `Tag` class.
supported by the format-agnostic `Tag` class.

```typescript
myFile.tag.title = "Time Won't Let Me Go";
Expand All @@ -86,9 +90,10 @@ myFile.dispose();
```

## Known Issues
* Maximum supported file size is 8192TB

- Maximum supported file size is 8192TB
- Why is this an issue? 8192TB is yuuuuge, but .NET implementation supports 8192PB file sizes.
- The Node.js 12 [fs](https://nodejs.org/docs/latest-v12.x/api/fs.html) library only supports
- The Node.js 12 [fs](https://nodejs.org/docs/latest-v12.x/api/fs.html) library only supports
`integer` types for position arguments, which safely goes up to `2^52 - 1`. Node 15 supports
`number` or `biginteger` for position arguments which would increase supported sizes to 64-bit
integers. Please create issue if this is a blocker.
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
{
"name": "node-taglib-sharp",
"description": "Read and write audio/video/picture tags using a similar interface to TagLib#",
"version": "6.0.1",
"name": "@digimezzo/node-taglib-sharp",
"description": "Fork of node-taglib-sharp with extra bug fixes",
"version": "1.0.2",
"license": "LGPL-2.1-or-later",
"author": "Ben Russell <benrr101@outlook.com> (https://github.com/benrr101)",
"repository": "github:benrr101/node-taglib-sharp",
"bugs": "https://github.com/benrr101/node-taglib-sharp/issues",
"maintainers": [
"Digimezzo <digimezzo@outlook.com> (https://github.com/digimezzo)"
],
"repository": "github:digimezzo/node-taglib-sharp",
"bugs": "https://github.com/digimezzo/node-taglib-sharp/issues",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
Expand Down
70 changes: 10 additions & 60 deletions src/id3v2/frames/textInformationFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,74 +410,24 @@ export class TextInformationFrame extends Frame {
// Some frames are designed to be split into multiple parts by a /
fieldList.push(... value.split("/"));
} else if (this.frameId === FrameIdentifiers.TCON) {
// @TODO: Can we just make a separate class for TCON?
// TCON in ID3v2.2 and ID3v2.3 is specified as
// * (xx) - where xx is a number from the ID3v1 genre list
// * (xx)yy - where xx is a number from the ID3v1 genre list and yyy is a
// "refinement" of the genre
// * (RX) - "Remix"
// * (CR) - "Cover"
// * (( - used to escape a "(" in a refinement/genre name

// NOTE: This encoding has an inherent flaw around how multiple genres should be
// encoded. Since multiple genres are already an edge case, I'm just going to
// say yolo to this whole block of code copied over from the .NET implementation
while (value.length > 1 && value[0] === "(") {
const closing = value.indexOf(")");
if (closing < 0) {
break;
}
while (value.length > 1 && value[0] === '(') {
const closing = value.indexOf(')');
if (closing < 0) break;

const number = value.substring(1, closing);
fieldList.push(number);

let text: string;
if (number === TextInformationFrame.COVER_ABBREV) {
text = TextInformationFrame.COVER_STRING;
} else if (number === TextInformationFrame.REMIX_ABBREV) {
text = TextInformationFrame.REMIX_STRING;
} else {
text = Genres.indexToAudio(number, true);
}
value = StringUtils.trimStart(value.substring(closing + 1), ' ').replace(/^\/+/, '');

if (!text) {
// Number in parentheses was not a numeric genre but part of a larger bit
// of text?
break;
}

// Number in parentheses was a numeric genre
fieldList.push(text);
value = StringUtils.trimStart(value.substring(closing + 1), "/ ");

// Ignore genre if the same genre appears after the numeric genre
if (value.startsWith(text)) {
value = StringUtils.trimStart(value.substring(text.length), "/ ");
const text = Genres.indexToAudio(number, true);
if (text && value.startsWith(text)) {
value = StringUtils.trimStart(value.substring(text.length),' ').replace(/^\/+/, '');
}
}

// Process whatever's left
if (value.length > 0) {
// Split the remaining genre value by dividers if the setting is turned on
let splitValue = Id3v2Settings.useNonStandardV2V3GenreSeparators
? value.split(/[\/;]/).map((v) => v.trim()).filter((v) => !!v)
: [value];

splitValue = splitValue.map((v) => {
// Unescape escaped opening parenthesis
let v2 = v.replace(/\(\(/, "(");

// If non-standard numeric genres is enabled, parse them
if (Id3v2Settings.useNonStandardV2V3NumericGenres) {
const text = Genres.indexToAudio(v2, false);
if (text) {
v2 = text;
}
}

return v2;
});

fieldList.push(...splitValue);
const remainingGenres = value.split(/[\/;]/);
fieldList.push(...remainingGenres);
}
} else {
fieldList.push(value);
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export class StringUtils {

public static trimStart(toTrim: string, chars: string): string {
while (toTrim.length > 0 && chars.indexOf(toTrim[0]) > -1) {
toTrim = toTrim.substring(0);
toTrim = toTrim.substring(1);
}
return toTrim;
}
Expand Down