taglib-wasm
Version:
TagLib for TypeScript platforms: Deno, Node.js, Bun, Electron, browsers, and Cloudflare Workers
262 lines (261 loc) • 6.65 kB
JavaScript
import { PictureType } from "./types.js";
import {
FileOperationError,
InvalidFormatError,
MetadataError
} from "./errors.js";
import { writeFileData } from "./utils/write.js";
let cachedTagLib = null;
async function getTagLib() {
if (!cachedTagLib) {
const { TagLib } = await import("./taglib.js");
cachedTagLib = await TagLib.initialize();
}
return cachedTagLib;
}
async function readTags(file) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
throw new InvalidFormatError(
"File may be corrupted or in an unsupported format"
);
}
return audioFile.tag();
} finally {
audioFile.dispose();
}
}
async function applyTags(file, tags, options) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
throw new InvalidFormatError(
"File may be corrupted or in an unsupported format"
);
}
const tag = audioFile.tag();
if (tags.title !== void 0) tag.setTitle(tags.title);
if (tags.artist !== void 0) tag.setArtist(tags.artist);
if (tags.album !== void 0) tag.setAlbum(tags.album);
if (tags.comment !== void 0) tag.setComment(tags.comment);
if (tags.genre !== void 0) tag.setGenre(tags.genre);
if (tags.year !== void 0) tag.setYear(tags.year);
if (tags.track !== void 0) tag.setTrack(tags.track);
if (!audioFile.save()) {
throw new FileOperationError(
"save",
"Failed to save metadata changes. The file may be read-only or corrupted."
);
}
return audioFile.getFileBuffer();
} finally {
audioFile.dispose();
}
}
async function updateTags(file, tags, options) {
if (typeof file !== "string") {
throw new Error("updateTags requires a file path string to save changes");
}
const modifiedBuffer = await applyTags(file, tags, options);
await writeFileData(file, modifiedBuffer);
}
async function readProperties(file) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
throw new InvalidFormatError(
"File may be corrupted or in an unsupported format"
);
}
const props = audioFile.audioProperties();
if (!props) {
throw new MetadataError(
"read",
"File may not contain valid audio data",
"audioProperties"
);
}
return props;
} finally {
audioFile.dispose();
}
}
const Title = "title";
const Artist = "artist";
const Album = "album";
const Comment = "comment";
const Genre = "genre";
const Year = "year";
const Track = "track";
const AlbumArtist = "albumartist";
const Composer = "composer";
const DiscNumber = "discnumber";
async function isValidAudioFile(file) {
try {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
const valid = audioFile.isValid();
audioFile.dispose();
return valid;
} catch {
return false;
}
}
async function getFormat(file) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
return void 0;
}
return audioFile.getFormat();
} finally {
audioFile.dispose();
}
}
async function clearTags(file) {
return applyTags(file, {
title: "",
artist: "",
album: "",
comment: "",
genre: "",
year: 0,
track: 0
});
}
async function readPictures(file) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
throw new InvalidFormatError(
"File may be corrupted or in an unsupported format"
);
}
return audioFile.getPictures();
} finally {
audioFile.dispose();
}
}
async function applyPictures(file, pictures) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
throw new InvalidFormatError(
"File may be corrupted or in an unsupported format"
);
}
audioFile.setPictures(pictures);
if (!audioFile.save()) {
throw new FileOperationError(
"save",
"Failed to save picture changes. The file may be read-only or corrupted."
);
}
return audioFile.getFileBuffer();
} finally {
audioFile.dispose();
}
}
async function addPicture(file, picture) {
const taglib = await getTagLib();
const audioFile = await taglib.open(file);
try {
if (!audioFile.isValid()) {
throw new InvalidFormatError(
"File may be corrupted or in an unsupported format"
);
}
audioFile.addPicture(picture);
if (!audioFile.save()) {
throw new FileOperationError(
"save",
"Failed to save picture changes. The file may be read-only or corrupted."
);
}
return audioFile.getFileBuffer();
} finally {
audioFile.dispose();
}
}
async function clearPictures(file) {
return applyPictures(file, []);
}
async function getCoverArt(file) {
const pictures = await readPictures(file);
if (pictures.length === 0) {
return null;
}
const frontCover = pictures.find(
(pic) => pic.type === PictureType.FrontCover
);
if (frontCover) {
return frontCover.data;
}
return pictures[0].data;
}
async function setCoverArt(file, imageData, mimeType) {
const picture = {
mimeType,
data: imageData,
type: PictureType.FrontCover,
description: "Front Cover"
};
return applyPictures(file, [picture]);
}
function findPictureByType(pictures, type) {
return pictures.find((pic) => pic.type === type) || null;
}
async function replacePictureByType(file, newPicture) {
const pictures = await readPictures(file);
const filteredPictures = pictures.filter(
(pic) => pic.type !== newPicture.type
);
filteredPictures.push(newPicture);
return applyPictures(file, filteredPictures);
}
async function getPictureMetadata(file) {
const pictures = await readPictures(file);
return pictures.map((pic) => ({
type: pic.type,
mimeType: pic.mimeType,
description: pic.description,
size: pic.data.length
}));
}
import { PictureType as PictureType2 } from "./types.js";
export {
Album,
AlbumArtist,
Artist,
Comment,
Composer,
DiscNumber,
Genre,
PictureType2 as PictureType,
Title,
Track,
Year,
addPicture,
applyPictures,
applyTags,
clearPictures,
clearTags,
findPictureByType,
getCoverArt,
getFormat,
getPictureMetadata,
isValidAudioFile,
readPictures,
readProperties,
readTags,
replacePictureByType,
setCoverArt,
updateTags
};