Remote integrated with express. Ready for backend
This commit is contained in:
parent
e8b6dd3180
commit
d7a8b31684
2 changed files with 344 additions and 338 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as electron from "electron";
|
import * as electron from "electron";
|
||||||
import * as windowStateKeeper from "electron-window-state";
|
import * as windowStateKeeper from "electron-window-state";
|
||||||
|
@ -5,8 +6,9 @@ import * as express from "express";
|
||||||
import * as getPort from "get-port";
|
import * as getPort from "get-port";
|
||||||
import * as yt from "youtube-search-without-api-key";
|
import * as yt from "youtube-search-without-api-key";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {Stream} from "stream";
|
import { Stream } from "stream";
|
||||||
|
import * as qrcode from "qrcode-terminal";
|
||||||
|
import * as os from "os";
|
||||||
export class Win {
|
export class Win {
|
||||||
win: any | undefined = null;
|
win: any | undefined = null;
|
||||||
app: any | undefined = null;
|
app: any | undefined = null;
|
||||||
|
@ -24,17 +26,21 @@ export class Win {
|
||||||
ciderCache: path.resolve(electron.app.getPath("userData"), "CiderCache"),
|
ciderCache: path.resolve(electron.app.getPath("userData"), "CiderCache"),
|
||||||
themes: path.resolve(electron.app.getPath("userData"), "Themes"),
|
themes: path.resolve(electron.app.getPath("userData"), "Themes"),
|
||||||
plugins: path.resolve(electron.app.getPath("userData"), "Plugins"),
|
plugins: path.resolve(electron.app.getPath("userData"), "Plugins"),
|
||||||
}
|
};
|
||||||
private audioStream: any = new Stream.PassThrough();
|
private audioStream: any = new Stream.PassThrough();
|
||||||
private clientPort: number = 0;
|
private clientPort: number = 0;
|
||||||
|
private remotePort: number = 6942;
|
||||||
private EnvironmentVariables: object = {
|
private EnvironmentVariables: object = {
|
||||||
"env": {
|
env: {
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
dev: electron.app.isPackaged
|
dev: electron.app.isPackaged,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
private options: any = {
|
private options: any = {
|
||||||
icon: path.join(this.paths.resourcePath, `icons/icon.` + (process.platform === "win32" ? "ico" : "png")),
|
icon: path.join(
|
||||||
|
this.paths.resourcePath,
|
||||||
|
`icons/icon.` + (process.platform === "win32" ? "ico" : "png")
|
||||||
|
),
|
||||||
width: 1024,
|
width: 1024,
|
||||||
height: 600,
|
height: 600,
|
||||||
x: undefined,
|
x: undefined,
|
||||||
|
@ -43,8 +49,8 @@ export class Win {
|
||||||
minHeight: 410,
|
minHeight: 410,
|
||||||
frame: false,
|
frame: false,
|
||||||
title: "Cider",
|
title: "Cider",
|
||||||
vibrancy: 'dark',
|
vibrancy: "dark",
|
||||||
transparent: (process.platform === "darwin"),
|
transparent: process.platform === "darwin",
|
||||||
hasShadow: false,
|
hasShadow: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
@ -57,27 +63,27 @@ export class Win {
|
||||||
nodeIntegrationInWorker: false,
|
nodeIntegrationInWorker: false,
|
||||||
webSecurity: false,
|
webSecurity: false,
|
||||||
|
|
||||||
preload: path.join(this.paths.srcPath, './preload/cider-preload.js')
|
preload: path.join(this.paths.srcPath, "./preload/cider-preload.js"),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the browser window
|
* Creates the browser window
|
||||||
*/
|
*/
|
||||||
async createWindow(): Promise<void> {
|
async createWindow(): Promise<void> {
|
||||||
this.clientPort = await getPort({port: 9000});
|
this.clientPort = await getPort({ port: 9000 });
|
||||||
this.verifyFiles();
|
this.verifyFiles();
|
||||||
|
|
||||||
// Load the previous state with fallback to defaults
|
// Load the previous state with fallback to defaults
|
||||||
const windowState = windowStateKeeper({
|
const windowState = windowStateKeeper({
|
||||||
defaultWidth: 1024,
|
defaultWidth: 1024,
|
||||||
defaultHeight: 600
|
defaultHeight: 600,
|
||||||
});
|
});
|
||||||
this.options.width = windowState.width;
|
this.options.width = windowState.width;
|
||||||
this.options.height = windowState.height;
|
this.options.height = windowState.height;
|
||||||
|
|
||||||
// Start the webserver for the browser window to load
|
// Start the webserver for the browser window to load
|
||||||
this.startWebServer()
|
this.startWebServer();
|
||||||
|
|
||||||
this.win = new electron.BrowserWindow(this.options);
|
this.win = new electron.BrowserWindow(this.options);
|
||||||
|
|
||||||
|
@ -88,7 +94,6 @@ export class Win {
|
||||||
// Register listeners on Window to track size and position of the Window.
|
// Register listeners on Window to track size and position of the Window.
|
||||||
windowState.manage(this.win);
|
windowState.manage(this.win);
|
||||||
|
|
||||||
|
|
||||||
return this.win;
|
return this.win;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,25 +101,29 @@ export class Win {
|
||||||
* Verifies the files for the renderer to use (Cache, library info, etc.)
|
* Verifies the files for the renderer to use (Cache, library info, etc.)
|
||||||
*/
|
*/
|
||||||
private verifyFiles(): void {
|
private verifyFiles(): void {
|
||||||
const expectedDirectories = [
|
const expectedDirectories = ["CiderCache"];
|
||||||
"CiderCache"
|
|
||||||
]
|
|
||||||
const expectedFiles = [
|
const expectedFiles = [
|
||||||
"library-songs.json",
|
"library-songs.json",
|
||||||
"library-artists.json",
|
"library-artists.json",
|
||||||
"library-albums.json",
|
"library-albums.json",
|
||||||
"library-playlists.json",
|
"library-playlists.json",
|
||||||
"library-recentlyAdded.json",
|
"library-recentlyAdded.json",
|
||||||
]
|
];
|
||||||
for (let i = 0; i < expectedDirectories.length; i++) {
|
for (let i = 0; i < expectedDirectories.length; i++) {
|
||||||
if (!fs.existsSync(path.join(electron.app.getPath("userData"), expectedDirectories[i]))) {
|
if (
|
||||||
fs.mkdirSync(path.join(electron.app.getPath("userData"), expectedDirectories[i]))
|
!fs.existsSync(
|
||||||
|
path.join(electron.app.getPath("userData"), expectedDirectories[i])
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
fs.mkdirSync(
|
||||||
|
path.join(electron.app.getPath("userData"), expectedDirectories[i])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let i = 0; i < expectedFiles.length; i++) {
|
for (let i = 0; i < expectedFiles.length; i++) {
|
||||||
const file = path.join(this.paths.ciderCache, expectedFiles[i])
|
const file = path.join(this.paths.ciderCache, expectedFiles[i]);
|
||||||
if (!fs.existsSync(file)) {
|
if (!fs.existsSync(file)) {
|
||||||
fs.writeFileSync(file, JSON.stringify([]))
|
fs.writeFileSync(file, JSON.stringify([]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,22 +134,30 @@ export class Win {
|
||||||
private startWebServer(): void {
|
private startWebServer(): void {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(express.static(path.join(this.paths.srcPath, './renderer/')));
|
app.use(express.static(path.join(this.paths.srcPath, "./renderer/")));
|
||||||
app.set("views", path.join(this.paths.srcPath, './renderer/views'));
|
app.set("views", path.join(this.paths.srcPath, "./renderer/views"));
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
|
let firstRequest = true;
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (req.url.includes("audio.webm") || (req.headers.host.includes("localhost") && (this.devMode || req.headers["user-agent"].includes("Electron")))) {
|
if (
|
||||||
|
req.url.includes("audio.webm") ||
|
||||||
|
(req.headers.host.includes("localhost") &&
|
||||||
|
(this.devMode || req.headers["user-agent"].includes("Electron")))
|
||||||
|
) {
|
||||||
next();
|
next();
|
||||||
|
} else {
|
||||||
|
res.redirect("https://discord.gg/applemusic");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.render("main", this.EnvironmentVariables)
|
|
||||||
|
|
||||||
|
res.render("main", this.EnvironmentVariables);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/audio.webm', (req, res) => {
|
app.get("/audio.webm", (req, res) => {
|
||||||
try {
|
try {
|
||||||
req.socket.setTimeout(Number.MAX_SAFE_INTEGER);
|
req.socket.setTimeout(Number.MAX_SAFE_INTEGER);
|
||||||
// CiderBase.requests.push({req: req, res: res});
|
// CiderBase.requests.push({req: req, res: res});
|
||||||
|
@ -150,21 +167,43 @@ export class Win {
|
||||||
// requests.splice(pos, 1);
|
// requests.splice(pos, 1);
|
||||||
// console.info("CLOSED", CiderBase.requests.length);
|
// console.info("CLOSED", CiderBase.requests.length);
|
||||||
// });
|
// });
|
||||||
this.audioStream.on('data', (data: any) => {
|
this.audioStream.on("data", (data: any) => {
|
||||||
try {
|
try {
|
||||||
res.write(data);
|
res.write(data);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.log(ex)
|
console.log(ex);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.log(ex)
|
console.log(ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
//app.use(express.static())
|
||||||
|
|
||||||
app.listen(this.clientPort, () => {
|
app.listen(this.clientPort, () => {
|
||||||
console.log(`Cider client port: ${this.clientPort}`);
|
console.log(`Cider client port: ${this.clientPort}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remote Client (I had no idea how to add it to our existing express server, so I just made another one) -@quacksire
|
||||||
|
*/
|
||||||
|
const remote = express();
|
||||||
|
remote.use(express.static(path.join(this.paths.srcPath, "./web-remote/")))
|
||||||
|
remote.listen(this.remotePort, () => {
|
||||||
|
console.log(`Cider remote port: ${this.clientPort}`);
|
||||||
|
if (firstRequest) {
|
||||||
|
console.log("---- Ignore Me ;) ---");
|
||||||
|
qrcode.generate(`http://${os.hostname}:${this.clientPort}/remote`);
|
||||||
|
console.log("---- Ignore Me ;) ---");
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* USING https://www.npmjs.com/package/qrcode-terminal for terminal
|
||||||
|
* WE SHOULD USE https://www.npmjs.com/package/qrcode for the remote (or others) for showing to user via an in-app dialog
|
||||||
|
* -@quacksire
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
firstRequest = false;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,181 +213,225 @@ export class Win {
|
||||||
// intercept "https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js" and redirect to local file "./apple-hls.js" instead
|
// intercept "https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js" and redirect to local file "./apple-hls.js" instead
|
||||||
this.win.webContents.session.webRequest.onBeforeRequest(
|
this.win.webContents.session.webRequest.onBeforeRequest(
|
||||||
{
|
{
|
||||||
urls: ["https://*/*.js"]
|
urls: ["https://*/*.js"],
|
||||||
},
|
},
|
||||||
(details: { url: string | string[]; }, callback: (arg0: { redirectURL?: string; cancel?: boolean; }) => void) => {
|
(
|
||||||
|
details: { url: string | string[] },
|
||||||
|
callback: (arg0: { redirectURL?: string; cancel?: boolean }) => void
|
||||||
|
) => {
|
||||||
if (details.url.includes("hls.js")) {
|
if (details.url.includes("hls.js")) {
|
||||||
callback({
|
callback({
|
||||||
redirectURL: `http://localhost:${this.clientPort}/apple-hls.js`
|
redirectURL: `http://localhost:${this.clientPort}/apple-hls.js`,
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
callback({
|
callback({
|
||||||
cancel: false
|
cancel: false,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
|
|
||||||
this.win.webContents.session.webRequest.onBeforeSendHeaders(async (details: { url: string; requestHeaders: { [x: string]: string; }; }, callback: (arg0: { requestHeaders: any; }) => void) => {
|
this.win.webContents.session.webRequest.onBeforeSendHeaders(
|
||||||
if (details.url === "https://buy.itunes.apple.com/account/web/info") {
|
async (
|
||||||
details.requestHeaders['sec-fetch-site'] = 'same-site';
|
details: { url: string; requestHeaders: { [x: string]: string } },
|
||||||
details.requestHeaders['DNT'] = '1';
|
callback: (arg0: { requestHeaders: any }) => void
|
||||||
let itspod = await this.win.webContents.executeJavaScript(`window.localStorage.getItem("music.ampwebplay.itspod")`)
|
) => {
|
||||||
if (itspod != null)
|
if (details.url === "https://buy.itunes.apple.com/account/web/info") {
|
||||||
details.requestHeaders['Cookie'] = `itspod=${itspod}`
|
details.requestHeaders["sec-fetch-site"] = "same-site";
|
||||||
|
details.requestHeaders["DNT"] = "1";
|
||||||
|
let itspod = await this.win.webContents.executeJavaScript(
|
||||||
|
`window.localStorage.getItem("music.ampwebplay.itspod")`
|
||||||
|
);
|
||||||
|
if (itspod != null)
|
||||||
|
details.requestHeaders["Cookie"] = `itspod=${itspod}`;
|
||||||
|
}
|
||||||
|
callback({ requestHeaders: details.requestHeaders });
|
||||||
}
|
}
|
||||||
callback({requestHeaders: details.requestHeaders})
|
);
|
||||||
})
|
|
||||||
|
|
||||||
let location = `http://localhost:${this.clientPort}/`
|
let location = `http://localhost:${this.clientPort}/`;
|
||||||
|
|
||||||
if (electron.app.isPackaged) {
|
if (electron.app.isPackaged) {
|
||||||
this.win.loadURL(location)
|
this.win.loadURL(location);
|
||||||
} else {
|
} else {
|
||||||
this.win.loadURL(location, {userAgent: 'Cider Development Environment'})
|
this.win.loadURL(location, {
|
||||||
|
userAgent: "Cider Development Environment",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the window handlers
|
* Initializes the window handlers
|
||||||
*/
|
*/
|
||||||
private startHandlers(): void {
|
private startHandlers(): void {
|
||||||
|
|
||||||
/**********************************************************************************************************************
|
/**********************************************************************************************************************
|
||||||
* ipcMain Events
|
* ipcMain Events
|
||||||
****************************************************************************************************************** */
|
****************************************************************************************************************** */
|
||||||
electron.ipcMain.on("cider-platform", (event) => {
|
electron.ipcMain.on("cider-platform", (event) => {
|
||||||
event.returnValue = process.platform
|
event.returnValue = process.platform;
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on("get-gpu-mode", (event) => {
|
|
||||||
event.returnValue = process.platform
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on("is-dev", (event) => {
|
|
||||||
event.returnValue = this.devMode
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('close', () => { // listen for close event
|
|
||||||
this.win.close();
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('put-library-songs', (event, arg) => {
|
|
||||||
fs.writeFileSync(path.join(this.paths.ciderCache, "library-songs.json"), JSON.stringify(arg))
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('put-library-artists', (event, arg) => {
|
|
||||||
fs.writeFileSync(path.join(this.paths.ciderCache, "library-artists.json"), JSON.stringify(arg))
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('put-library-albums', (event, arg) => {
|
|
||||||
fs.writeFileSync(path.join(this.paths.ciderCache, "library-albums.json"), JSON.stringify(arg))
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('put-library-playlists', (event, arg) => {
|
|
||||||
fs.writeFileSync(path.join(this.paths.ciderCache, "library-playlists.json"), JSON.stringify(arg))
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('put-library-recentlyAdded', (event, arg) => {
|
|
||||||
fs.writeFileSync(path.join(this.paths.ciderCache, "library-recentlyAdded.json"), JSON.stringify(arg))
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('get-library-songs', (event) => {
|
|
||||||
let librarySongs = fs.readFileSync(path.join(this.paths.ciderCache, "library-songs.json"), "utf8")
|
|
||||||
event.returnValue = JSON.parse(librarySongs)
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('get-library-artists', (event) => {
|
|
||||||
let libraryArtists = fs.readFileSync(path.join(this.paths.ciderCache, "library-artists.json"), "utf8")
|
|
||||||
event.returnValue = JSON.parse(libraryArtists)
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('get-library-albums', (event) => {
|
|
||||||
let libraryAlbums = fs.readFileSync(path.join(this.paths.ciderCache, "library-albums.json"), "utf8")
|
|
||||||
event.returnValue = JSON.parse(libraryAlbums)
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('get-library-playlists', (event) => {
|
|
||||||
let libraryPlaylists = fs.readFileSync(path.join(this.paths.ciderCache, "library-playlists.json"), "utf8")
|
|
||||||
event.returnValue = JSON.parse(libraryPlaylists)
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('get-library-recentlyAdded', (event) => {
|
|
||||||
let libraryRecentlyAdded = fs.readFileSync(path.join(this.paths.ciderCache, "library-recentlyAdded.json"), "utf8")
|
|
||||||
event.returnValue = JSON.parse(libraryRecentlyAdded)
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.handle('getYTLyrics', async (event, track, artist) => {
|
|
||||||
const u = track + " " + artist + " official video";
|
|
||||||
return await yt.search(u)
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.handle('setVibrancy', (event, key, value) => {
|
|
||||||
this.win.setVibrancy(value)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
electron.ipcMain.on('maximize', () => { // listen for maximize event
|
electron.ipcMain.on("get-gpu-mode", (event) => {
|
||||||
if (this.win.isMaximized()) {
|
event.returnValue = process.platform;
|
||||||
this.win.unmaximize()
|
});
|
||||||
} else {
|
|
||||||
this.win.maximize()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
electron.ipcMain.on('minimize', () => { // listen for minimize event
|
electron.ipcMain.on("is-dev", (event) => {
|
||||||
|
event.returnValue = this.devMode;
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("close", () => {
|
||||||
|
// listen for close event
|
||||||
|
this.win.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("put-library-songs", (event, arg) => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-songs.json"),
|
||||||
|
JSON.stringify(arg)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("put-library-artists", (event, arg) => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-artists.json"),
|
||||||
|
JSON.stringify(arg)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("put-library-albums", (event, arg) => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-albums.json"),
|
||||||
|
JSON.stringify(arg)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("put-library-playlists", (event, arg) => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-playlists.json"),
|
||||||
|
JSON.stringify(arg)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("put-library-recentlyAdded", (event, arg) => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-recentlyAdded.json"),
|
||||||
|
JSON.stringify(arg)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-library-songs", (event) => {
|
||||||
|
let librarySongs = fs.readFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-songs.json"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
event.returnValue = JSON.parse(librarySongs);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-library-artists", (event) => {
|
||||||
|
let libraryArtists = fs.readFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-artists.json"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
event.returnValue = JSON.parse(libraryArtists);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-library-albums", (event) => {
|
||||||
|
let libraryAlbums = fs.readFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-albums.json"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
event.returnValue = JSON.parse(libraryAlbums);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-library-playlists", (event) => {
|
||||||
|
let libraryPlaylists = fs.readFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-playlists.json"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
event.returnValue = JSON.parse(libraryPlaylists);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("get-library-recentlyAdded", (event) => {
|
||||||
|
let libraryRecentlyAdded = fs.readFileSync(
|
||||||
|
path.join(this.paths.ciderCache, "library-recentlyAdded.json"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
event.returnValue = JSON.parse(libraryRecentlyAdded);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.handle("getYTLyrics", async (event, track, artist) => {
|
||||||
|
const u = track + " " + artist + " official video";
|
||||||
|
return await yt.search(u);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.handle("setVibrancy", (event, key, value) => {
|
||||||
|
this.win.setVibrancy(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("maximize", () => {
|
||||||
|
// listen for maximize event
|
||||||
|
if (this.win.isMaximized()) {
|
||||||
|
this.win.unmaximize();
|
||||||
|
} else {
|
||||||
|
this.win.maximize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("minimize", () => {
|
||||||
|
// listen for minimize event
|
||||||
this.win.minimize();
|
this.win.minimize();
|
||||||
})
|
});
|
||||||
|
|
||||||
// Set scale
|
// Set scale
|
||||||
electron.ipcMain.on('setScreenScale', (event, scale) => {
|
electron.ipcMain.on("setScreenScale", (event, scale) => {
|
||||||
this.win.webContents.setZoomFactor(parseFloat(scale))
|
this.win.webContents.setZoomFactor(parseFloat(scale));
|
||||||
})
|
});
|
||||||
|
|
||||||
/* *********************************************************************************************
|
/* *********************************************************************************************
|
||||||
* Window Events
|
* Window Events
|
||||||
* **********************************************************************************************/
|
* **********************************************************************************************/
|
||||||
|
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
let WND_STATE = {
|
let WND_STATE = {
|
||||||
MINIMIZED: 0,
|
MINIMIZED: 0,
|
||||||
NORMAL: 1,
|
NORMAL: 1,
|
||||||
MAXIMIZED: 2,
|
MAXIMIZED: 2,
|
||||||
FULL_SCREEN: 3
|
FULL_SCREEN: 3,
|
||||||
}
|
};
|
||||||
let wndState = WND_STATE.NORMAL
|
let wndState = WND_STATE.NORMAL;
|
||||||
|
|
||||||
this.win.on("resize", (_: any) => {
|
this.win.on("resize", (_: any) => {
|
||||||
const isMaximized = this.win.isMaximized()
|
const isMaximized = this.win.isMaximized();
|
||||||
const isMinimized = this.win.isMinimized()
|
const isMinimized = this.win.isMinimized();
|
||||||
const isFullScreen = this.win.isFullScreen()
|
const isFullScreen = this.win.isFullScreen();
|
||||||
const state = wndState;
|
const state = wndState;
|
||||||
if (isMinimized && state !== WND_STATE.MINIMIZED) {
|
if (isMinimized && state !== WND_STATE.MINIMIZED) {
|
||||||
wndState = WND_STATE.MINIMIZED
|
wndState = WND_STATE.MINIMIZED;
|
||||||
} else if (isFullScreen && state !== WND_STATE.FULL_SCREEN) {
|
} else if (isFullScreen && state !== WND_STATE.FULL_SCREEN) {
|
||||||
wndState = WND_STATE.FULL_SCREEN
|
wndState = WND_STATE.FULL_SCREEN;
|
||||||
} else if (isMaximized && state !== WND_STATE.MAXIMIZED) {
|
} else if (isMaximized && state !== WND_STATE.MAXIMIZED) {
|
||||||
wndState = WND_STATE.MAXIMIZED
|
wndState = WND_STATE.MAXIMIZED;
|
||||||
this.win.webContents.executeJavaScript(`app.chrome.maximized = true`)
|
this.win.webContents.executeJavaScript(`app.chrome.maximized = true`);
|
||||||
} else if (state !== WND_STATE.NORMAL) {
|
} else if (state !== WND_STATE.NORMAL) {
|
||||||
wndState = WND_STATE.NORMAL
|
wndState = WND_STATE.NORMAL;
|
||||||
this.win.webContents.executeJavaScript(`app.chrome.maximized = false`)
|
this.win.webContents.executeJavaScript(
|
||||||
|
`app.chrome.maximized = false`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.win.on("closed", () => {
|
this.win.on("closed", () => {
|
||||||
this.win = null
|
this.win = null;
|
||||||
})
|
});
|
||||||
|
|
||||||
// Set window Handler
|
// Set window Handler
|
||||||
this.win.webContents.setWindowOpenHandler((x: any) => {
|
this.win.webContents.setWindowOpenHandler((x: any) => {
|
||||||
if (x.url.includes("apple") || x.url.includes("localhost")) {
|
if (x.url.includes("apple") || x.url.includes("localhost")) {
|
||||||
return {action: "allow"}
|
return { action: "allow" };
|
||||||
}
|
}
|
||||||
electron.shell.openExternal(x.url).catch(console.error)
|
electron.shell.openExternal(x.url).catch(console.error);
|
||||||
return {action: 'deny'}
|
return { action: "deny" };
|
||||||
})
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,9 +14,9 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body oncontextmenu="return false;">
|
<body oncontextmenu="return false;">
|
||||||
<div id="app" :style="{'--artwork': getAlbumArtUrl()}">
|
<div id="app" :style="{'--artwork': getAlbumArtUrl()}">
|
||||||
<!-- App view when connected -->
|
<!-- App view when connected -->
|
||||||
<template v-if="connectedState == 1">
|
<template v-if="connectedState == 1">
|
||||||
<!-- Streamer Overlay -->
|
<!-- Streamer Overlay -->
|
||||||
<template></template>
|
<template></template>
|
||||||
<!-- Mini Player -->
|
<!-- Mini Player -->
|
||||||
|
@ -52,8 +52,7 @@
|
||||||
<div class="md-container md-container_panel player-panel" v-if="screen == 'player'">
|
<div class="md-container md-container_panel player-panel" v-if="screen == 'player'">
|
||||||
<div class="player_top">
|
<div class="player_top">
|
||||||
<div class="md-body player-artwork-container">
|
<div class="md-body player-artwork-container">
|
||||||
<div class="media-artwork" :class="artworkPlaying()"
|
<div class="media-artwork" :class="artworkPlaying()" :style="{'--artwork': getAlbumArtUrl()}">
|
||||||
:style="{'--artwork': getAlbumArtUrl()}">
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -85,7 +84,7 @@
|
||||||
{{ lyric.line }}
|
{{ lyric.line }}
|
||||||
</h3>
|
</h3>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<h3 class="lyric-line" @click="seekTo(lyric.startTime, false)"
|
<h3 class="lyric-line" @click="seekTo(lyric.startTime, false)"
|
||||||
:class="getLyricClass(lyric.startTime, lyric.endTime)">
|
:class="getLyricClass(lyric.startTime, lyric.endTime)">
|
||||||
<div class="lyricWaiting">
|
<div class="lyricWaiting">
|
||||||
|
@ -101,8 +100,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer">
|
<div class="md-footer">
|
||||||
<button class="md-btn playback-button--small lyrics active"
|
<button class="md-btn playback-button--small lyrics active" @click="player.lowerPanelState = 'controls'"></button>
|
||||||
@click="player.lowerPanelState = 'controls'"></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="player_bottom" v-if="player.lowerPanelState == 'controls'">
|
<div class="player_bottom" v-if="player.lowerPanelState == 'controls'">
|
||||||
|
@ -122,14 +120,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer">
|
<div class="md-footer">
|
||||||
<input type="range" min="0"
|
<input type="range" min="0" :value="player.currentMediaItem.durationInMillis - player.currentMediaItem.remainingTime" :max="player.currentMediaItem.durationInMillis" class="web-slider playback-slider" @input="seekTo($event.target.value)">
|
||||||
:value="player.currentMediaItem.durationInMillis - player.currentMediaItem.remainingTime"
|
|
||||||
:max="player.currentMediaItem.durationInMillis" class="web-slider playback-slider"
|
|
||||||
@input="seekTo($event.target.value)">
|
|
||||||
<div class="row nopadding player-duration-container" style="width: 90%;margin: 0 auto;">
|
<div class="row nopadding player-duration-container" style="width: 90%;margin: 0 auto;">
|
||||||
<div class="col nopadding player-duration-time" style="text-align:left">
|
<div class="col nopadding player-duration-time" style="text-align:left">
|
||||||
{{ parseTime(player.currentMediaItem.durationInMillis -
|
{{ parseTime(player.currentMediaItem.durationInMillis - player.currentMediaItem.remainingTime) }}
|
||||||
player.currentMediaItem.remainingTime) }}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col nopadding player-duration-time" style="text-align:right">
|
<div class="col nopadding player-duration-time" style="text-align:right">
|
||||||
-{{ parseTime(player.currentMediaItem.remainingTime) }}
|
-{{ parseTime(player.currentMediaItem.remainingTime) }}
|
||||||
|
@ -137,21 +131,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer playback-buttons">
|
<div class="md-footer playback-buttons">
|
||||||
<button class="md-btn playback-button--small repeat" @click="repeat()"
|
<button class="md-btn playback-button--small repeat" @click="repeat()" v-if="player.currentMediaItem.repeatMode == 0"></button>
|
||||||
v-if="player.currentMediaItem.repeatMode == 0"></button>
|
<button class="md-btn playback-button--small repeat active" @click="repeat()" v-else-if="player.currentMediaItem.repeatMode == 2"></button>
|
||||||
<button class="md-btn playback-button--small repeat active" @click="repeat()"
|
<button class="md-btn playback-button--small repeat repeatOne" @click="repeat()" v-else-if="player.currentMediaItem.repeatMode == 1"></button>
|
||||||
v-else-if="player.currentMediaItem.repeatMode == 2"></button>
|
|
||||||
<button class="md-btn playback-button--small repeat repeatOne" @click="repeat()"
|
|
||||||
v-else-if="player.currentMediaItem.repeatMode == 1"></button>
|
|
||||||
<button class="md-btn playback-button previous" @click="previous()"></button>
|
<button class="md-btn playback-button previous" @click="previous()"></button>
|
||||||
<button class="md-btn playback-button pause" @click="pause()"
|
<button class="md-btn playback-button pause" @click="pause()" v-if="player.currentMediaItem.status"></button>
|
||||||
v-if="player.currentMediaItem.status"></button>
|
|
||||||
<button class="md-btn playback-button play" @click="play()" v-else></button>
|
<button class="md-btn playback-button play" @click="play()" v-else></button>
|
||||||
<button class="md-btn playback-button next" @click="next()"></button>
|
<button class="md-btn playback-button next" @click="next()"></button>
|
||||||
<button class="md-btn playback-button--small shuffle" @click="shuffle()"
|
<button class="md-btn playback-button--small shuffle" @click="shuffle()" v-if="player.currentMediaItem.shuffleMode == 0"></button>
|
||||||
v-if="player.currentMediaItem.shuffleMode == 0"></button>
|
<button class="md-btn playback-button--small shuffle active" @click="shuffle()" v-else></button>
|
||||||
<button class="md-btn playback-button--small shuffle active" @click="shuffle()"
|
|
||||||
v-else></button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer">
|
<div class="md-footer">
|
||||||
<div class="row volume-slider-container">
|
<div class="row volume-slider-container">
|
||||||
|
@ -159,8 +147,7 @@
|
||||||
<div class="player-volume-glyph decrease"></div>
|
<div class="player-volume-glyph decrease"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input type="range" class="web-slider volume-slider" max="1" min="0" step="0.01"
|
<input type="range" class="web-slider volume-slider" max="1" min="0" step="0.01" @input="setVolume($event.target.value)" :value="player.currentMediaItem.volume">
|
||||||
@input="setVolume($event.target.value)" :value="player.currentMediaItem.volume">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="player-volume-glyph increase"></div>
|
<div class="player-volume-glyph increase"></div>
|
||||||
|
@ -170,10 +157,8 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer">
|
<div class="md-footer">
|
||||||
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'portrait'"
|
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'portrait'" @click="showLyrics()"></button>
|
||||||
@click="showLyrics()"></button>
|
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'landscape'" @click="showLyricsInline()"></button>
|
||||||
<button class="md-btn playback-button--small lyrics"
|
|
||||||
v-if="checkOrientation() == 'landscape'" @click="showLyricsInline()"></button>
|
|
||||||
<button class="md-btn playback-button--small queue" @click="showQueue()"></button>
|
<button class="md-btn playback-button--small queue" @click="showQueue()"></button>
|
||||||
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -191,43 +176,34 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col" style="display: flex;align-items: center;">
|
<div class="col" style="display: flex;align-items: center;">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input type="text" placeholder="Artists, Songs, Lyrics, and More"
|
<input type="text" placeholder="Artists, Songs, Lyrics, and More" spellcheck="false" v-model="search.query" @change="searchQuery()" v-on:keyup.enter="searchQuery()" class="search-input">
|
||||||
spellcheck="false" v-model="search.query" @change="searchQuery()"
|
|
||||||
v-on:keyup.enter="searchQuery()" class="search-input">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-header search-type-container">
|
<div class="md-header search-type-container">
|
||||||
<button class="search-type-button" @click="search.searchType = 'applemusic';searchQuery()"
|
<button class="search-type-button" @click="search.searchType = 'applemusic';searchQuery()" :class="searchTypeClass('applemusic')" style="width:100%;">Apple Music
|
||||||
:class="searchTypeClass('applemusic')" style="width:100%;">Apple Music
|
|
||||||
</button>
|
</button>
|
||||||
<button class="search-type-button" @click="search.searchType = 'library';searchQuery()"
|
<button class="search-type-button" @click="search.searchType = 'library';searchQuery()" :class="searchTypeClass('library')" style="width:100%;">Library
|
||||||
:class="searchTypeClass('library')" style="width:100%;">Library
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-header search-tab-container" v-if="search.state == 2">
|
<div class="md-header search-tab-container" v-if="search.state == 2">
|
||||||
<button class="search-tab" @click="search.tab = 'all'" :class="searchTabClass('all')">All
|
<button class="search-tab" @click="search.tab = 'all'" :class="searchTabClass('all')">All
|
||||||
Results
|
Results
|
||||||
</button>
|
</button>
|
||||||
<button class="search-tab" @click="search.tab = 'songs'"
|
<button class="search-tab" @click="search.tab = 'songs'" :class="searchTabClass('songs')">Songs
|
||||||
:class="searchTabClass('songs')">Songs
|
|
||||||
</button>
|
</button>
|
||||||
<button class="search-tab" @click="search.tab = 'albums'"
|
<button class="search-tab" @click="search.tab = 'albums'" :class="searchTabClass('albums')">Albums
|
||||||
:class="searchTabClass('albums')">Albums
|
|
||||||
</button>
|
</button>
|
||||||
<button class="search-tab" @click="search.tab = 'artists'"
|
<button class="search-tab" @click="search.tab = 'artists'" :class="searchTabClass('artists')">Artists
|
||||||
:class="searchTabClass('artists')">Artists
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="search-body-container">
|
<div class="search-body-container">
|
||||||
<transition name="wpfade">
|
<transition name="wpfade">
|
||||||
<div class="md-body search-body" v-if="search.state == 0">
|
<div class="md-body search-body" v-if="search.state == 0">
|
||||||
<div
|
<div style="font-size: 17px;display:flex;flex-direction: column;justify-content: center;align-items: center;">
|
||||||
style="font-size: 17px;display:flex;flex-direction: column;justify-content: center;align-items: center;">
|
<img src="./assets/search.svg" style="width: 40px;margin: 32px;opacity: 0.85"> Search by song, album, artist, or lyrics.
|
||||||
<img src="./assets/search.svg" style="width: 40px;margin: 32px;opacity: 0.85">
|
|
||||||
Search by song, album, artist, or lyrics.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
@ -237,10 +213,7 @@
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="wpfade">
|
<transition name="wpfade">
|
||||||
<div class="md-body search-body"
|
<div class="md-body search-body" ref="searchBody" @scroll="searchScroll" style="overflow-y:auto;" v-if="search.state == 2">
|
||||||
ref="searchBody"
|
|
||||||
@scroll="searchScroll"
|
|
||||||
style="overflow-y:auto;" v-if="search.state == 2">
|
|
||||||
<template v-if="canShowSearchTab('songs')">
|
<template v-if="canShowSearchTab('songs')">
|
||||||
<div class="list-entry-header">Songs</div>
|
<div class="list-entry-header">Songs</div>
|
||||||
|
|
||||||
|
@ -377,11 +350,9 @@
|
||||||
</transition>
|
</transition>
|
||||||
<!-- Track Select Actions -->
|
<!-- Track Select Actions -->
|
||||||
<transition name="wpfade">
|
<transition name="wpfade">
|
||||||
<div class="md-container md-container_panel context-menu" style="overflow-y:auto;"
|
<div class="md-container md-container_panel context-menu" style="overflow-y:auto;" v-if="search.trackSelect">
|
||||||
v-if="search.trackSelect">
|
|
||||||
<div class="md-body context-menu-body">
|
<div class="md-body context-menu-body">
|
||||||
<button class="context-menu-item context-menu-item--left"
|
<button class="context-menu-item context-menu-item--left" @click="playMediaItemById(search.selected.id);clearSelectedTrack()">
|
||||||
@click="playMediaItemById(search.selected.id);clearSelectedTrack()">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-auto flex-center" v-if="search.selected.artwork"
|
<div class="col-auto flex-center" v-if="search.selected.artwork"
|
||||||
style="display:flex;align-items: center;">
|
style="display:flex;align-items: center;">
|
||||||
|
@ -404,8 +375,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body context-menu-body" style="height: auto;">
|
<div class="md-body context-menu-body" style="height: auto;">
|
||||||
<button class="context-menu-item context-menu-item--left"
|
<button class="context-menu-item context-menu-item--left" @click="playMediaItemById(search.selected.id);clearSelectedTrack()">
|
||||||
@click="playMediaItemById(search.selected.id);clearSelectedTrack()">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
Play
|
Play
|
||||||
|
@ -415,8 +385,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button class="context-menu-item context-menu-item--left"
|
<button class="context-menu-item context-menu-item--left" @click="playNext('song', search.selected.id);clearSelectedTrack()">
|
||||||
@click="playNext('song', search.selected.id);clearSelectedTrack()">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
Play Next
|
Play Next
|
||||||
|
@ -426,8 +395,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button class="context-menu-item context-menu-item--left"
|
<button class="context-menu-item context-menu-item--left" @click="playLater('song', search.selected.id);clearSelectedTrack()">
|
||||||
@click="playLater('song', search.selected.id);clearSelectedTrack()">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
Play Later
|
Play Later
|
||||||
|
@ -520,24 +488,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="album-body-container" :style="getMediaPalette(artistPage.data)">
|
<div class="album-body-container" :style="getMediaPalette(artistPage.data)">
|
||||||
<div class="artist-header" v-if="artistPage.data['artwork']"
|
<div class="artist-header" v-if="artistPage.data['artwork']" :style="getMediaPalette(artistPage.data)">
|
||||||
:style="getMediaPalette(artistPage.data)">
|
<div class="artist-header-portrait" :style="{'--artwork': getAlbumArtUrlList(artistPage.data['artwork']['url'], 600)}"></div>
|
||||||
<div class="artist-header-portrait"
|
|
||||||
:style="{'--artwork': getAlbumArtUrlList(artistPage.data['artwork']['url'], 600)}"></div>
|
|
||||||
<h2>{{ artistPage.data["name"] }}</h2>
|
<h2>{{ artistPage.data["name"] }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body artist-body">
|
<div class="md-body artist-body">
|
||||||
<h2>Songs</h2>
|
<h2>Songs</h2>
|
||||||
<div class="song-scroller-horizontal">
|
<div class="song-scroller-horizontal">
|
||||||
<button v-for="song in artistPage.data['songs']" class="song-placeholder"
|
<button v-for="song in artistPage.data['songs']" class="song-placeholder" @click="trackSelect(song)">
|
||||||
@click="trackSelect(song)">
|
|
||||||
{{ song.name }}
|
{{ song.name }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h2>Albums</h2>
|
<h2>Albums</h2>
|
||||||
<div class="mediaitem-scroller-horizontal">
|
<div class="mediaitem-scroller-horizontal">
|
||||||
<button v-for="album in artistPage.data['albums']" class="album-placeholder"
|
<button v-for="album in artistPage.data['albums']" class="album-placeholder" @click="showAlbum(album.id)">
|
||||||
@click="showAlbum(album.id)">
|
|
||||||
{{ album.name }}
|
{{ album.name }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -574,30 +538,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-header" style="text-align: right;padding: 5px 16px;">
|
<div class="md-header" style="text-align: right;padding: 5px 16px;">
|
||||||
<button
|
<button class="md-btn playback-button--small autoplay" v-if="!player.currentMediaItem.autoplayEnabled" @click="setAutoplay(true)"></button>
|
||||||
class="md-btn playback-button--small autoplay"
|
<button class="md-btn playback-button--small autoplay activeColor" v-else @click="setAutoplay(false)"></button>
|
||||||
v-if="!player.currentMediaItem.autoplayEnabled"
|
|
||||||
@click="setAutoplay(true)"
|
|
||||||
></button>
|
|
||||||
<button
|
|
||||||
class="md-btn playback-button--small autoplay activeColor"
|
|
||||||
v-else
|
|
||||||
@click="setAutoplay(false)"
|
|
||||||
></button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body queue-body" v-if="!player.queue['_queueItems']">
|
<div class="md-body queue-body" v-if="!player.queue['_queueItems']">
|
||||||
Empty
|
Empty
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body queue-body" style="overflow-y:auto;" id="list-queue" v-else>
|
<div class="md-body queue-body" style="overflow-y:auto;" id="list-queue" v-else>
|
||||||
<draggable
|
<draggable v-model="queue.temp" handle=".handle" filter=".passed" @change="queueMove">
|
||||||
v-model="queue.temp"
|
<template v-for="(song, position) in queue.temp" v-if="position > player.queue['_position']">
|
||||||
handle=".handle"
|
|
||||||
filter=".passed"
|
|
||||||
@change="queueMove">
|
|
||||||
<template
|
|
||||||
v-for="(song, position) in queue.temp"
|
|
||||||
v-if="position > player.queue['_position']"
|
|
||||||
>
|
|
||||||
<div class="list-entry" :class="getQueuePositionClass(position)">
|
<div class="list-entry" :class="getQueuePositionClass(position)">
|
||||||
<div class="row" style="width:100%;">
|
<div class="row" style="width:100%;">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
|
@ -626,11 +575,8 @@
|
||||||
</draggable>
|
</draggable>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer">
|
<div class="md-footer">
|
||||||
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'portrait'"
|
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'portrait'" @click="showLyrics()"></button>
|
||||||
@click="showLyrics()"></button>
|
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'landscape'" @click="screen = 'player';showLyricsInline()"></button>
|
||||||
<button class="md-btn playback-button--small lyrics"
|
|
||||||
v-if="checkOrientation() == 'landscape'"
|
|
||||||
@click="screen = 'player';showLyricsInline()"></button>
|
|
||||||
<button class="md-btn playback-button--small queue active" @click="screen = 'player'"></button>
|
<button class="md-btn playback-button--small queue active" @click="screen = 'player'"></button>
|
||||||
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -665,7 +611,7 @@
|
||||||
{{ lyric.line }}
|
{{ lyric.line }}
|
||||||
</h3>
|
</h3>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<h3 class="lyric-line" @click="seekTo(lyric.startTime, false)"
|
<h3 class="lyric-line" @click="seekTo(lyric.startTime, false)"
|
||||||
:class="getLyricClass(lyric.startTime, lyric.endTime)">
|
:class="getLyricClass(lyric.startTime, lyric.endTime)">
|
||||||
<div class="lyricWaiting">
|
<div class="lyricWaiting">
|
||||||
|
@ -689,9 +635,7 @@
|
||||||
</transition>
|
</transition>
|
||||||
<!-- Album Page -->
|
<!-- Album Page -->
|
||||||
<transition name="wpfade">
|
<transition name="wpfade">
|
||||||
<div class="md-container md-container_panel md-container_album"
|
<div class="md-container md-container_panel md-container_album" v-if="screen == 'album-page' && albumPage.data['name']">
|
||||||
v-if="screen == 'album-page' && albumPage.data['name']"
|
|
||||||
>
|
|
||||||
<div class="md-header">
|
<div class="md-header">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
|
@ -701,8 +645,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="album-body-container">
|
<div class="album-body-container">
|
||||||
<div class="md-header">
|
<div class="md-header">
|
||||||
<div class="albumpage-artwork"
|
<div class="albumpage-artwork" :style="{'--artwork': getAlbumArtUrlList(albumPage.data['artwork']['url'], 300)}">
|
||||||
:style="{'--artwork': getAlbumArtUrlList(albumPage.data['artwork']['url'], 300)}">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="albumpage-album-name">
|
<div class="albumpage-album-name">
|
||||||
{{ albumPage.data["name"] }}
|
{{ albumPage.data["name"] }}
|
||||||
|
@ -711,20 +654,15 @@
|
||||||
{{ albumPage.data["artistName"] }}
|
{{ albumPage.data["artistName"] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="albumpage-misc-info">
|
<div class="albumpage-misc-info">
|
||||||
{{ albumPage.data.genreNames[0] }} ∙ {{ new Date(albumPage.data.releaseDate).getFullYear()
|
{{ albumPage.data.genreNames[0] }} ∙ {{ new Date(albumPage.data.releaseDate).getFullYear() }}
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin-top: 20px;">
|
<div class="row" style="margin-top: 20px;">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<button class="wr-btn"
|
<button class="wr-btn" @click="playAlbum(albumPage.data.id, false)" style="width:100%;">Play
|
||||||
@click="playAlbum(albumPage.data.id, false)"
|
|
||||||
style="width:100%;">Play
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<button class="wr-btn" style="width:100%;"
|
<button class="wr-btn" style="width:100%;" @click="playAlbum(albumPage.data.id, true)">Shuffle
|
||||||
@click="playAlbum(albumPage.data.id, true)"
|
|
||||||
>Shuffle
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -737,12 +675,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body artist-body">
|
<div class="md-body artist-body">
|
||||||
<div class="list-entry-header">Tracks</div>
|
<div class="list-entry-header">Tracks</div>
|
||||||
<div class="list-entry" v-for="song in albumPage.data['tracks']"
|
<div class="list-entry" v-for="song in albumPage.data['tracks']" @click="trackSelect(song)">
|
||||||
@click="trackSelect(song)">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-auto flex-center">
|
<div class="col-auto flex-center">
|
||||||
<div class="list-entry-image" v-if="song.artwork"
|
<div class="list-entry-image" v-if="song.artwork" :style="{'--artwork': getAlbumArtUrlList(song.artwork.url)}">
|
||||||
:style="{'--artwork': getAlbumArtUrlList(song.artwork.url)}">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col flex-center">
|
<div class="col flex-center">
|
||||||
|
@ -768,92 +704,79 @@
|
||||||
</transition>
|
</transition>
|
||||||
<!-- Album Page - Editorial Notes -->
|
<!-- Album Page - Editorial Notes -->
|
||||||
<transition name="wpfade">
|
<transition name="wpfade">
|
||||||
<div class="md-container md-container_panel context-menu" v-if="albumPage.editorsNotes"
|
<div class="md-container md-container_panel context-menu" v-if="albumPage.editorsNotes" style="padding-top: 42px;">
|
||||||
style="padding-top: 42px;">
|
<div class="md-header" :style="getMediaPalette(albumPage.data)" style="font-size: 18px;background:var(--bgColor);color:var(--textColor1);text-align: center;border-radius: 10px 10px 0 0;border-top: 1px solid #ffffff1f;">
|
||||||
<div class="md-header"
|
|
||||||
:style="getMediaPalette(albumPage.data)"
|
|
||||||
style="font-size: 18px;background:var(--bgColor);color:var(--textColor1);text-align: center;border-radius: 10px 10px 0 0;border-top: 1px solid #ffffff1f;"
|
|
||||||
>
|
|
||||||
{{ albumPage.data["name"] }}
|
{{ albumPage.data["name"] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body album-page-fullnotes-body"
|
<div class="md-body album-page-fullnotes-body" :style="getMediaPalette(albumPage.data)" style="background:var(--bgColor);color:var(--textColor1);" v-html="albumPage.data['editorialNotes']['standard']">
|
||||||
:style="getMediaPalette(albumPage.data)"
|
|
||||||
style="background:var(--bgColor);color:var(--textColor1);"
|
|
||||||
v-html="albumPage.data['editorialNotes']['standard']">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer"
|
<div class="md-footer" :style="getMediaPalette(albumPage.data)" style="background:var(--bgColor);color:var(--textColor1);">
|
||||||
:style="getMediaPalette(albumPage.data)"
|
|
||||||
style="background:var(--bgColor);color:var(--textColor1);"
|
|
||||||
>
|
|
||||||
<button class="context-menu-item" @click="albumPage.editorsNotes = false">Close</button>
|
<button class="context-menu-item" @click="albumPage.editorsNotes = false">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
<!-- Loading -->
|
<!-- Loading -->
|
||||||
<transition name="wpfade">
|
<transition name="wpfade">
|
||||||
<div class="md-container md-container_panel connection-error-panel" v-if="connectedState != 1">
|
<div class="md-container md-container_panel connection-error-panel" v-if="connectedState != 1">
|
||||||
<div class="md-header">
|
<div class="md-header">
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="md-body" style="display:flex;justify-content: center;align-items: center;">
|
|
||||||
<div v-if="connectedState == 0">
|
|
||||||
Loading...
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div class="md-body" style="display:flex;justify-content: center;align-items: center;">
|
||||||
<h3 style="text-align:center;">Connection Interrupted</h3>
|
<div v-if="connectedState == 0">
|
||||||
<button class="md-btn md-btn-primary"
|
Loading...
|
||||||
style="font-weight:500;width: 120px;border-radius: 50px;display:block;margin: 0 auto;"
|
</div>
|
||||||
@click="connect()">Retry
|
<div v-else>
|
||||||
|
<h3 style="text-align:center;">Connection Interrupted</h3>
|
||||||
|
<button class="md-btn md-btn-primary" style="font-weight:500;width: 120px;border-radius: 50px;display:block;margin: 0 auto;" @click="connect()">Retry
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="md-footer">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md-footer">
|
</transition>
|
||||||
|
<!-- Template -->
|
||||||
|
<transition name="wpfade">
|
||||||
|
<div class="md-container md-container_panel" v-if="false">
|
||||||
|
<div class="md-header">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="md-body">
|
||||||
</transition>
|
|
||||||
<!-- Template -->
|
|
||||||
<transition name="wpfade">
|
|
||||||
<div class="md-container md-container_panel" v-if="false">
|
|
||||||
<div class="md-header">
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="md-body">
|
<div class="md-footer">
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="md-footer">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/x-template" id="footer-player">
|
|
||||||
<div class="footer-player" v-show="$parent.player.currentMediaItem['name']">
|
|
||||||
<div class="row" style="width:100%;margin:0px;">
|
|
||||||
<div class="col-auto flex-center" style="padding:0 6px;" @click="$parent.screen = 'player'">
|
|
||||||
<div class="list-entry-image" :style="{'--artwork': $parent.getAlbumArtUrl()}">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col flex-center text-overflow-elipsis" @click="$parent.screen = 'player'">
|
</transition>
|
||||||
<div class="list-entry-name text-overflow-elipsis">
|
|
||||||
{{ $parent.player.currentMediaItem.name }}
|
|
||||||
</div>
|
|
||||||
<div class="list-entry-artist text-overflow-elipsis">
|
|
||||||
{{ $parent.player.currentMediaItem.artistName }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<button class="md-btn playback-button pause" @click="$parent.pause()"
|
|
||||||
v-if="$parent.player.currentMediaItem.status"></button>
|
|
||||||
<button class="md-btn playback-button play" @click="$parent.play()" v-else></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="index.js?v=1"></script>
|
<script type="text/x-template" id="footer-player">
|
||||||
|
<div class="footer-player" v-show="$parent.player.currentMediaItem['name']">
|
||||||
|
<div class="row" style="width:100%;margin:0px;">
|
||||||
|
<div class="col-auto flex-center" style="padding:0 6px;" @click="$parent.screen = 'player'">
|
||||||
|
<div class="list-entry-image" :style="{'--artwork': $parent.getAlbumArtUrl()}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col flex-center text-overflow-elipsis" @click="$parent.screen = 'player'">
|
||||||
|
<div class="list-entry-name text-overflow-elipsis">
|
||||||
|
{{ $parent.player.currentMediaItem.name }}
|
||||||
|
</div>
|
||||||
|
<div class="list-entry-artist text-overflow-elipsis">
|
||||||
|
{{ $parent.player.currentMediaItem.artistName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button class="md-btn playback-button pause" @click="$parent.pause()" v-if="$parent.player.currentMediaItem.status"></button>
|
||||||
|
<button class="md-btn playback-button play" @click="$parent.play()" v-else></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="./index.js?v=1"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue