2020-04-05 09:31:16 +00:00
|
|
|
const ID3Writer = require("browser-id3-writer");
|
2020-11-06 17:12:04 +00:00
|
|
|
const musicMetadata = require("music-metadata-browser");
|
2020-02-11 07:51:07 +00:00
|
|
|
export const FLAC_HEADER = [0x66, 0x4C, 0x61, 0x43];
|
|
|
|
export const MP3_HEADER = [0x49, 0x44, 0x33];
|
|
|
|
export const OGG_HEADER = [0x4F, 0x67, 0x67, 0x53];
|
|
|
|
export const M4A_HEADER = [0x66, 0x74, 0x79, 0x70];
|
2020-04-23 10:15:07 +00:00
|
|
|
export const WMA_HEADER = [
|
|
|
|
0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
|
|
|
|
0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C,
|
|
|
|
]
|
2020-04-23 12:46:08 +00:00
|
|
|
export const WAV_HEADER = [0x52, 0x49, 0x46, 0x46]
|
2020-02-10 16:34:26 +00:00
|
|
|
export const AudioMimeType = {
|
|
|
|
mp3: "audio/mpeg",
|
|
|
|
flac: "audio/flac",
|
|
|
|
m4a: "audio/mp4",
|
2020-04-23 12:46:08 +00:00
|
|
|
ogg: "audio/ogg",
|
|
|
|
wma: "audio/x-ms-wma",
|
|
|
|
wav: "audio/x-wav"
|
2020-02-10 16:34:26 +00:00
|
|
|
};
|
2020-07-18 13:58:07 +00:00
|
|
|
export const IXAREA_API_ENDPOINT = "https://stats.ixarea.com/apis"
|
2020-01-21 11:03:41 +00:00
|
|
|
|
|
|
|
// Also a new draft API: blob.arrayBuffer()
|
2020-02-10 16:34:26 +00:00
|
|
|
export async function GetArrayBuffer(blobObject) {
|
2020-01-21 11:03:41 +00:00
|
|
|
return await new Promise(resolve => {
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = (e) => {
|
|
|
|
resolve(e.target.result);
|
|
|
|
};
|
|
|
|
reader.readAsArrayBuffer(blobObject);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-04-23 12:46:08 +00:00
|
|
|
export function GetFileInfo(artist, title, filenameNoExt, separator = "-") {
|
2020-01-21 11:03:41 +00:00
|
|
|
let newArtist = "", newTitle = "";
|
2020-04-23 12:46:08 +00:00
|
|
|
let filenameArray = filenameNoExt.split(separator);
|
2020-01-21 11:03:41 +00:00
|
|
|
if (filenameArray.length > 1) {
|
|
|
|
newArtist = filenameArray[0].trim();
|
|
|
|
newTitle = filenameArray[1].trim();
|
|
|
|
} else if (filenameArray.length === 1) {
|
|
|
|
newTitle = filenameArray[0].trim();
|
|
|
|
}
|
|
|
|
|
2020-02-10 16:34:26 +00:00
|
|
|
if (typeof artist == "string" && artist !== "") newArtist = artist;
|
|
|
|
if (typeof title == "string" && title !== "") newTitle = title;
|
2020-02-06 08:18:40 +00:00
|
|
|
return {artist: newArtist, title: newTitle};
|
2020-01-21 11:03:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2020-04-05 09:31:16 +00:00
|
|
|
export function GetMetaCoverURL(metadata) {
|
2020-01-21 11:03:41 +00:00
|
|
|
let pic_url = "";
|
|
|
|
if (metadata.common.picture !== undefined && metadata.common.picture.length > 0) {
|
|
|
|
let pic = new Blob([metadata.common.picture[0].data], {type: metadata.common.picture[0].format});
|
|
|
|
pic_url = URL.createObjectURL(pic);
|
|
|
|
}
|
|
|
|
return pic_url;
|
2020-02-06 08:18:40 +00:00
|
|
|
}
|
2020-02-10 16:34:26 +00:00
|
|
|
|
|
|
|
export function IsBytesEqual(first, second) {
|
2020-02-11 07:51:07 +00:00
|
|
|
// if want wholly check, should length first>=second
|
2020-02-10 16:34:26 +00:00
|
|
|
return first.every((val, idx) => {
|
|
|
|
return val === second[idx];
|
|
|
|
})
|
|
|
|
}
|
2020-02-11 07:51:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
export function DetectAudioExt(data, fallbackExt) {
|
|
|
|
if (IsBytesEqual(MP3_HEADER, data.slice(0, MP3_HEADER.length))) return "mp3";
|
|
|
|
if (IsBytesEqual(FLAC_HEADER, data.slice(0, FLAC_HEADER.length))) return "flac";
|
|
|
|
if (IsBytesEqual(OGG_HEADER, data.slice(0, OGG_HEADER.length))) return "ogg";
|
2020-04-05 09:31:16 +00:00
|
|
|
if (IsBytesEqual(M4A_HEADER, data.slice(4, 4 + M4A_HEADER.length))) return "m4a";
|
2020-04-23 10:15:07 +00:00
|
|
|
if (IsBytesEqual(WMA_HEADER, data.slice(0, WMA_HEADER.length))) return "wma";
|
2020-04-23 12:46:08 +00:00
|
|
|
if (IsBytesEqual(WAV_HEADER, data.slice(0, WAV_HEADER.length))) return "wav";
|
2020-02-11 07:51:07 +00:00
|
|
|
return fallbackExt;
|
|
|
|
}
|
2020-04-05 09:31:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
export async function GetWebImage(pic_url) {
|
|
|
|
try {
|
|
|
|
let resp = await fetch(pic_url);
|
|
|
|
let mime = resp.headers.get("Content-Type");
|
|
|
|
if (mime.startsWith("image/")) {
|
|
|
|
let buf = await resp.arrayBuffer();
|
|
|
|
let objBlob = new Blob([buf], {type: mime});
|
|
|
|
let objUrl = URL.createObjectURL(objBlob);
|
2020-08-03 06:03:10 +00:00
|
|
|
return {"buffer": buf, "src": pic_url, "url": objUrl, "type": mime};
|
2020-04-05 09:31:16 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
}
|
2020-08-03 06:03:10 +00:00
|
|
|
return {"buffer": null, "src": pic_url, "url": "", "type": ""}
|
2020-04-05 09:31:16 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 23:03:57 +00:00
|
|
|
export async function WriteMp3Meta(audioData, artistList, title, album, pictureData = null, pictureDesc = "Cover", originalMeta = null) {
|
2020-04-05 09:31:16 +00:00
|
|
|
const writer = new ID3Writer(audioData);
|
2020-11-20 23:03:57 +00:00
|
|
|
if (originalMeta !== null) {
|
|
|
|
artistList = originalMeta.common.artists || artistList
|
|
|
|
title = originalMeta.common.title || title
|
|
|
|
album = originalMeta.common.album || album
|
|
|
|
const frames = originalMeta.native['ID3v2.4'] || originalMeta.native['ID3v2.3'] || originalMeta.native['ID3v2.2'] || []
|
|
|
|
frames.forEach(frame => {
|
|
|
|
if (frame.id !== 'TPE1' && frame.id !== 'TIT2' && frame.id !== 'TALB') {
|
|
|
|
try {
|
|
|
|
writer.setFrame(frame.id, frame.value)
|
|
|
|
} catch (e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
writer.setFrame('TPE1', artistList)
|
|
|
|
.setFrame('TIT2', title)
|
|
|
|
.setFrame('TALB', album);
|
2020-04-05 09:31:16 +00:00
|
|
|
if (pictureData !== null) {
|
|
|
|
writer.setFrame('APIC', {
|
|
|
|
type: 3,
|
|
|
|
data: pictureData,
|
|
|
|
description: pictureDesc,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
writer.addTag();
|
|
|
|
return writer.arrayBuffer;
|
|
|
|
}
|
|
|
|
|