taglib-wasm
Version:
TagLib for TypeScript platforms: Deno, Node.js, Bun, Electron, browsers, and Cloudflare Workers
222 lines (221 loc) • 7.26 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);
}
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];
let filename;
if (options.nameFormat) {
filename = options.nameFormat(picture, i);
} else {
const typeNames = {
[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"
};
const typeName = typeNames[picture.type] || "other";
const ext = picture.mimeType.split("/")[1] || "jpg";
filename = `${typeName}-${i + 1}.${ext}`;
}
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 findCoverArtFiles(audioPath) {
const dir = audioPath.substring(0, audioPath.lastIndexOf("/") + 1);
const commonNames = [
"cover",
"front",
"folder",
"album",
"artwork",
"Cover",
"Front",
"Folder",
"Album",
"Artwork"
];
const extensions = ["jpg", "jpeg", "png", "gif", "webp"];
const found = {};
for (const name of commonNames) {
for (const ext of extensions) {
const path = `${dir}${name}.${ext}`;
try {
const data = await readFileData(path);
if (data.length > 0) {
if (!found.front && ["cover", "front", "Cover", "Front"].includes(name)) {
found.front = path;
} else if (!found.folder && ["folder", "Folder"].includes(name)) {
found.folder = path;
} else {
found[name.toLowerCase()] = path;
}
break;
}
} catch {
}
}
}
const backNames = ["back", "Back", "back-cover", "Back-Cover"];
for (const name of backNames) {
for (const ext of extensions) {
const path = `${dir}${name}.${ext}`;
try {
const data = await readFileData(path);
if (data.length > 0) {
found.back = path;
break;
}
} catch {
}
}
if (found.back) break;
}
return found;
}
export {
copyCoverArt,
exportAllPictures,
exportCoverArt,
exportPictureByType,
findCoverArtFiles,
importCoverArt,
importPictureWithType,
loadPictureFromFile,
savePictureToFile
};