taglib-wasm
Version:
TagLib for TypeScript platforms: Deno, Node.js, Bun, Electron, browsers, and Cloudflare Workers
216 lines (215 loc) • 7.15 kB
JavaScript
import { PICTURE_TYPE_VALUES } from "./types.js";
import {
applyPictures,
getCoverArt,
readPictures,
replacePictureByType,
setCoverArt
} from "./simple.js";
import { readFileData } from "./utils/file.js";
import { writeFileData } from "./utils/write.js";
async function exportCoverArt(audioPath, imagePath) {
const coverData = await getCoverArt(audioPath);
if (!coverData) {
throw new Error(`No cover art found in: ${audioPath}`);
}
await writeFileData(imagePath, coverData);
}
async function exportPictureByType(audioPath, imagePath, type) {
const pictures = await readPictures(audioPath);
const typeValue = typeof type === "string" ? PICTURE_TYPE_VALUES[type] : type;
const picture = pictures.find((pic) => pic.type === typeValue);
if (!picture) {
throw new Error(`No picture of type ${type} found in: ${audioPath}`);
}
await writeFileData(imagePath, picture.data);
}
const PICTURE_TYPE_NAMES = {
[PICTURE_TYPE_VALUES.FrontCover]: "front-cover",
[PICTURE_TYPE_VALUES.BackCover]: "back-cover",
[PICTURE_TYPE_VALUES.LeafletPage]: "leaflet",
[PICTURE_TYPE_VALUES.Media]: "media",
[PICTURE_TYPE_VALUES.LeadArtist]: "lead-artist",
[PICTURE_TYPE_VALUES.Artist]: "artist",
[PICTURE_TYPE_VALUES.Conductor]: "conductor",
[PICTURE_TYPE_VALUES.Band]: "band",
[PICTURE_TYPE_VALUES.Composer]: "composer",
[PICTURE_TYPE_VALUES.Lyricist]: "lyricist",
[PICTURE_TYPE_VALUES.RecordingLocation]: "recording-location",
[PICTURE_TYPE_VALUES.DuringRecording]: "during-recording",
[PICTURE_TYPE_VALUES.DuringPerformance]: "during-performance",
[PICTURE_TYPE_VALUES.MovieScreenCapture]: "screen-capture",
[PICTURE_TYPE_VALUES.ColouredFish]: "fish",
[PICTURE_TYPE_VALUES.Illustration]: "illustration",
[PICTURE_TYPE_VALUES.BandLogo]: "band-logo",
[PICTURE_TYPE_VALUES.PublisherLogo]: "publisher-logo"
};
function generatePictureFilename(picture, index) {
const typeName = PICTURE_TYPE_NAMES[picture.type] ?? "other";
const ext = picture.mimeType.split("/")[1] ?? "jpg";
return `${typeName}-${index + 1}.${ext}`;
}
async function exportAllPictures(audioPath, outputDir, options = {}) {
const pictures = await readPictures(audioPath);
const exportedPaths = [];
const dir = outputDir.endsWith("/") ? outputDir : outputDir + "/";
for (let i = 0; i < pictures.length; i++) {
const picture = pictures[i];
const filename = options.nameFormat ? options.nameFormat(picture, i) : generatePictureFilename(picture, i);
const fullPath = dir + filename;
await writeFileData(fullPath, picture.data);
exportedPaths.push(fullPath);
}
return exportedPaths;
}
async function importCoverArt(audioPath, imagePath, options = {}) {
const imageData = await readFileData(imagePath);
let mimeType = options.mimeType;
if (!mimeType) {
const ext = imagePath.split(".").pop()?.toLowerCase();
const mimeTypes = {
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"png": "image/png",
"gif": "image/gif",
"webp": "image/webp",
"bmp": "image/bmp"
};
mimeType = mimeTypes[ext ?? ""] ?? "image/jpeg";
}
const modifiedBuffer = await setCoverArt(audioPath, imageData, mimeType);
await writeFileData(audioPath, modifiedBuffer);
}
async function importPictureWithType(audioPath, imagePath, type, options = {}) {
const imageData = await readFileData(imagePath);
let mimeType = options.mimeType;
if (!mimeType) {
const ext = imagePath.split(".").pop()?.toLowerCase();
const mimeTypes = {
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"png": "image/png",
"gif": "image/gif",
"webp": "image/webp",
"bmp": "image/bmp"
};
mimeType = mimeTypes[ext ?? ""] ?? "image/jpeg";
}
const picture = {
mimeType,
data: imageData,
type: typeof type === "string" ? PICTURE_TYPE_VALUES[type] : type,
description: options.description
};
const modifiedBuffer = await replacePictureByType(audioPath, picture);
await writeFileData(audioPath, modifiedBuffer);
}
async function loadPictureFromFile(imagePath, type = "FrontCover", options = {}) {
const data = await readFileData(imagePath);
let mimeType = options.mimeType;
if (!mimeType) {
const ext = imagePath.split(".").pop()?.toLowerCase();
const mimeTypes = {
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"png": "image/png",
"gif": "image/gif",
"webp": "image/webp",
"bmp": "image/bmp"
};
mimeType = mimeTypes[ext ?? ""] ?? "image/jpeg";
}
return {
mimeType,
data,
type: typeof type === "string" ? PICTURE_TYPE_VALUES[type] : type,
description: options.description ?? imagePath.split("/").pop()
};
}
async function savePictureToFile(picture, imagePath) {
await writeFileData(imagePath, picture.data);
}
async function copyCoverArt(sourcePath, targetPath, options = {}) {
if (options.copyAll) {
const pictures = await readPictures(sourcePath);
if (pictures.length === 0) {
throw new Error(`No pictures found in: ${sourcePath}`);
}
const modifiedBuffer = await applyPictures(targetPath, pictures);
await writeFileData(targetPath, modifiedBuffer);
} else {
const coverData = await getCoverArt(sourcePath);
if (!coverData) {
throw new Error(`No cover art found in: ${sourcePath}`);
}
const pictures = await readPictures(sourcePath);
const coverPicture = pictures.find(
(p) => p.type === PICTURE_TYPE_VALUES.FrontCover
) ?? pictures[0];
const modifiedBuffer = await setCoverArt(
targetPath,
coverData,
coverPicture.mimeType
);
await writeFileData(targetPath, modifiedBuffer);
}
}
async function fileExists(path) {
try {
const data = await readFileData(path);
return data.length > 0;
} catch {
return false;
}
}
async function findCoverFile(dir, names, extensions) {
for (const name of names) {
for (const ext of extensions) {
const path = `${dir}${name}.${ext}`;
if (await fileExists(path)) {
return path;
}
}
}
return void 0;
}
async function findCoverArtFiles(audioPath) {
const dir = audioPath.substring(0, audioPath.lastIndexOf("/") + 1);
const extensions = ["jpg", "jpeg", "png", "gif", "webp"];
const found = {};
const frontPath = await findCoverFile(
dir,
["cover", "front", "Cover", "Front"],
extensions
);
if (frontPath) found.front = frontPath;
const folderPath = await findCoverFile(
dir,
["folder", "Folder"],
extensions
);
if (folderPath) found.folder = folderPath;
const otherNames = ["album", "artwork", "Album", "Artwork"];
for (const name of otherNames) {
const path = await findCoverFile(dir, [name], extensions);
if (path) found[name.toLowerCase()] = path;
}
const backPath = await findCoverFile(
dir,
["back", "Back", "back-cover", "Back-Cover"],
extensions
);
if (backPath) found.back = backPath;
return found;
}
export {
copyCoverArt,
exportAllPictures,
exportCoverArt,
exportPictureByType,
findCoverArtFiles,
importCoverArt,
importPictureWithType,
loadPictureFromFile,
savePictureToFile
};