
* chore: Bump various packages * feat: Update everything to ESM * fix: building and porting js to cjs
197 lines
7.1 KiB
TypeScript
197 lines
7.1 KiB
TypeScript
// import { ProviderDB } from "./db";
|
|
import { EventEmitter } from "events";
|
|
import mm from "music-metadata";
|
|
import { resolve } from "node:path";
|
|
import { Md5 } from "ts-md5";
|
|
import { utils } from "../../base/utils.js";
|
|
import { readdirSync } from "node:fs";
|
|
|
|
export class LocalFiles {
|
|
static localSongs: any = [];
|
|
static localSongsArts: any = [];
|
|
// public static DB = ProviderDB.db;
|
|
static eventEmitter = new EventEmitter();
|
|
|
|
static getDataType(item_id: String | any) {
|
|
if ((item_id ?? "").startsWith("ciderlocalart")) return "artwork";
|
|
else if ((item_id ?? "").startsWith("ciderlocal")) return "track";
|
|
}
|
|
|
|
// static async sendOldLibrary() {
|
|
// ProviderDB.init();
|
|
// let rows = (await ProviderDB.db.allDocs({ include_docs: true, attachments: true })).rows.map((item: any) => {
|
|
// return item.doc;
|
|
// });
|
|
// let tracks = rows.filter((item: any) => {
|
|
// return this.getDataType(item._id) == "track";
|
|
// });
|
|
// let arts = rows.filter((item: any) => {
|
|
// return this.getDataType(item._id) == "artwork";
|
|
// });
|
|
// this.localSongs = tracks;
|
|
// this.localSongsArts = arts;
|
|
// return tracks;
|
|
// }
|
|
|
|
static async scanLibrary() {
|
|
// ProviderDB.init();
|
|
let folders = utils.getStoreValue("libraryPrefs.localPaths");
|
|
if (folders == null || folders.length == null || folders.length == 0) folders = [];
|
|
let files: any[] = [];
|
|
for (var folder of folders) {
|
|
// get files from the Music folder
|
|
files = files.concat(await LocalFiles.getFiles(folder));
|
|
}
|
|
|
|
let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"];
|
|
let audiofiles = files.filter((f) => supporttedformats.includes(f.substring(f.lastIndexOf(".") + 1)));
|
|
let metadatalist = [];
|
|
let metadatalistart = [];
|
|
let numid = 0;
|
|
for (var audio of audiofiles) {
|
|
try {
|
|
const metadata = await mm.parseFile(audio);
|
|
let lochash = Md5.hashStr(audio) ?? numid;
|
|
if (metadata != null) {
|
|
let form = {
|
|
id: "ciderlocal" + lochash,
|
|
_id: "ciderlocal" + lochash,
|
|
type: "podcast-episodes",
|
|
href: audio,
|
|
attributes: {
|
|
artwork: {
|
|
width: 3000,
|
|
height: 3000,
|
|
url: "/ciderlocalart/" + "ciderlocal" + lochash,
|
|
},
|
|
topics: [],
|
|
url: "",
|
|
subscribable: true,
|
|
mediaKind: "audio",
|
|
genreNames: [""],
|
|
// "playParams": {
|
|
// "id": "ciderlocal" + numid,
|
|
// "kind": "podcast",
|
|
// "isLibrary": true,
|
|
// "reporting": false },
|
|
trackNumber: metadata.common.track?.no ?? 0,
|
|
discNumber: metadata.common.disk?.no ?? 0,
|
|
name: metadata.common.title ?? audio.substring(audio.lastIndexOf("\\") + 1),
|
|
albumName: metadata.common.album,
|
|
artistName: metadata.common.artist,
|
|
copyright: metadata.common.copyright ?? "",
|
|
assetUrl: "file:///" + audio,
|
|
contentAdvisory: "",
|
|
releaseDateTime: `${metadata?.common?.year ?? "2022"}-05-13T00:23:00Z`,
|
|
durationInMillis: Math.floor((metadata.format.duration ?? 0) * 1000),
|
|
bitrate: Math.floor((metadata.format?.bitrate ?? 0) / 1000),
|
|
offers: [
|
|
{
|
|
kind: "get",
|
|
type: "STDQ",
|
|
},
|
|
],
|
|
contentRating: "clean",
|
|
},
|
|
flavor: Math.floor((metadata.format?.bitrate ?? 0) / 1000),
|
|
localFilesMetadata: {
|
|
lossless: metadata.format?.lossless,
|
|
container: metadata.format?.container,
|
|
bitDepth: metadata.format?.bitsPerSample ?? 0,
|
|
sampleRate: metadata.format?.sampleRate ?? 0,
|
|
},
|
|
};
|
|
let art = {
|
|
id: "ciderlocal" + lochash,
|
|
_id: "ciderlocalart" + lochash,
|
|
url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString("base64") : "",
|
|
};
|
|
metadatalistart.push(art);
|
|
numid += 1;
|
|
// ProviderDB.db.putIfNotExists(form);
|
|
// ProviderDB.db.putIfNotExists(art);
|
|
metadatalist.push(form);
|
|
|
|
if (this.localSongs.length === 0 && numid % 10 === 0) {
|
|
// send updated chunks only if there is no previous database
|
|
this.eventEmitter.emit("newtracks", metadatalist);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error("localfiles error:", e);
|
|
}
|
|
}
|
|
// console.log('metadatalist', metadatalist);
|
|
this.localSongs = metadatalist;
|
|
this.localSongsArts = metadatalistart;
|
|
return metadatalist;
|
|
}
|
|
|
|
static async getFiles(dir: string): Promise<string[]> {
|
|
const dirents = readdirSync(dir, { withFileTypes: true });
|
|
const files = await Promise.all(
|
|
dirents.map((dirent: any) => {
|
|
const res = resolve(dir, dirent.name);
|
|
return dirent.isDirectory() ? this.getFiles(res) : res;
|
|
}),
|
|
);
|
|
return Array.prototype.concat(...files);
|
|
}
|
|
|
|
// static async cleanUpDB() {
|
|
// let folders = utils.getStoreValue("libraryPrefs.localPaths");
|
|
// let rows = (await ProviderDB.db.allDocs({ include_docs: true, attachments: true })).rows.map((item: any) => {
|
|
// return item.doc;
|
|
// });
|
|
// let tracks = rows.filter((item: any) => {
|
|
// return (
|
|
// this.getDataType(item._id) == "track" &&
|
|
// !folders.some((i: String) => {
|
|
// return item["attributes"]["assetUrl"].startsWith("file:///" + i);
|
|
// })
|
|
// );
|
|
// });
|
|
// let hashs = tracks.map((i: any) => {
|
|
// return i._id;
|
|
// });
|
|
// for (let hash of hashs) {
|
|
// try {
|
|
// ProviderDB.db.get(hash).then(function (doc: any) {
|
|
// return ProviderDB.db.remove(doc);
|
|
// });
|
|
// } catch (e) {}
|
|
// try {
|
|
// ProviderDB.db.get(hash.replace("ciderlocal", "ciderlocalart")).then(function (doc: any) {
|
|
// return ProviderDB.db.remove(doc);
|
|
// });
|
|
// } catch (e) {}
|
|
// }
|
|
// }
|
|
|
|
static setupHandlers() {
|
|
const app = utils.getExpress();
|
|
console.log("Setting up handlers for local files");
|
|
app.get("/ciderlocal/:songs", (req: any, res: any) => {
|
|
const audio = atob(req.params.songs.replace(/_/g, "/").replace(/-/g, "+"));
|
|
// console.log('auss', LocalFiles.localSongs)
|
|
let data = {
|
|
data: LocalFiles.localSongs.filter((f: any) => audio.split(",").includes(f.id)),
|
|
};
|
|
res.send(data);
|
|
});
|
|
|
|
app.get("/ciderlocalart/:songs", (req: any, res: any) => {
|
|
const audio = req.params.songs;
|
|
// metadata.common.picture[0].data.toString('base64')
|
|
|
|
res.setHeader("Cache-Control", "public, max-age=31536000");
|
|
res.setHeader("Expires", new Date(Date.now() + 31536000000).toUTCString());
|
|
res.setHeader("Content-Type", "image/jpeg");
|
|
|
|
let data = LocalFiles.localSongsArts.filter((f: any) => f.id == audio);
|
|
res.status(200).send(Buffer.from(data[0]?.url, "base64"));
|
|
});
|
|
|
|
return app;
|
|
}
|
|
}
|