changes for local files: read below
* added pouchdb-node * moved all logic for local files to src/main/providers/local * added new local library section on sidebar
This commit is contained in:
parent
e69e394e2a
commit
2cb29c28c5
10 changed files with 165 additions and 118 deletions
|
@ -40,6 +40,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/electron": "^3.0.7",
|
"@sentry/electron": "^3.0.7",
|
||||||
"@sentry/integrations": "^6.19.6",
|
"@sentry/integrations": "^6.19.6",
|
||||||
|
"@types/pouchdb": "^6.4.0",
|
||||||
|
"@types/pouchdb-node": "^6.1.4",
|
||||||
"adm-zip": "0.4.10",
|
"adm-zip": "0.4.10",
|
||||||
"airtunes2": "git+https://github.com/ciderapp/node_airtunes2",
|
"airtunes2": "git+https://github.com/ciderapp/node_airtunes2",
|
||||||
"castv2-client": "^1.2.0",
|
"castv2-client": "^1.2.0",
|
||||||
|
@ -58,11 +60,14 @@
|
||||||
"jimp": "^0.16.1",
|
"jimp": "^0.16.1",
|
||||||
"jsonc": "^2.0.0",
|
"jsonc": "^2.0.0",
|
||||||
"lastfmapi": "^0.1.1",
|
"lastfmapi": "^0.1.1",
|
||||||
|
"level": "^8.0.0",
|
||||||
"mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git",
|
"mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git",
|
||||||
"mpris-service": "^2.1.2",
|
"mpris-service": "^2.1.2",
|
||||||
"music-metadata": "^7.12.3",
|
"music-metadata": "^7.12.3",
|
||||||
"node-gyp": "^9.0.0",
|
"node-gyp": "^9.0.0",
|
||||||
"node-ssdp": "^4.0.1",
|
"node-ssdp": "^4.0.1",
|
||||||
|
"pouchdb-adapter-leveldb": "^7.3.0",
|
||||||
|
"pouchdb-node": "^7.3.0",
|
||||||
"qrcode": "^1.5.0",
|
"qrcode": "^1.5.0",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {join} from "path";
|
import { join } from "path";
|
||||||
import {app, BrowserWindow as bw, ipcMain, ShareMenu, shell, screen, dialog} from "electron";
|
import { app, BrowserWindow as bw, ipcMain, ShareMenu, shell, screen, dialog } from "electron";
|
||||||
import * as windowStateKeeper from "electron-window-state";
|
import * as windowStateKeeper from "electron-window-state";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import * as getPort from "get-port";
|
import * as getPort from "get-port";
|
||||||
|
@ -27,8 +27,7 @@ import { watch } from "chokidar";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
import wallpaper from "wallpaper";
|
import wallpaper from "wallpaper";
|
||||||
import * as AdmZip from "adm-zip";
|
import * as AdmZip from "adm-zip";
|
||||||
import * as path from 'path';
|
import { LocalFiles } from "../providers/local/";
|
||||||
const { readdir } = require('fs').promises;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,12 +39,11 @@ const { readdir } = require('fs').promises;
|
||||||
export class BrowserWindow {
|
export class BrowserWindow {
|
||||||
public static win: any | undefined = null;
|
public static win: any | undefined = null;
|
||||||
private devMode: boolean = !app.isPackaged;
|
private devMode: boolean = !app.isPackaged;
|
||||||
|
public static express: any | undefined = null;
|
||||||
|
|
||||||
private audioStream: any = new Stream.PassThrough();
|
private audioStream: any = new Stream.PassThrough();
|
||||||
private headerSent: any = false;
|
private headerSent: any = false;
|
||||||
private chromecastIP: any = [];
|
private chromecastIP: any = [];
|
||||||
private localSongs: any = [];
|
|
||||||
private localSongsArts: any = [];
|
|
||||||
private clientPort: number = 0;
|
private clientPort: number = 0;
|
||||||
private remotePort: number = 6942;
|
private remotePort: number = 6942;
|
||||||
private EnvironmentVariables: object = {
|
private EnvironmentVariables: object = {
|
||||||
|
@ -410,9 +408,10 @@ export class BrowserWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the webserver for the browser window to load
|
// Start the webserver for the browser window to load
|
||||||
|
// LocalFiles.DB.init()
|
||||||
this.startWebServer();
|
this.startWebServer();
|
||||||
|
|
||||||
|
|
||||||
BrowserWindow.win = new bw(this.options);
|
BrowserWindow.win = new bw(this.options);
|
||||||
// cant be built in CI
|
// cant be built in CI
|
||||||
// if (process.platform === "win32" && (utils.getStoreValue('visual.transparent') ?? false)) {
|
// if (process.platform === "win32" && (utils.getStoreValue('visual.transparent') ?? false)) {
|
||||||
|
@ -468,7 +467,7 @@ export class BrowserWindow {
|
||||||
*/
|
*/
|
||||||
private startWebServer(): void {
|
private startWebServer(): void {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
BrowserWindow.express = app;
|
||||||
app.use(express.static(join(utils.getPath('srcPath'), "./renderer/")));
|
app.use(express.static(join(utils.getPath('srcPath'), "./renderer/")));
|
||||||
app.set("views", join(utils.getPath('srcPath'), "./renderer/views"));
|
app.set("views", join(utils.getPath('srcPath'), "./renderer/views"));
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
|
@ -553,23 +552,6 @@ export class BrowserWindow {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/ciderlocal/:songs", (req, res) => {
|
|
||||||
const audio = atob(req.params.songs.replace(/_/g, '/').replace(/-/g, '+'));
|
|
||||||
//console.log('auss', audio)
|
|
||||||
let data = {data:
|
|
||||||
this.localSongs.filter((f: any) => audio.split(',').includes(f.id))};
|
|
||||||
res.send(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/ciderlocalart/:songs", (req, res) => {
|
|
||||||
const audio = req.params.songs;
|
|
||||||
// metadata.common.picture[0].data.toString('base64')
|
|
||||||
let data =
|
|
||||||
this.localSongsArts.filter((f: any) => f.id == audio);
|
|
||||||
res.status(200).send(Buffer.from(data[0]?.url, 'base64'));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/themes/:theme/*", (req: { params: { theme: string, 0: string } }, res) => {
|
app.get("/themes/:theme/*", (req: { params: { theme: string, 0: string } }, res) => {
|
||||||
const theme = req.params.theme;
|
const theme = req.params.theme;
|
||||||
const file = req.params[0];
|
const file = req.params[0];
|
||||||
|
@ -632,6 +614,9 @@ export class BrowserWindow {
|
||||||
utils.getWindow().webContents.send('setStoreValue', 'connectUser', JSON.parse(req.params.data))
|
utils.getWindow().webContents.send('setStoreValue', 'connectUser', JSON.parse(req.params.data))
|
||||||
res.redirect(`https://connect.cidercollective.dev/linked.html`)
|
res.redirect(`https://connect.cidercollective.dev/linked.html`)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
LocalFiles.setupHandlers()
|
||||||
|
|
||||||
// [Connect] Set auth URL in store for `shell.openExternal`
|
// [Connect] Set auth URL in store for `shell.openExternal`
|
||||||
utils.setStoreValue('cc_authURL', `https://connect.cidercollective.dev/callback/discord?app=cider&appPort=${this.clientPort}`)
|
utils.setStoreValue('cc_authURL', `https://connect.cidercollective.dev/callback/discord?app=cider&appPort=${this.clientPort}`)
|
||||||
console.log(`[Connect] Auth URL: ${utils.getStoreValue('cc_authURL')}`)
|
console.log(`[Connect] Auth URL: ${utils.getStoreValue('cc_authURL')}`)
|
||||||
|
@ -1199,95 +1184,10 @@ export class BrowserWindow {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
ipcMain.on("scanLibrary", async (event, folders) => {
|
ipcMain.handle("scanLibrary", async (event, folders) => {
|
||||||
async function getFiles(dir: any) {
|
const metadatalist = await LocalFiles.scanLibrary()
|
||||||
const dirents = await readdir(dir, { withFileTypes: true });
|
BrowserWindow.win.webContents.send('getUpdatedLocalList', metadatalist);
|
||||||
const files = await Promise.all(dirents.map((dirent: any) => {
|
|
||||||
const res = path.resolve(dir, dirent.name);
|
|
||||||
return dirent.isDirectory() ? getFiles(res) : res;
|
|
||||||
}));
|
|
||||||
return Array.prototype.concat(...files);
|
|
||||||
}
|
|
||||||
if (folders == null || folders.length == null || folders.length == 0) folders = ["D:\\Music"]
|
|
||||||
console.log('folders', folders)
|
|
||||||
let files: any[] = []
|
|
||||||
for (var folder of folders){
|
|
||||||
// get files from the Music folder
|
|
||||||
files = files.concat(await getFiles(folder))
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log("cider.files", files2);
|
|
||||||
let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"]
|
|
||||||
let audiofiles = files.filter(f => supporttedformats.includes(f.substring(f.lastIndexOf('.') + 1)));
|
|
||||||
// console.log("cider.files2", audiofiles, audiofiles.length);
|
|
||||||
let metadatalist = []
|
|
||||||
let metadatalistart = []
|
|
||||||
let numid = 0;
|
|
||||||
for (var audio of audiofiles) {
|
|
||||||
try{
|
|
||||||
const metadata = await mm.parseFile(audio);
|
|
||||||
if (metadata != null){
|
|
||||||
let form = {
|
|
||||||
"id": "ciderlocal" + numid,
|
|
||||||
"type": "podcast-episodes",
|
|
||||||
"href": audio,
|
|
||||||
"attributes": {
|
|
||||||
"artwork": {
|
|
||||||
"width": 3000,
|
|
||||||
"height": 3000,
|
|
||||||
"url": "/ciderlocalart/" + "ciderlocal" + numid,
|
|
||||||
},
|
|
||||||
"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),
|
|
||||||
|
|
||||||
"offers": [
|
|
||||||
{
|
|
||||||
"kind": "get",
|
|
||||||
"type": "STDQ"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"contentRating": "clean"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
metadatalistart.push({
|
|
||||||
id : "ciderlocal" + numid,
|
|
||||||
url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString('base64') : "",
|
|
||||||
})
|
})
|
||||||
numid += 1;
|
|
||||||
metadatalist.push(form)}
|
|
||||||
} catch (e){}
|
|
||||||
}
|
|
||||||
// console.log('metadatalist', metadatalist);
|
|
||||||
this.localSongs = metadatalist;
|
|
||||||
this.localSongsArts = metadatalistart;
|
|
||||||
BrowserWindow.win.webContents.send('getUpdatedLocalList', metadatalist);
|
|
||||||
}
|
|
||||||
// console.log('metadatalist', metadatalist);
|
|
||||||
this.localSongs = metadatalist;
|
|
||||||
BrowserWindow.win.webContents.send('getUpdatedLocalList', metadatalist);
|
|
||||||
}
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
ipcMain.on('writeWAV', (event, leftpcm, rightpcm, bufferlength) => {
|
ipcMain.on('writeWAV', (event, leftpcm, rightpcm, bufferlength) => {
|
||||||
|
|
||||||
|
@ -1487,7 +1387,7 @@ export class BrowserWindow {
|
||||||
|
|
||||||
ipcMain.handle('folderSelector', async (_event) => {
|
ipcMain.handle('folderSelector', async (_event) => {
|
||||||
let u = await dialog.showOpenDialog({
|
let u = await dialog.showOpenDialog({
|
||||||
properties: ['openDirectory','multiSelections']
|
properties: ['openDirectory', 'multiSelections']
|
||||||
});
|
});
|
||||||
return u.filePaths
|
return u.filePaths
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,7 +31,8 @@ export class Store {
|
||||||
"applemusic": false,
|
"applemusic": false,
|
||||||
"library": false,
|
"library": false,
|
||||||
"amplaylists": false,
|
"amplaylists": false,
|
||||||
"playlists": false
|
"playlists": false,
|
||||||
|
"localLibrary": false
|
||||||
},
|
},
|
||||||
"onStartup": {
|
"onStartup": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
|
|
@ -68,6 +68,14 @@ export class utils {
|
||||||
return ipcMain
|
return ipcMain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the Express instance
|
||||||
|
* @returns {any}
|
||||||
|
*/
|
||||||
|
static getExpress(): any {
|
||||||
|
return bw.express
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the i18n locale for the given language.
|
* Fetches the i18n locale for the given language.
|
||||||
* @param language {string} The language to fetch the locale for.
|
* @param language {string} The language to fetch the locale for.
|
||||||
|
|
|
@ -13,7 +13,9 @@ import {BrowserWindow} from "./base/browserwindow";
|
||||||
import {init as Sentry} from "@sentry/electron";
|
import {init as Sentry} from "@sentry/electron";
|
||||||
import {RewriteFrames} from "@sentry/integrations";
|
import {RewriteFrames} from "@sentry/integrations";
|
||||||
import {components, ipcMain} from "electron"
|
import {components, ipcMain} from "electron"
|
||||||
|
import {ProviderDB} from "./providers/local/db";
|
||||||
|
|
||||||
|
ProviderDB.init()
|
||||||
|
|
||||||
// Analytics for debugging fun yeah.
|
// Analytics for debugging fun yeah.
|
||||||
Sentry({
|
Sentry({
|
||||||
|
|
8
src/main/providers/local/db/index.ts
Normal file
8
src/main/providers/local/db/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import * as PouchDB from 'pouchdb-node';
|
||||||
|
|
||||||
|
export class ProviderDB {
|
||||||
|
public static db: any = null
|
||||||
|
static async init() {
|
||||||
|
// ProviderDB.db = new PouchDB('tracks')
|
||||||
|
}
|
||||||
|
}
|
122
src/main/providers/local/index.ts
Normal file
122
src/main/providers/local/index.ts
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// import { ProviderDB } from "./db";
|
||||||
|
import * as path from 'path';
|
||||||
|
const { readdir } = require('fs').promises;
|
||||||
|
import { utils } from '../../base/utils';
|
||||||
|
import * as mm from 'music-metadata';
|
||||||
|
|
||||||
|
export class LocalFiles {
|
||||||
|
static localSongs: any = [];
|
||||||
|
static localSongsArts: any = [];
|
||||||
|
// public static DB = ProviderDB.db;
|
||||||
|
|
||||||
|
|
||||||
|
static async scanLibrary() {
|
||||||
|
let folders = utils.getStoreValue("libraryPrefs.localPaths")
|
||||||
|
if (folders == null || folders.length == null || folders.length == 0) folders = ["D:\\Music"]
|
||||||
|
console.log('folders', folders)
|
||||||
|
let files: any[] = []
|
||||||
|
for (var folder of folders) {
|
||||||
|
// get files from the Music folder
|
||||||
|
files = files.concat(await LocalFiles.getFiles(folder))
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("cider.files", files2);
|
||||||
|
let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"]
|
||||||
|
let audiofiles = files.filter(f => supporttedformats.includes(f.substring(f.lastIndexOf('.') + 1)));
|
||||||
|
// console.log("cider.files2", audiofiles, audiofiles.length);
|
||||||
|
let metadatalist = []
|
||||||
|
let metadatalistart = []
|
||||||
|
let numid = 0;
|
||||||
|
for (var audio of audiofiles) {
|
||||||
|
try {
|
||||||
|
const metadata = await mm.parseFile(audio);
|
||||||
|
if (metadata != null) {
|
||||||
|
let form = {
|
||||||
|
"id": "ciderlocal" + numid,
|
||||||
|
"type": "podcast-episodes",
|
||||||
|
"href": audio,
|
||||||
|
"attributes": {
|
||||||
|
"artwork": {
|
||||||
|
"width": 3000,
|
||||||
|
"height": 3000,
|
||||||
|
"url": "/ciderlocalart/" + "ciderlocal" + numid,
|
||||||
|
},
|
||||||
|
"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),
|
||||||
|
|
||||||
|
"offers": [
|
||||||
|
{
|
||||||
|
"kind": "get",
|
||||||
|
"type": "STDQ"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contentRating": "clean"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
metadatalistart.push({
|
||||||
|
id: "ciderlocal" + numid,
|
||||||
|
url: metadata.common.picture != undefined ? metadata.common.picture[0].data.toString('base64') : "",
|
||||||
|
})
|
||||||
|
numid += 1;
|
||||||
|
metadatalist.push(form)
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
}
|
||||||
|
// console.log('metadatalist', metadatalist);
|
||||||
|
this.localSongs = metadatalist;
|
||||||
|
this.localSongsArts = metadatalistart;
|
||||||
|
return metadatalist;
|
||||||
|
}
|
||||||
|
static async getFiles(dir: any) {
|
||||||
|
const dirents = await readdir(dir, { withFileTypes: true });
|
||||||
|
const files = await Promise.all(dirents.map((dirent: any) => {
|
||||||
|
const res = path.resolve(dir, dirent.name);
|
||||||
|
return dirent.isDirectory() ? this.getFiles(res) : res;
|
||||||
|
}));
|
||||||
|
return Array.prototype.concat(...files);
|
||||||
|
}
|
||||||
|
|
||||||
|
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', audio)
|
||||||
|
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')
|
||||||
|
let data =
|
||||||
|
LocalFiles.localSongsArts.filter((f: any) => f.id == audio);
|
||||||
|
res.status(200).send(Buffer.from(data[0]?.url, 'base64'));
|
||||||
|
});
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
}
|
|
@ -1074,7 +1074,7 @@ const app = new Vue({
|
||||||
this.checkForThemeUpdates()
|
this.checkForThemeUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcRenderer.send("scanLibrary",app.cfg.libraryPrefs.localPaths)
|
ipcRenderer.invoke("scanLibrary")
|
||||||
},
|
},
|
||||||
showFoo(querySelector, time) {
|
showFoo(querySelector, time) {
|
||||||
clearTimeout(this.idleTimer);
|
clearTimeout(this.idleTimer);
|
||||||
|
|
|
@ -53,12 +53,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$root.cfg.libraryPrefs.localPaths = this.folders;
|
this.$root.cfg.libraryPrefs.localPaths = this.folders;
|
||||||
ipcRenderer.send("scanLibrary",app.cfg.libraryPrefs.localPaths)
|
ipcRenderer.invoke("scanLibrary")
|
||||||
},
|
},
|
||||||
remove(dir){
|
remove(dir){
|
||||||
this.folders = this.folders.filter(item => item !== dir)
|
this.folders = this.folders.filter(item => item !== dir)
|
||||||
this.$root.cfg.libraryPrefs.localPaths = this.folders;
|
this.$root.cfg.libraryPrefs.localPaths = this.folders;
|
||||||
ipcRenderer.send("scanLibrary",app.cfg.libraryPrefs.localPaths)
|
ipcRenderer.invoke("scanLibrary")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["node_modules/*"]
|
"*": ["node_modules/*"]
|
||||||
},
|
},
|
||||||
|
"skipLibCheck": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/musickit-typescript",
|
"node_modules/musickit-typescript",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue