Merge branch 'upcoming' into upcoming
This commit is contained in:
commit
7bc77b2b1d
26 changed files with 1390 additions and 353 deletions
|
@ -6,7 +6,7 @@
|
||||||
"description": "A new look into listening and enjoying music in style and performance.",
|
"description": "A new look into listening and enjoying music in style and performance.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
"author": "Cider Collective <cryptofyre@cryptofyre.org> (https://cider.sh)",
|
"author": "Cider Collective <cryptofyre@cider.sh> (https://cider.sh)",
|
||||||
"repository": "https://github.com/ciderapp/Cider.git",
|
"repository": "https://github.com/ciderapp/Cider.git",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/ciderapp/Cider/issues?q=is%3Aopen+is%3Aissue+label%3Abug"
|
"url": "https://github.com/ciderapp/Cider/issues?q=is%3Aopen+is%3Aissue+label%3Abug"
|
||||||
|
@ -45,11 +45,12 @@
|
||||||
"run-script-os": "^1.1.6",
|
"run-script-os": "^1.1.6",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"v8-compile-cache": "^2.3.0",
|
"v8-compile-cache": "^2.3.0",
|
||||||
"ws": "^8.4.0",
|
"ws": "^8.4.2",
|
||||||
"xml2js": "^0.4.23",
|
"xml2js": "^0.4.23",
|
||||||
"youtube-search-without-api-key": "^1.0.7"
|
"youtube-search-without-api-key": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/discord-rpc": "^4.0.0",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"electron": "https://github.com/castlabs/electron-releases.git",
|
"electron": "https://github.com/castlabs/electron-releases.git",
|
||||||
"electron-builder": "^22.14.5",
|
"electron-builder": "^22.14.5",
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default class PluginHandler {
|
||||||
if (plugins[file] || plugin.name in plugins) {
|
if (plugins[file] || plugin.name in plugins) {
|
||||||
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
|
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
|
||||||
} else {
|
} else {
|
||||||
plugins[file] = new plugin();
|
plugins[file] = new plugin(electron.app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -33,16 +33,16 @@ export default class PluginHandler {
|
||||||
if (fs.existsSync(this.userPluginsPath)) {
|
if (fs.existsSync(this.userPluginsPath)) {
|
||||||
fs.readdirSync(this.userPluginsPath).forEach(file => {
|
fs.readdirSync(this.userPluginsPath).forEach(file => {
|
||||||
if (file.endsWith('.ts') || file.endsWith('.js')) {
|
if (file.endsWith('.ts') || file.endsWith('.js')) {
|
||||||
const plugin = require(path.join(this.userPluginsPath, file));
|
const plugin = require(path.join(this.userPluginsPath, file)).default;
|
||||||
if (plugins[file] || plugin in plugins) {
|
if (plugins[file] || plugin in plugins) {
|
||||||
console.log(`[${plugin.default}] Plugin already loaded / Duplicate Class Name`);
|
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
|
||||||
} else {
|
} else {
|
||||||
plugins[file] = new plugin.default();
|
plugins[file] = new plugin(electron.app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log('loaded plugins:', JSON.stringify(plugins))
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,4 +54,4 @@ export default class PluginHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import * as fs from "fs";
|
||||||
import { Stream } from "stream";
|
import { Stream } from "stream";
|
||||||
import * as qrcode from "qrcode-terminal";
|
import * as qrcode from "qrcode-terminal";
|
||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
|
import {wsapi} from "./wsapi";
|
||||||
|
|
||||||
export class Win {
|
export class Win {
|
||||||
win: any | undefined = null;
|
win: any | undefined = null;
|
||||||
app: any | undefined = null;
|
app: any | undefined = null;
|
||||||
|
@ -83,6 +85,8 @@ export class Win {
|
||||||
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
|
||||||
|
const ws = new wsapi()
|
||||||
|
ws.InitWebSockets()
|
||||||
this.startWebServer();
|
this.startWebServer();
|
||||||
|
|
||||||
this.win = new electron.BrowserWindow(this.options);
|
this.win = new electron.BrowserWindow(this.options);
|
||||||
|
|
284
src/main/base/wsapi.ts
Normal file
284
src/main/base/wsapi.ts
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import * as ws from "ws";
|
||||||
|
import * as http from "http";
|
||||||
|
import * as https from "https";
|
||||||
|
import * as url from "url";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import * as electron from "electron";
|
||||||
|
const WebSocket = ws;
|
||||||
|
const WebSocketServer = ws.Server;
|
||||||
|
|
||||||
|
private class standardResponse {
|
||||||
|
status: number;
|
||||||
|
message: string;
|
||||||
|
data: any;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class wsapi {
|
||||||
|
port: any = 26369
|
||||||
|
wss: any = null
|
||||||
|
clients: []
|
||||||
|
createId() {
|
||||||
|
// create random guid
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
|
var r = Math.random() * 16 | 0,
|
||||||
|
v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public async InitWebSockets () {
|
||||||
|
electron.ipcMain.on('wsapi-updatePlaybackState', (event, arg) => {
|
||||||
|
wsapi.updatePlaybackState(arg);
|
||||||
|
})
|
||||||
|
|
||||||
|
electron.ipcMain.on('wsapi-returnQueue', (event, arg) => {
|
||||||
|
wsapi.returnQueue(JSON.parse(arg));
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on('wsapi-returnSearch', (event, arg) => {
|
||||||
|
console.log("SEARCH")
|
||||||
|
wsapi.returnSearch(JSON.parse(arg));
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on('wsapi-returnSearchLibrary', (event, arg) => {
|
||||||
|
wsapi.returnSearchLibrary(JSON.parse(arg));
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on('wsapi-returnDynamic', (event, arg, type) => {
|
||||||
|
wsapi.returnDynamic(JSON.parse(arg), type);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on('wsapi-returnMusicKitApi', (event, arg, method) => {
|
||||||
|
wsapi.returnMusicKitApi(JSON.parse(arg), method);
|
||||||
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on('wsapi-returnLyrics', (event, arg) => {
|
||||||
|
wsapi.returnLyrics(JSON.parse(arg));
|
||||||
|
});
|
||||||
|
this.wss = new WebSocketServer({
|
||||||
|
port: this.port,
|
||||||
|
perMessageDeflate: {
|
||||||
|
zlibDeflateOptions: {
|
||||||
|
// See zlib defaults.
|
||||||
|
chunkSize: 1024,
|
||||||
|
memLevel: 7,
|
||||||
|
level: 3
|
||||||
|
},
|
||||||
|
zlibInflateOptions: {
|
||||||
|
chunkSize: 10 * 1024
|
||||||
|
},
|
||||||
|
// Other options settable:
|
||||||
|
clientNoContextTakeover: true, // Defaults to negotiated value.
|
||||||
|
serverNoContextTakeover: true, // Defaults to negotiated value.
|
||||||
|
serverMaxWindowBits: 10, // Defaults to negotiated value.
|
||||||
|
// Below options specified as default values.
|
||||||
|
concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
||||||
|
threshold: 1024 // Size (in bytes) below which messages
|
||||||
|
// should not be compressed if context takeover is disabled.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log(`WebSocketServer started on port: ${this.port}`);
|
||||||
|
|
||||||
|
const defaultResponse = new standardResponse(0, {}, "OK");
|
||||||
|
|
||||||
|
|
||||||
|
this.wss.on('connection', function connection(ws) {
|
||||||
|
ws.id = wsapi.createId();
|
||||||
|
console.log(`Client ${ws.id} connected`)
|
||||||
|
wsapi.clients.push(ws);
|
||||||
|
ws.on('message', function incoming(message) {
|
||||||
|
|
||||||
|
});
|
||||||
|
// ws on message
|
||||||
|
ws.on('message', function incoming(message) {
|
||||||
|
let data = JSON.parse(message);
|
||||||
|
let response = new standardResponse(0, {}, "OK");;
|
||||||
|
if (data.action) {
|
||||||
|
data.action.toLowerCase();
|
||||||
|
}
|
||||||
|
switch (data.action) {
|
||||||
|
default:
|
||||||
|
response.message = "Action not found";
|
||||||
|
break;
|
||||||
|
case "identify":
|
||||||
|
response.message = "Thanks for identifying!"
|
||||||
|
response.data = {
|
||||||
|
id: ws.id
|
||||||
|
}
|
||||||
|
ws.identity = {
|
||||||
|
name: data.name,
|
||||||
|
author: data.author,
|
||||||
|
description: data.description,
|
||||||
|
version: data.version
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "play-next":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.playNext(\`${data.type}\`,\`${data.id}\`)`);
|
||||||
|
response.message = "Play Next";
|
||||||
|
break;
|
||||||
|
case "play-later":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.playLater(\`${data.type}\`,\`${data.id}\`)`);
|
||||||
|
response.message = "Play Later";
|
||||||
|
break;
|
||||||
|
case "quick-play":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.quickPlay(\`${data.term}\`)`);
|
||||||
|
response.message = "Quick Play";
|
||||||
|
break;
|
||||||
|
case "get-lyrics":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.getLyrics()`);
|
||||||
|
break;
|
||||||
|
case "shuffle":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.toggleShuffle()`);
|
||||||
|
break;
|
||||||
|
case "set-shuffle":
|
||||||
|
if(data.shuffle == true) {
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 1`);
|
||||||
|
}else{
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 0`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "repeat":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.toggleRepeat()`);
|
||||||
|
break;
|
||||||
|
case "seek":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().seekToTime(${parseFloat(data.time)})`);
|
||||||
|
response.message = "Seek";
|
||||||
|
break;
|
||||||
|
case "pause":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().pause()`);
|
||||||
|
response.message = "Paused";
|
||||||
|
break;
|
||||||
|
case "play":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().play()`);
|
||||||
|
response.message = "Playing";
|
||||||
|
break;
|
||||||
|
case "stop":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().stop()`);
|
||||||
|
response.message = "Stopped";
|
||||||
|
break;
|
||||||
|
case "volume":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().volume = ${parseFloat(data.volume)}`);
|
||||||
|
response.message = "Volume";
|
||||||
|
break;
|
||||||
|
case "mute":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().mute()`);
|
||||||
|
response.message = "Muted";
|
||||||
|
break;
|
||||||
|
case "unmute":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().unmute()`);
|
||||||
|
response.message = "Unmuted";
|
||||||
|
break;
|
||||||
|
case "next":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().skipToNextItem()`);
|
||||||
|
response.message = "Next";
|
||||||
|
break;
|
||||||
|
case "previous":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`MusicKit.getInstance().skipToPreviousItem()`);
|
||||||
|
response.message = "Previous";
|
||||||
|
break;
|
||||||
|
case "musickit-api":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.musickitApi(\`${data.method}\`, \`${data.id}\`, ${JSON.stringify(data.params)})`);
|
||||||
|
break;
|
||||||
|
case "musickit-library-api":
|
||||||
|
break;
|
||||||
|
case "set-autoplay":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.setAutoplay(${data.autoplay})`);
|
||||||
|
break;
|
||||||
|
case "queue-move":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.moveQueueItem(${data.from},${data.to})`);
|
||||||
|
break;
|
||||||
|
case "get-queue":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.getQueue()`);
|
||||||
|
break;
|
||||||
|
case "search":
|
||||||
|
if (!data.limit) {
|
||||||
|
data.limit = 10;
|
||||||
|
}
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.search(\`${data.term}\`, \`${data.limit}\`)`);
|
||||||
|
break;
|
||||||
|
case "library-search":
|
||||||
|
if (!data.limit) {
|
||||||
|
data.limit = 10;
|
||||||
|
}
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.searchLibrary(\`${data.term}\`, \`${data.limit}\`)`);
|
||||||
|
break;
|
||||||
|
case "show-window":
|
||||||
|
electron.app.win.show()
|
||||||
|
break;
|
||||||
|
case "hide-window":
|
||||||
|
electron.app.win.hide()
|
||||||
|
break;
|
||||||
|
case "play-mediaitem":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.playTrackById(${data.id}, \`${data.kind}\`)`);
|
||||||
|
response.message = "Playing track";
|
||||||
|
break;
|
||||||
|
case "get-status":
|
||||||
|
response.data = {
|
||||||
|
isAuthorized: true
|
||||||
|
};
|
||||||
|
response.message = "Status";
|
||||||
|
break;
|
||||||
|
case "get-currentmediaitem":
|
||||||
|
electron.app.win.webContents.executeJavaScript(`wsapi.getPlaybackState()`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ws.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('close', function close() {
|
||||||
|
// remove client from list
|
||||||
|
wsapi.clients.splice(wsapi.clients.indexOf(ws), 1);
|
||||||
|
console.log(`Client ${ws.id} disconnected`);
|
||||||
|
});
|
||||||
|
ws.send(JSON.stringify(defaultResponse));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sendToClient(id) {
|
||||||
|
// replace the clients.forEach with a filter to find the client that requested
|
||||||
|
}
|
||||||
|
updatePlaybackState(attr) {
|
||||||
|
const response = new standardResponse(0, attr, "OK", "playbackStateUpdate");
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnMusicKitApi(results, method) {
|
||||||
|
const response = new standardResponse(0, results, "OK", `musickitapi.${method}`);
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnDynamic(results, type) {
|
||||||
|
const response = new standardResponse(0, results, "OK", type);
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnLyrics(results) {
|
||||||
|
const response = new standardResponse(0, results, "OK", "lyrics");
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnSearch(results) {
|
||||||
|
const response = new standardResponse(0, results, "OK", "searchResults");
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnSearchLibrary(results) {
|
||||||
|
const response = new standardResponse(0, results, "OK", "searchResultsLibrary");
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
returnQueue(queue) {
|
||||||
|
const response = new standardResponse(0, queue, "OK", "queue");
|
||||||
|
wsapi.clients.forEach(function each(client) {
|
||||||
|
client.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ import {AppEvents} from "./base/app";
|
||||||
import PluginHandler from "./base/plugins";
|
import PluginHandler from "./base/plugins";
|
||||||
|
|
||||||
// const test = new PluginHandler();
|
// const test = new PluginHandler();
|
||||||
|
|
||||||
const config = new ConfigStore();
|
const config = new ConfigStore();
|
||||||
const App = new AppEvents(config.store);
|
const App = new AppEvents(config.store);
|
||||||
const Cider = new Win(electron.app, config.store)
|
const Cider = new Win(electron.app, config.store)
|
||||||
|
@ -23,17 +22,21 @@ const plug = new PluginHandler();
|
||||||
|
|
||||||
electron.app.on('ready', () => {
|
electron.app.on('ready', () => {
|
||||||
App.ready();
|
App.ready();
|
||||||
plug.callPlugins('onReady');
|
|
||||||
|
|
||||||
console.log('[Cider] Application is Ready. Creating Window.')
|
console.log('[Cider] Application is Ready. Creating Window.')
|
||||||
if (!electron.app.isPackaged) {
|
if (!electron.app.isPackaged) {
|
||||||
console.info('[Cider] Running in development mode.')
|
console.info('[Cider] Running in development mode.')
|
||||||
require('vue-devtools').install()
|
require('vue-devtools').install()
|
||||||
}
|
}
|
||||||
electron.components.whenReady().then(() => {
|
|
||||||
Cider.createWindow();
|
electron.components.whenReady().then(async () => {
|
||||||
|
await Cider.createWindow()
|
||||||
|
plug.callPlugins('onReady', Cider);
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -110,4 +113,4 @@ electron.app.on('before-quit', () => {
|
||||||
// console.warn("[InstanceHandler] Existing Instance is Blocking Second Instance.");
|
// console.warn("[InstanceHandler] Existing Instance is Blocking Second Instance.");
|
||||||
// app.quit();
|
// app.quit();
|
||||||
// app.isQuiting = true
|
// app.isQuiting = true
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
let i = 1, k = 1;
|
let i = 1, k = 1;
|
||||||
export default class ExamplePlugin {
|
export default class ExamplePlugin {
|
||||||
|
/**
|
||||||
|
* Private variables for interaction in plugins
|
||||||
|
*/
|
||||||
|
private _win: any;
|
||||||
|
private _app: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
||||||
|
@ -12,14 +17,16 @@ export default class ExamplePlugin {
|
||||||
/**
|
/**
|
||||||
* Runs on plugin load (Currently run on application start)
|
* Runs on plugin load (Currently run on application start)
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor(app: any) {
|
||||||
|
this._app = app;
|
||||||
}
|
console.log('Example plugin loaded');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs on app ready
|
* Runs on app ready
|
||||||
*/
|
*/
|
||||||
onReady(): void {
|
onReady(win: any): void {
|
||||||
|
this._win = win;
|
||||||
console.log('Example plugin ready');
|
console.log('Example plugin ready');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,4 +55,4 @@ export default class ExamplePlugin {
|
||||||
k++
|
k++
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
37
src/main/plugins/Extras/sendSongToTitlebar.ts
Normal file
37
src/main/plugins/Extras/sendSongToTitlebar.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
export default class sendSongToTitlebar {
|
||||||
|
/**
|
||||||
|
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
||||||
|
*/
|
||||||
|
public name: string = 'sendSongToTitlebar';
|
||||||
|
public description: string = 'Sets the app\'s titlebar to the Song title';
|
||||||
|
public version: string = '0.0.1';
|
||||||
|
public author: string = 'Cider Collective (credit to 8times9 via #147)';
|
||||||
|
/**
|
||||||
|
* Runs on plugin load (Currently run on application start)
|
||||||
|
*/
|
||||||
|
private _win: any;
|
||||||
|
private _app: any;
|
||||||
|
constructor() {}
|
||||||
|
/**
|
||||||
|
* Runs on app ready
|
||||||
|
*/
|
||||||
|
onReady(win: any): void {
|
||||||
|
this._win = win;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Runs on app stop
|
||||||
|
*/
|
||||||
|
onBeforeQuit(): void {}
|
||||||
|
/**
|
||||||
|
* Runs on playback State Change
|
||||||
|
* @param attributes Music Attributes (attributes.state = current state)
|
||||||
|
*/
|
||||||
|
onPlaybackStateDidChange(attributes: any): void {
|
||||||
|
this._win.win.setTitle(`${(attributes != null && attributes.name != null && attributes.name.length > 0) ? (attributes.name + " - ") : ''}Cider`)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Runs on song change
|
||||||
|
* @param attributes Music Attributes
|
||||||
|
*/
|
||||||
|
onNowPlayingItemDidChange(attributes: object): void {}
|
||||||
|
}
|
205
src/main/plugins/discordrpc.ts
Normal file
205
src/main/plugins/discordrpc.ts
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import * as DiscordRPC from 'discord-rpc'
|
||||||
|
export default class DiscordRPCPlugin {
|
||||||
|
/**
|
||||||
|
* Private variables for interaction in plugins
|
||||||
|
*/
|
||||||
|
private _win: any;
|
||||||
|
private _app: any;
|
||||||
|
private _discord: any;
|
||||||
|
private connect(clientId: any) {
|
||||||
|
this._discord = { isConnected: false };
|
||||||
|
if (this._win.store.store.general.discord_rpc == 0 || this._discord.isConnected) return;
|
||||||
|
|
||||||
|
DiscordRPC.register(clientId) // Apparently needed for ask to join, join, spectate etc.
|
||||||
|
const client = new DiscordRPC.Client({ transport: "ipc" });
|
||||||
|
this._discord = Object.assign(client, { error: false, activityCache: null, isConnected: false });
|
||||||
|
|
||||||
|
// Login to Discord
|
||||||
|
this._discord.login({ clientId })
|
||||||
|
.then(() => {
|
||||||
|
this._discord.isConnected = true;
|
||||||
|
})
|
||||||
|
.catch((e : any) => console.error(`[DiscordRPC][connect] ${e}`));
|
||||||
|
|
||||||
|
this._discord.on('ready', () => {
|
||||||
|
console.log(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${client.user.username} (${client.user.id})`);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handles Errors
|
||||||
|
this._discord.on('error', (err: any) => {
|
||||||
|
console.error(`[DiscordRPC] ${err}`);
|
||||||
|
this.disconnect()
|
||||||
|
this._discord.isConnected = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from Discord RPC
|
||||||
|
*/
|
||||||
|
private disconnect() {
|
||||||
|
if (this._win.store.store.general.discord_rpc == 0 || !this._discord.isConnected) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._discord.destroy().then(() => {
|
||||||
|
this._discord.isConnected = false;
|
||||||
|
console.log('[DiscordRPC][disconnect] Disconnected from discord.')
|
||||||
|
}).catch((e : any) => console.error(`[DiscordRPC][disconnect] ${e}`));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the activity of the client
|
||||||
|
* @param {object} attributes
|
||||||
|
*/
|
||||||
|
private updateActivity(attributes : any) {
|
||||||
|
if (this._win.store.store.general.discord_rpc == 0) return;
|
||||||
|
|
||||||
|
if (!this._discord.isConnected) {
|
||||||
|
this._discord.clearActivity().catch((e : any) => console.error(`[DiscordRPC][updateActivity] ${e}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('[DiscordRPC][updateActivity] Updating Discord Activity.')
|
||||||
|
|
||||||
|
const listenURL = `https://cider.sh/p?s&id=${attributes.playParams.id}` // cider://play/s/[id] (for song)
|
||||||
|
//console.log(attributes)
|
||||||
|
|
||||||
|
interface ActObject {
|
||||||
|
details?: any,
|
||||||
|
state?: any,
|
||||||
|
startTimestamp?: any,
|
||||||
|
endTimestamp?: any,
|
||||||
|
largeImageKey? : any,
|
||||||
|
largeImageText?: any,
|
||||||
|
smallImageKey?: any,
|
||||||
|
smallImageText?: any,
|
||||||
|
instance: true,
|
||||||
|
buttons?: [
|
||||||
|
{ label: "Listen on Cider", url?: any },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let ActivityObject : ActObject | null = {
|
||||||
|
details: attributes.name,
|
||||||
|
state: `by ${attributes.artistName}`,
|
||||||
|
startTimestamp: attributes.startTime,
|
||||||
|
endTimestamp: attributes.endTime,
|
||||||
|
largeImageKey : (attributes.artwork.url.replace('{w}', '1024').replace('{h}', '1024')) ?? 'cider',
|
||||||
|
largeImageText: attributes.albumName,
|
||||||
|
smallImageKey: (attributes.status ? 'play' : 'pause'),
|
||||||
|
smallImageText: (attributes.status ? 'Playing' : 'Paused'),
|
||||||
|
instance: true,
|
||||||
|
buttons: [
|
||||||
|
{ label: "Listen on Cider", url: listenURL },
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if (ActivityObject.largeImageKey == "" || ActivityObject.largeImageKey == null) {
|
||||||
|
ActivityObject.largeImageKey = (this._win.store.store.general.discord_rpc == 1) ? "cider" : "logo"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the pause/play icon and test for clear activity on pause
|
||||||
|
if (this._win.store.store.general.discordClearActivityOnPause == 1) {
|
||||||
|
delete ActivityObject.smallImageKey
|
||||||
|
delete ActivityObject.smallImageText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the timestamp if its not greater than 0
|
||||||
|
if (!((new Date(attributes.endTime)).getTime() > 0)) {
|
||||||
|
delete ActivityObject.startTimestamp
|
||||||
|
delete ActivityObject.endTimestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist check
|
||||||
|
if (!attributes.artistName) {
|
||||||
|
delete ActivityObject.state
|
||||||
|
}
|
||||||
|
|
||||||
|
// Album text check
|
||||||
|
if (!ActivityObject.largeImageText || ActivityObject.largeImageText.length < 2) {
|
||||||
|
delete ActivityObject.largeImageText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the name is greater than 128 because some songs can be that long
|
||||||
|
if (ActivityObject.details.length > 128) {
|
||||||
|
ActivityObject.details = ActivityObject.details.substring(0, 125) + '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Check if its pausing (false) or playing (true)
|
||||||
|
if (!attributes.status) {
|
||||||
|
if (this._win.store.store.general.discordClearActivityOnPause == 1) {
|
||||||
|
this._discord.clearActivity().catch((e : any) => console.error(`[DiscordRPC][clearActivity] ${e}`));
|
||||||
|
ActivityObject = null
|
||||||
|
} else {
|
||||||
|
delete ActivityObject.startTimestamp
|
||||||
|
delete ActivityObject.endTimestamp
|
||||||
|
ActivityObject.smallImageKey = 'pause'
|
||||||
|
ActivityObject.smallImageText = 'Paused'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (ActivityObject && ActivityObject !== this._discord.activityCache && ActivityObject.details && ActivityObject.state) {
|
||||||
|
try {
|
||||||
|
// console.log(`[DiscordRPC][setActivity] Setting activity to ${JSON.stringify(ActivityObject)}`);
|
||||||
|
this._discord.setActivity(ActivityObject)
|
||||||
|
this._discord.activityCache = ActivityObject
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[DiscordRPC][setActivity] ${err}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Plugin Details (Eventually implemented into a GUI in settings)
|
||||||
|
*/
|
||||||
|
public name: string = 'DiscordRPCPlugin';
|
||||||
|
public description: string = 'Discord RPC plugin for Cider';
|
||||||
|
public version: string = '0.0.1';
|
||||||
|
public author: string = 'vapormusic / Cider Collective';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on plugin load (Currently run on application start)
|
||||||
|
*/
|
||||||
|
constructor(app: any) {
|
||||||
|
this._app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on app ready
|
||||||
|
*/
|
||||||
|
onReady(win: any): void {
|
||||||
|
this._win = win;
|
||||||
|
this.connect((this._win.store.store.general.discord_rpc == 1) ? '911790844204437504' : '886578863147192350');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on app stop
|
||||||
|
*/
|
||||||
|
onBeforeQuit(): void {
|
||||||
|
console.log('Example plugin stopped');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on playback State Change
|
||||||
|
* @param attributes Music Attributes (attributes.state = current state)
|
||||||
|
*/
|
||||||
|
onPlaybackStateDidChange(attributes: object): void {
|
||||||
|
this.updateActivity(attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on song change
|
||||||
|
* @param attributes Music Attributes
|
||||||
|
*/
|
||||||
|
onNowPlayingItemDidChange(attributes: object): void {
|
||||||
|
this.updateActivity(attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
src/renderer/.jsbeautifyrc
Normal file
5
src/renderer/.jsbeautifyrc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"js": {
|
||||||
|
"beautify.ignore": "src/renderer/index.js"
|
||||||
|
}
|
||||||
|
}
|
103
src/renderer/WSAPI_Interop.js
Normal file
103
src/renderer/WSAPI_Interop.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
const wsapi = {
|
||||||
|
cache: {playParams: {id: 0}, status: null, remainingTime: 0},
|
||||||
|
playbackCache: {status: null, time: Date.now()},
|
||||||
|
search(term, limit) {
|
||||||
|
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums'}).then((results)=>{
|
||||||
|
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
searchLibrary(term, limit) {
|
||||||
|
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums'}).then((results)=>{
|
||||||
|
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getAttributes: function () {
|
||||||
|
const mk = MusicKit.getInstance();
|
||||||
|
const nowPlayingItem = mk.nowPlayingItem;
|
||||||
|
const isPlayingExport = mk.isPlaying;
|
||||||
|
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
|
||||||
|
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
|
||||||
|
|
||||||
|
attributes.status = isPlayingExport ? isPlayingExport : false;
|
||||||
|
attributes.name = attributes.name ? attributes.name : 'No Title Found';
|
||||||
|
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
|
||||||
|
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
|
||||||
|
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'};
|
||||||
|
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found';
|
||||||
|
attributes.albumName = attributes.albumName ? attributes.albumName : '';
|
||||||
|
attributes.artistName = attributes.artistName ? attributes.artistName : '';
|
||||||
|
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
|
||||||
|
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0;
|
||||||
|
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
|
||||||
|
attributes.startTime = Date.now();
|
||||||
|
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
|
||||||
|
attributes.volume = mk.volume;
|
||||||
|
attributes.shuffleMode = mk.shuffleMode;
|
||||||
|
attributes.repeatMode = mk.repeatMode;
|
||||||
|
attributes.autoplayEnabled = mk.autoplayEnabled;
|
||||||
|
return attributes
|
||||||
|
},
|
||||||
|
moveQueueItem(oldPosition, newPosition) {
|
||||||
|
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0])
|
||||||
|
MusicKit.getInstance().queue._reindex()
|
||||||
|
},
|
||||||
|
setAutoplay(value) {
|
||||||
|
MusicKit.getInstance().autoplayEnabled = value
|
||||||
|
},
|
||||||
|
returnDynamic(data, type) {
|
||||||
|
ipcRenderer.send('wsapi-returnDynamic', JSON.stringify(data), type)
|
||||||
|
},
|
||||||
|
musickitApi(method, id, params) {
|
||||||
|
MusicKit.getInstance().api[method](id, params).then((results)=>{
|
||||||
|
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getPlaybackState () {
|
||||||
|
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
|
||||||
|
},
|
||||||
|
getLyrics() {
|
||||||
|
return []
|
||||||
|
_lyrics.GetLyrics(1, false)
|
||||||
|
},
|
||||||
|
getQueue() {
|
||||||
|
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue))
|
||||||
|
},
|
||||||
|
playNext(type, id) {
|
||||||
|
var request = {}
|
||||||
|
request[type] = id
|
||||||
|
MusicKit.getInstance().playNext(request)
|
||||||
|
},
|
||||||
|
playLater(type, id) {
|
||||||
|
var request = {}
|
||||||
|
request[type] = id
|
||||||
|
MusicKit.getInstance().playLater(request)
|
||||||
|
},
|
||||||
|
love() {
|
||||||
|
|
||||||
|
},
|
||||||
|
playTrackById(id, kind = "song") {
|
||||||
|
MusicKit.getInstance().setQueue({ [kind]: id }).then(function (queue) {
|
||||||
|
MusicKit.getInstance().play()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
quickPlay(term) {
|
||||||
|
// Quick play by song name
|
||||||
|
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
|
||||||
|
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"] }).then(function (queue) {
|
||||||
|
MusicKit.getInstance().play()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
toggleShuffle() {
|
||||||
|
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
|
||||||
|
},
|
||||||
|
toggleRepeat() {
|
||||||
|
if(MusicKit.getInstance().repeatMode == 0) {
|
||||||
|
MusicKit.getInstance().repeatMode = 2
|
||||||
|
}else if(MusicKit.getInstance().repeatMode == 2){
|
||||||
|
MusicKit.getInstance().repeatMode = 1
|
||||||
|
}else{
|
||||||
|
MusicKit.getInstance().repeatMode = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -249,6 +249,11 @@ const app = new Vue({
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0
|
end: 0
|
||||||
},
|
},
|
||||||
|
v3: {
|
||||||
|
requestBody: {
|
||||||
|
platform: "web"
|
||||||
|
}
|
||||||
|
},
|
||||||
tmpVar: [],
|
tmpVar: [],
|
||||||
notification: false,
|
notification: false,
|
||||||
chrome: {
|
chrome: {
|
||||||
|
@ -610,10 +615,16 @@ const app = new Vue({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.mk.addEventListener(MusicKit.Events.playbackStateDidChange, ()=>{
|
||||||
|
ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
|
||||||
|
})
|
||||||
|
|
||||||
this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => {
|
this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => {
|
||||||
self.lyriccurrenttime = self.mk.currentPlaybackTime
|
self.lyriccurrenttime = self.mk.currentPlaybackTime
|
||||||
this.currentSongInfo = a
|
this.currentSongInfo = a
|
||||||
self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime)
|
self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime)
|
||||||
|
// wsapi
|
||||||
|
ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes());
|
||||||
})
|
})
|
||||||
|
|
||||||
this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
|
this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
|
||||||
|
@ -875,15 +886,18 @@ const app = new Vue({
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async showCollection(response, title, type) {
|
async showCollection(response, title, type, requestBody = {}) {
|
||||||
let self = this
|
let self = this
|
||||||
|
console.log(response)
|
||||||
|
this.collectionList.requestBody = {}
|
||||||
this.collectionList.response = response
|
this.collectionList.response = response
|
||||||
this.collectionList.title = title
|
this.collectionList.title = title
|
||||||
this.collectionList.type = type
|
this.collectionList.type = type
|
||||||
|
this.collectionList.requestBody = requestBody
|
||||||
app.appRoute("collection-list")
|
app.appRoute("collection-list")
|
||||||
},
|
},
|
||||||
async showArtistView(artist, title, view) {
|
async showArtistView(artist, title, view) {
|
||||||
let response = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artist}/view/${view}`)).data
|
let response = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artist}/view/${view}`,{}, {includeResponseMeta: !0})).data
|
||||||
console.log(response)
|
console.log(response)
|
||||||
await this.showCollection(response, title, "artists")
|
await this.showCollection(response, title, "artists")
|
||||||
},
|
},
|
||||||
|
@ -892,7 +906,8 @@ const app = new Vue({
|
||||||
await this.showCollection(response, title, "record-labels")
|
await this.showCollection(response, title, "record-labels")
|
||||||
},
|
},
|
||||||
async showSearchView(term, group, title) {
|
async showSearchView(term, group, title) {
|
||||||
let response = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${term}`, {
|
|
||||||
|
let requestBody = {
|
||||||
platform: "web",
|
platform: "web",
|
||||||
groups: group,
|
groups: group,
|
||||||
types: "activities,albums,apple-curators,artists,curators,editorial-items,music-movies,music-videos,playlists,songs,stations,tv-episodes,uploaded-videos,record-labels",
|
types: "activities,albums,apple-curators,artists,curators,editorial-items,music-movies,music-videos,playlists,songs,stations,tv-episodes,uploaded-videos,record-labels",
|
||||||
|
@ -918,14 +933,18 @@ const app = new Vue({
|
||||||
resource: ["autos"]
|
resource: ["autos"]
|
||||||
},
|
},
|
||||||
groups: group
|
groups: group
|
||||||
|
}
|
||||||
|
let response = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search?term=${term}`, requestBody , {
|
||||||
|
includeResponseMeta: !0
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('searchres', response)
|
console.log('searchres', response)
|
||||||
let responseFormat = {
|
let responseFormat = {
|
||||||
data: response.data.results[group].data,
|
data: response.data.results[group].data,
|
||||||
next: response.data.results[group].data,
|
next: response.data.results[group].next,
|
||||||
groups: group
|
groups: group
|
||||||
}
|
}
|
||||||
await this.showCollection(responseFormat, title, "search")
|
await this.showCollection(responseFormat, title, "search", requestBody)
|
||||||
},
|
},
|
||||||
async getPlaylistContinuous(response, transient = false) {
|
async getPlaylistContinuous(response, transient = false) {
|
||||||
response = response.data.data[0]
|
response = response.data.data[0]
|
||||||
|
@ -1134,7 +1153,10 @@ const app = new Vue({
|
||||||
window.location.hash = `${kind}/${id}`
|
window.location.hash = `${kind}/${id}`
|
||||||
document.querySelector("#app-content").scrollTop = 0
|
document.querySelector("#app-content").scrollTop = 0
|
||||||
} else if (!kind.toString().includes("radioStation") && !kind.toString().includes("song") && !kind.toString().includes("musicVideo") && !kind.toString().includes("uploadedVideo") && !kind.toString().includes("music-movie")) {
|
} else if (!kind.toString().includes("radioStation") && !kind.toString().includes("song") && !kind.toString().includes("musicVideo") && !kind.toString().includes("uploadedVideo") && !kind.toString().includes("music-movie")) {
|
||||||
let params = { extend: "editorialVideo" }
|
let params = {
|
||||||
|
extend: "offers,editorialVideo",
|
||||||
|
"views": "appears-on,more-by-artist,related-videos,other-versions,you-might-also-like,video-extras,audio-extras",
|
||||||
|
}
|
||||||
app.page = (kind) + "_" + (id);
|
app.page = (kind) + "_" + (id);
|
||||||
app.getTypeFromID((kind), (id), (isLibrary), params);
|
app.getTypeFromID((kind), (id), (isLibrary), params);
|
||||||
window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
|
window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
|
||||||
|
@ -2878,7 +2900,7 @@ const app = new Vue({
|
||||||
}
|
}
|
||||||
id = item.id
|
id = item.id
|
||||||
}
|
}
|
||||||
let response = await this.mk.api.v3.music(`/v1/me/ratings/${type}?platform=web&ids=${type.includes('library') ? item.id : id}}`)
|
let response = await this.mk.api.v3.music(`/v1/me/ratings/${type}?platform=web&ids=${type.includes('library') ? item.id : id}`)
|
||||||
if (response.data.data.length != 0) {
|
if (response.data.data.length != 0) {
|
||||||
let value = response.data.data[0].attributes.value
|
let value = response.data.data[0].attributes.value
|
||||||
return value
|
return value
|
||||||
|
@ -3073,7 +3095,6 @@ const app = new Vue({
|
||||||
items: [{
|
items: [{
|
||||||
"icon": "./assets/feather/list.svg",
|
"icon": "./assets/feather/list.svg",
|
||||||
"name": "Add to Playlist...",
|
"name": "Add to Playlist...",
|
||||||
"hidden": true,
|
|
||||||
"action": function() {
|
"action": function() {
|
||||||
app.promptAddToPlaylist()
|
app.promptAddToPlaylist()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
--navbarHeight: 48px;
|
--navbarHeight: 48px;
|
||||||
--selected: rgb(130 130 130 / 30%);
|
--selected: rgb(130 130 130 / 30%);
|
||||||
--selected-click: rgb(80 80 80 / 30%);
|
--selected-click: rgb(80 80 80 / 30%);
|
||||||
|
--hover: rgb(200 200 200 / 10%);
|
||||||
--keyColor: #fa586a;
|
--keyColor: #fa586a;
|
||||||
--keyColor-rgb: 250, 88, 106;
|
--keyColor-rgb: 250, 88, 106;
|
||||||
--keyColor-rollover: #ff8a9c;
|
--keyColor-rollover: #ff8a9c;
|
||||||
|
@ -254,6 +255,32 @@ input[type="text"], input[type="number"] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.artworkMaterial {
|
||||||
|
position: relative;
|
||||||
|
height:100%;
|
||||||
|
width:100%;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
>img {
|
||||||
|
position: absolute;
|
||||||
|
width: 200%;
|
||||||
|
opacity: 0.5;
|
||||||
|
filter: brightness(200%) blur(180px) saturate(280%) contrast(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
>img:first-child {
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
>img:last-child {
|
||||||
|
bottom:0;
|
||||||
|
right: 0;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[artwork-hidden] {
|
[artwork-hidden] {
|
||||||
transition: opacity .25s var(--appleEase);
|
transition: opacity .25s var(--appleEase);
|
||||||
|
@ -867,6 +894,13 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item.volume > input[type=range]::-webkit-slider-thumb {
|
.app-chrome .app-chrome-item.volume > input[type=range]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 14px;
|
||||||
|
width: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgb(50 50 50);
|
||||||
|
cursor: default;
|
||||||
|
box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.4);
|
||||||
transition: all var(--appleTransition);
|
transition: all var(--appleTransition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,10 +914,6 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item.volume > input[type=range] {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-chrome .app-chrome-item.volume > input[type=range] {
|
.app-chrome .app-chrome-item.volume > input[type=range] {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
|
@ -891,16 +921,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-size: 70% 100%;
|
background-size: 70% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
width: 100%,
|
||||||
|
|
||||||
.app-chrome .app-chrome-item.volume > input[type=range]::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
height: 14px;
|
|
||||||
width: 14px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgb(50 50 50);
|
|
||||||
cursor: default;
|
|
||||||
box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item.volume > input[type=range]::-webkit-slider-runnable-track {
|
.app-chrome .app-chrome-item.volume > input[type=range]::-webkit-slider-runnable-track {
|
||||||
|
@ -1018,6 +1039,15 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
||||||
//margin-bottom: -3px;
|
//margin-bottom: -3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.explicit-icon {
|
||||||
|
background-image: url("./assets/explicit.svg");
|
||||||
|
height: 9px;
|
||||||
|
width: 36px;
|
||||||
|
filter: contrast(0);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item > .app-playback-controls .song-duration p {
|
.app-chrome .app-chrome-item > .app-playback-controls .song-duration p {
|
||||||
|
@ -1133,6 +1163,34 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
filter: contrast(0.8);
|
filter: contrast(0.8);
|
||||||
|
|
||||||
|
.lcdMenu {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0px;
|
||||||
|
appearance: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background: var(--hover);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background: var(--selected-click);
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
.svg-icon {
|
||||||
|
--url: url('views/svg/more.svg')!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-chrome .app-chrome-item > .app-playback-controls .playback-info {
|
.app-chrome .app-chrome-item > .app-playback-controls .playback-info {
|
||||||
|
@ -1177,7 +1235,57 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
/* Window is smaller <= 1023px width */
|
/* Window is smaller <= 1023px width */
|
||||||
@media only screen and (max-width: 1023px) {
|
@media only screen and (max-width: 1023px) {
|
||||||
.display--small {
|
.display--small {
|
||||||
display: inherit !important;
|
display: inherit !important;;
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=range] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
border-radius: 5px;
|
||||||
|
background-size: 70% 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 14px;
|
||||||
|
width: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgb(50 50 50);
|
||||||
|
cursor: default;
|
||||||
|
box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.4);
|
||||||
|
transition: all var(--appleTransition);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb:hover {
|
||||||
|
background-image: radial-gradient(var(--keyColor) 2px, transparent 3px, transparent 10px);
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-thumb:active {
|
||||||
|
background-image: radial-gradient(var(--keyColor) 3px, transparent 4px, transparent 10px);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-slider-runnable-track {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.display--large {
|
.display--large {
|
||||||
|
@ -1874,6 +1982,36 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
|
|
||||||
/* Cider */
|
/* Cider */
|
||||||
|
|
||||||
|
.more-btn-round {
|
||||||
|
border-radius: 100%;
|
||||||
|
background: rgba(100, 100, 100, 0.5);
|
||||||
|
box-shadow: var(--ciderShadow-Generic);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 5;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(125%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
filter: brightness(75%);
|
||||||
|
transform: scale(0.98);
|
||||||
|
transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase);
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
width: 100%;
|
||||||
|
background: #eee;
|
||||||
|
--url: url("./views/svg/more.svg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.about-page {
|
.about-page {
|
||||||
.teamBtn {
|
.teamBtn {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1929,6 +2067,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
|
|
||||||
&.md-btn-block {
|
&.md-btn-block {
|
||||||
display: block;
|
display: block;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.md-btn-glyph {
|
||||||
|
display:flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.md-btn-primary {
|
&.md-btn-primary {
|
||||||
|
@ -2376,115 +2522,171 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
.playlist-page {
|
.playlist-page {
|
||||||
--bgColor: transparent;
|
--bgColor: transparent;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 59px, transparent 60px, transparent 100%);
|
//background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 18px, transparent 60px, transparent 100%);
|
||||||
top: 0;
|
top: 0;
|
||||||
padding-top: var(--navigationBarHeight);
|
padding-top: var(--navigationBarHeight);
|
||||||
|
|
||||||
.playlist-body {
|
.playlist-body {
|
||||||
padding: var(--contentInnerPadding);
|
padding: 0px var(--contentInnerPadding) 0px var(--contentInnerPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-bottom: 1px solid rgba(200, 200, 200, 0.05);
|
||||||
|
z-index: 6;
|
||||||
|
padding: 0px 1em;
|
||||||
|
backdrop-filter: blur(32px);
|
||||||
|
background: rgba(24, 24, 24, 0.15);
|
||||||
|
top: var(--navigationBarHeight);
|
||||||
|
transition: opacity 0.1s var(--appleEase);
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist-display {
|
.playlist-display {
|
||||||
padding: var(--contentInnerPadding);
|
padding: var(--contentInnerPadding);
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.playlist-info {
|
.artworkContainer {
|
||||||
flex-shrink: unset;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%);
|
||||||
|
opacity: .7;
|
||||||
|
animation: playlistArtworkFadeIn 1s var(--appleEase);
|
||||||
|
|
||||||
|
.artworkMaterial>img {
|
||||||
|
filter: brightness(100%) blur(80px) saturate(100%) contrast(1);
|
||||||
|
object-position: center;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transform: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlistInfo {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
justify-content: center;
|
||||||
justify-content: flex-end;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.playlist-name {
|
>.row {
|
||||||
font-weight: 700;
|
width: calc(100% - 32px);
|
||||||
font-size: 1.6rem;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
margin-right: 6px;
|
|
||||||
flex-shrink: unset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nameEdit {
|
.playlist-info {
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
margin-right: 6px;
|
|
||||||
flex-shrink: unset;
|
flex-shrink: unset;
|
||||||
background: transparent;
|
display: flex;
|
||||||
border: 0px;
|
flex-flow: column;
|
||||||
color: inherit;
|
justify-content: flex-end;
|
||||||
font-family: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-artist {
|
.playlist-name {
|
||||||
font-size: 20px;
|
font-weight: 700;
|
||||||
margin-bottom: 6px;
|
font-size: 1.6rem;
|
||||||
margin-right: 6px;
|
margin-bottom: 6px;
|
||||||
flex-shrink: unset;
|
margin-right: 6px;
|
||||||
}
|
flex-shrink: unset;
|
||||||
|
|
||||||
.playlist-desc {
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: 14px;
|
|
||||||
flex-shrink: unset;
|
|
||||||
margin-right: 5px;
|
|
||||||
max-height: 100px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
height: 100px;
|
|
||||||
-webkit-mask-image: -webkit-gradient(linear, left 50%, left 90%, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-btn {
|
.nameEdit {
|
||||||
appearance: none;
|
font-weight: 700;
|
||||||
position: absolute;
|
font-size: 1.6rem;
|
||||||
right: 0;
|
margin-bottom: 6px;
|
||||||
bottom: 0;
|
margin-right: 6px;
|
||||||
padding: 0 5px;
|
flex-shrink: unset;
|
||||||
font-size: 14px;
|
background: transparent;
|
||||||
color: var(--keyColor);
|
|
||||||
background-color: transparent;
|
|
||||||
border: 0px;
|
border: 0px;
|
||||||
cursor: pointer;
|
color: inherit;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: flex-end;
|
|
||||||
font-weight: 600;
|
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-desc-expanded {
|
.playlist-artist {
|
||||||
box-sizing: border-box;
|
font-size: 20px;
|
||||||
font-size: 14px;
|
margin-bottom: 6px;
|
||||||
position: relative;
|
margin-right: 6px;
|
||||||
|
flex-shrink: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.more-btn {
|
.playlist-desc {
|
||||||
appearance: none;
|
box-sizing: border-box;
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
padding: 0 5px;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: var(--keyColor);
|
flex-shrink: unset;
|
||||||
background-color: transparent;
|
margin-right: 5px;
|
||||||
border: 0px;
|
max-height: 100px;
|
||||||
cursor: pointer;
|
position: relative;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
.content {
|
||||||
overflow: hidden;
|
height: 100px;
|
||||||
display: flex;
|
-webkit-mask-image: -webkit-gradient(linear, left 50%, left 90%, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0)));
|
||||||
justify-content: flex-end;
|
}
|
||||||
align-items: flex-end;
|
|
||||||
font-weight: 600;
|
.more-btn {
|
||||||
font-family: inherit;
|
appearance: none;
|
||||||
text-transform: uppercase;
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--keyColor);
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-end;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: inherit;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-desc-expanded {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 14px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.more-btn {
|
||||||
|
appearance: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--keyColor);
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-end;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: inherit;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.friends-info {
|
.friends-info {
|
||||||
|
@ -2517,26 +2719,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist-more {
|
|
||||||
border-radius: 100%;
|
|
||||||
background: var(--keyColor);
|
|
||||||
box-shadow: var(--ciderShadow-Generic);
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
float: right;
|
|
||||||
border: 0px;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--keyColor-rollover);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: var(--keyColor-pressed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-time {
|
.playlist-time {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
|
@ -2544,6 +2726,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes playlistArtworkFadeIn {
|
||||||
|
0%{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Collection Page
|
// Collection Page
|
||||||
.collection-page {
|
.collection-page {
|
||||||
padding-bottom: 128px;
|
padding-bottom: 128px;
|
||||||
|
@ -2586,8 +2776,21 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
|
.floating-header {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-bottom: 1px solid rgba(200, 200, 200, 0.05);
|
||||||
|
z-index: 6;
|
||||||
|
padding: 0px 1em;
|
||||||
|
backdrop-filter: blur(32px);
|
||||||
|
background: rgba(24, 24, 24, 0.15);
|
||||||
|
top: var(--navigationBarHeight);
|
||||||
|
transition: opacity 0.1s var(--appleEase);
|
||||||
|
}
|
||||||
|
|
||||||
.artist-header {
|
.artist-header {
|
||||||
background: linear-gradient(45deg, var(--keyColor), #0e0e0e);
|
//background: linear-gradient(45deg, var(--keyColor), #0e0e0e);
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -2595,26 +2798,36 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.artist-more {
|
.header-content {
|
||||||
border-radius: 100%;
|
z-index: 1;
|
||||||
background: var(--keyColor);
|
}
|
||||||
box-shadow: var(--ciderShadow-Generic);
|
|
||||||
width: 36px;
|
.artworkContainer {
|
||||||
height: 36px;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%);
|
||||||
|
opacity: .7;
|
||||||
|
animation: playlistArtworkFadeIn 1s var(--appleEase);
|
||||||
|
|
||||||
|
.artworkMaterial>img {
|
||||||
|
filter: brightness(100%) blur(80px) saturate(100%) contrast(1);
|
||||||
|
object-position: center;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transform: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.more-btn-round {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 26px;
|
bottom: 26px;
|
||||||
right: 32px;
|
right: 32px;
|
||||||
border: 0px;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 5;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--keyColor-rollover);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: var(--keyColor-pressed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.animated {
|
.animated {
|
||||||
|
@ -2681,28 +2894,31 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.artist-play {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: var(--keyColor);
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: var(--mediaItemShadow);
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
border: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--keyColor-rollover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: var(--keyColor-pressed);
|
||||||
|
}
|
||||||
|
}
|
||||||
.artist-title {
|
.artist-title {
|
||||||
|
|
||||||
.artist-play {
|
.artist-play {
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
background: var(--keyColor);
|
|
||||||
border-radius: 100%;
|
|
||||||
margin: 14px;
|
|
||||||
box-shadow: var(--mediaItemShadow);
|
|
||||||
display: none;
|
|
||||||
cursor: pointer;
|
|
||||||
appearance: none;
|
|
||||||
border: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
transform: translateY(3px);
|
transform: translateY(3px);
|
||||||
|
margin: 14px;
|
||||||
&:hover {
|
|
||||||
background: var(--keyColor-rollover);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: var(--keyColor-pressed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.artist-animation-on {
|
&.artist-animation-on {
|
||||||
|
@ -2720,7 +2936,8 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
|
||||||
}
|
}
|
||||||
|
|
||||||
.artist-body {
|
.artist-body {
|
||||||
padding: var(--contentInnerPadding);
|
padding: 0px var(--contentInnerPadding) 0px var(--contentInnerPadding);
|
||||||
|
margin-top: -48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.showmoreless {
|
.showmoreless {
|
||||||
|
|
37
src/renderer/views/components/artwork-material.ejs
Normal file
37
src/renderer/views/components/artwork-material.ejs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<script type="text/x-template" id="artwork-material">
|
||||||
|
<div class="artworkMaterial">
|
||||||
|
<img :src="src" v-for="image in images"/>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.component('artwork-material', {
|
||||||
|
template: '#artwork-material',
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
src: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.src = app.getMediaItemArtwork(this.url, this.size)
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: false,
|
||||||
|
default: '32'
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: false,
|
||||||
|
default: '2'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
0
src/renderer/views/components/mediaitem-info.ejs
Normal file
0
src/renderer/views/components/mediaitem-info.ejs
Normal file
|
@ -1,6 +1,7 @@
|
||||||
<script type="text/x-template" id="mediaitem-scroller-horizontal">
|
<script type="text/x-template" id="mediaitem-scroller-horizontal">
|
||||||
<template>
|
<template>
|
||||||
<div class="cd-hmedia-scroller" :class="kind">
|
<div class="cd-hmedia-scroller" :class="kind">
|
||||||
|
<slot></slot>
|
||||||
<mediaitem-square :kind="kind" :item="item"
|
<mediaitem-square :kind="kind" :item="item"
|
||||||
v-for="item in items"></mediaitem-square>
|
v-for="item in items"></mediaitem-square>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,7 +14,7 @@
|
||||||
props: {
|
props: {
|
||||||
'items': {
|
'items': {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: false
|
||||||
},
|
},
|
||||||
'kind': {
|
'kind': {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
<div tabindex="0"
|
<div tabindex="0"
|
||||||
class="cd-mediaitem-square" :class="getClasses()" @contextmenu="contextMenu"
|
class="cd-mediaitem-square" :class="getClasses()" @contextmenu="contextMenu"
|
||||||
v-observe-visibility="{callback: visibilityChanged}"
|
v-observe-visibility="{callback: visibilityChanged}"
|
||||||
:style="{'--spcolor': getBgColor()}"
|
:style="{'--spcolor': getBgColor()}">
|
||||||
@click.self='app.routeView(item)'>
|
|
||||||
<template v-if="isVisible">
|
<template v-if="isVisible">
|
||||||
<div class="artwork-container">
|
<div class="artwork-container">
|
||||||
<div class="artwork" @click='app.routeView(item)'>
|
<div class="artwork" @click='app.routeView(item)'>
|
||||||
|
|
|
@ -72,10 +72,10 @@
|
||||||
style["top"] = this.event.clientY + "px";
|
style["top"] = this.event.clientY + "px";
|
||||||
// make sure the menu panel isnt off the screen
|
// make sure the menu panel isnt off the screen
|
||||||
if (this.event.clientX + this.size[0] > window.innerWidth) {
|
if (this.event.clientX + this.size[0] > window.innerWidth) {
|
||||||
style["left"] = (window.innerWidth - this.size[0]) + "px";
|
style["left"] = (this.event.clientX - this.size[0]) + "px";
|
||||||
}
|
}
|
||||||
if (this.event.clientY + this.size[1] > window.innerHeight) {
|
if (this.event.clientY + this.size[1] > window.innerHeight) {
|
||||||
style["top"] = (window.innerHeight - this.size[1]) + "px";
|
style["top"] = (this.event.clientY - this.size[1]) + "px";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return style
|
return style
|
||||||
|
|
|
@ -82,10 +82,12 @@
|
||||||
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
|
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
|
||||||
</div>
|
</div>
|
||||||
<div class="playback-info">
|
<div class="playback-info">
|
||||||
<div class="song-name">
|
<div class="song-name" style="-webkit-box-orient: horizontal;"
|
||||||
|
:style="[mk.nowPlayingItem['attributes']['contentRating'] == 'explicit' ? {'margin-left' : '23px'} : {'margin-left' : '0px'} ]">
|
||||||
{{ mk.nowPlayingItem["attributes"]["name"] }}
|
{{ mk.nowPlayingItem["attributes"]["name"] }}
|
||||||
|
<div class="explicit-icon" v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'" style="display: inline-block"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="song-artist "
|
<div class="song-artist"
|
||||||
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;">
|
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;">
|
||||||
<div class="item-navigate song-artist" style="display: inline-block"
|
<div class="item-navigate song-artist" style="display: inline-block"
|
||||||
:style="[chrome.progresshover ? {'opacity': '0'} : {'opacity' : '1'} ]"
|
:style="[chrome.progresshover ? {'opacity': '0'} : {'opacity' : '1'} ]"
|
||||||
|
@ -94,7 +96,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="song-artist item-navigate" style="display: inline-block"
|
<div class="song-artist item-navigate" style="display: inline-block"
|
||||||
:style="[chrome.progresshover ? {'opacity': '0'} : {'opacity' : '1'}]"
|
:style="[chrome.progresshover ? {'opacity': '0'} : {'opacity' : '1'}]"
|
||||||
@click="getNowPlayingItemDetailed('album')">
|
@click="getNowPlayingItemDetailed('album')" v-if="mk.nowPlayingItem['attributes']['albumName'] != ''">
|
||||||
<div class="separator" style="display: inline-block;">{{"—"}}</div>
|
<div class="separator" style="display: inline-block;">{{"—"}}</div>
|
||||||
{{(mk.nowPlayingItem["attributes"]["albumName"]) ?
|
{{(mk.nowPlayingItem["attributes"]["albumName"]) ?
|
||||||
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
|
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
|
||||||
|
@ -115,11 +117,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="mk.nowPlayingItem['attributes']['playParams']">
|
<template v-if="mk.nowPlayingItem['attributes']['playParams']">
|
||||||
<div class="actions"
|
<div class="actions">
|
||||||
v-if="isInLibrary(mk.nowPlayingItem['attributes']['playParams'])">
|
<button class="lcdMenu" @click="nowPlayingContextMenu">
|
||||||
❤️
|
<div class="svg-icon"></div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions" v-else>🖤</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -290,8 +292,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="app-chrome-item volume">
|
<div class="app-chrome-item volume">
|
||||||
<div class="app-chrome-item volume-icon"></div>
|
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
|
<div class="app-chrome-item volume-icon"></div>
|
||||||
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1"
|
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1"
|
||||||
v-model="mk.volume"
|
v-model="mk.volume"
|
||||||
v-if="typeof mk.volume != 'undefined'">
|
v-if="typeof mk.volume != 'undefined'">
|
||||||
|
@ -661,6 +663,8 @@
|
||||||
</button>
|
</button>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Artwork Material -->
|
||||||
|
<%- include('components/artwork-material') %>
|
||||||
<!-- Menu Panel -->
|
<!-- Menu Panel -->
|
||||||
<%- include('components/menu-panel') %>
|
<%- include('components/menu-panel') %>
|
||||||
<!-- Playlist Listing -->
|
<!-- Playlist Listing -->
|
||||||
|
@ -712,5 +716,6 @@
|
||||||
<script src="index.js?v=1"></script>
|
<script src="index.js?v=1"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/resonance-audio/build/resonance-audio.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/resonance-audio/build/resonance-audio.min.js"></script>
|
||||||
<script src="/audio/audio.js?v=1"></script>
|
<script src="/audio/audio.js?v=1"></script>
|
||||||
|
<script src="/WSAPI_Interop.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,5 +1,30 @@
|
||||||
<script type="text/x-template" id="cider-artist-feed">
|
<script type="text/x-template" id="cider-artist-feed">
|
||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="row nopadding">
|
||||||
|
<div class="col nopadding">
|
||||||
|
<h3>Followed Artists</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="well">
|
||||||
|
<mediaitem-scroller-horizontal>
|
||||||
|
<div v-for="artist in artists" style="margin: 6px;">
|
||||||
|
<mediaitem-square :item="artist" kind="small"></mediaitem-square>
|
||||||
|
<button @click="unfollow(artist.id)" class="md-btn md-btn-glyph" style="display:flex;">
|
||||||
|
<div class="sidebar-icon">
|
||||||
|
<div class="svg-icon" :style="{'--url': 'url(./assets/feather/x-circle.svg)'}"></div>
|
||||||
|
</div> Unfollow
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mediaitem-scroller-horizontal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -30,6 +55,7 @@
|
||||||
app: this.$root,
|
app: this.$root,
|
||||||
followedArtists: this.$root.cfg.home.followedArtists,
|
followedArtists: this.$root.cfg.home.followedArtists,
|
||||||
artistFeed: [],
|
artistFeed: [],
|
||||||
|
artists: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
@ -37,11 +63,26 @@
|
||||||
await this.getArtistFeed()
|
await this.getArtistFeed()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
unfollow(id) {
|
||||||
|
let index = this.followedArtists.indexOf(id)
|
||||||
|
if (index > -1) {
|
||||||
|
this.followedArtists.splice(index, 1)
|
||||||
|
}
|
||||||
|
let artist = this.artists.find(a => a.id == id)
|
||||||
|
let index2 = this.artists.indexOf(artist)
|
||||||
|
if (index2 > -1) {
|
||||||
|
this.artists.splice(index2, 1)
|
||||||
|
}
|
||||||
|
this.getArtistFeed()
|
||||||
|
},
|
||||||
async getArtistFeed() {
|
async getArtistFeed() {
|
||||||
let artists = this.followedArtists
|
let artists = this.followedArtists
|
||||||
let self = this
|
let self = this
|
||||||
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${artists.toString()}&views=featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see&extend=artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero&extend[playlists]=trackCount&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=20&art[url]=f`).then(artistData => {
|
this.artists = []
|
||||||
|
this.artistFeed = []
|
||||||
|
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${artists.toString()}&views=latest-release&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=2&art[url]=f`).then(artistData => {
|
||||||
artistData.data.data.forEach(item => {
|
artistData.data.data.forEach(item => {
|
||||||
|
self.artists.push(item)
|
||||||
if (item.views["latest-release"].data.length != 0) {
|
if (item.views["latest-release"].data.length != 0) {
|
||||||
self.artistFeed.push(item.views["latest-release"].data[0])
|
self.artistFeed.push(item.views["latest-release"].data[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,59 @@
|
||||||
<script type="text/x-template" id="cider-artist">
|
<script type="text/x-template" id="cider-artist">
|
||||||
<div class="content-inner artist-page">
|
<div class="content-inner artist-page">
|
||||||
<div class="artist-header" :style="getArtistPalette(data)" :key="data.id">
|
<div class="artist-header" :key="data.id" v-observe-visibility="{callback: isHeaderVisible}">
|
||||||
<animatedartwork-view
|
<animatedartwork-view
|
||||||
:priority="true"
|
:priority="true"
|
||||||
v-if="data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)"
|
v-if="data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)"
|
||||||
:video="data.attributes.editorialVideo.motionArtistWide16x9.video ?? (data.attributes.editorialVideo.motionArtistFullscreen16x9.video ?? '')">
|
:video="data.attributes.editorialVideo.motionArtistWide16x9.video ?? (data.attributes.editorialVideo.motionArtistFullscreen16x9.video ?? '')">
|
||||||
</animatedartwork-view>
|
</animatedartwork-view>
|
||||||
<div class="row">
|
<div class="header-content">
|
||||||
<div class="col-sm" style="width: auto;">
|
<div class="row">
|
||||||
<div class="artist-image" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
|
<div class="col-sm" style="width: auto;">
|
||||||
<mediaitem-artwork
|
<div class="artist-image" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
|
||||||
shadow="large"
|
<mediaitem-artwork
|
||||||
:url="data.attributes.artwork ? data.attributes.artwork.url : ''"
|
shadow="large"
|
||||||
size="190" type="artists"></mediaitem-artwork>
|
:url="data.attributes.artwork ? data.attributes.artwork.url : ''"
|
||||||
<button class="overlay-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
|
size="190" type="artists"></mediaitem-artwork>
|
||||||
|
<button class="overlay-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
|
||||||
app.mk.play()
|
app.mk.play()
|
||||||
})">
|
})">
|
||||||
<%- include("../svg/play.svg") %>
|
<%- include("../svg/play.svg") %>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col flex-center artist-title"
|
||||||
<div class="col flex-center artist-title"
|
:class="{'artist-animation-on': (data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) }"
|
||||||
:class="{'artist-animation-on': (data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) }"
|
>
|
||||||
>
|
<button class="artist-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
|
||||||
<button class="artist-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
|
|
||||||
app.mk.play()
|
app.mk.play()
|
||||||
})"><%- include("../svg/play.svg") %></button>
|
})"><%- include("../svg/play.svg") %></button>
|
||||||
<h1>{{ data.attributes.name }}</h1>
|
<h1>{{ data.attributes.name }}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="more-btn-round" @click="artistMenu">
|
||||||
|
<div class="svg-icon"></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="artworkContainer" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
|
||||||
|
<artwork-material :url="data.attributes.artwork.url" size="190" images="1"></artwork-material>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="floating-header" :style="{opacity: (headerVisible ? 0 : 1),'pointer-events': (headerVisible ? 'none' : '')}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto flex-center">
|
||||||
|
<button class="artist-play" style="display:block;" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
|
||||||
|
app.mk.play()
|
||||||
|
})"><%- include("../svg/play.svg") %></button>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{ data.attributes.name }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto flex-center">
|
||||||
|
<button class="more-btn-round" @click="artistMenu">
|
||||||
|
<div class="svg-icon"></div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="artist-more" @click="artistMenu">
|
|
||||||
<div style=" margin-top: -1px;
|
|
||||||
margin-left: -5px;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;">
|
|
||||||
<%- include("../svg/more.svg") %>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="artist-body">
|
<div class="artist-body">
|
||||||
<div class="row well">
|
<div class="row well">
|
||||||
|
@ -115,6 +132,10 @@
|
||||||
<h3>{{ data.attributes.isGroup ? "Formed" : "Born" }}</h3>
|
<h3>{{ data.attributes.isGroup ? "Formed" : "Born" }}</h3>
|
||||||
{{ data.attributes.bornOrFormed }}
|
{{ data.attributes.bornOrFormed }}
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="data.attributes.genreNames">
|
||||||
|
<h3>Genre</h3>
|
||||||
|
{{ data.attributes.genreNames.join(', ') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -129,10 +150,14 @@
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
topSongsExpanded: false,
|
topSongsExpanded: false,
|
||||||
app: this.$root
|
app: this.$root,
|
||||||
|
headerVisible: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isHeaderVisible(visible) {
|
||||||
|
this.headerVisible = visible
|
||||||
|
},
|
||||||
artistMenu (event) {
|
artistMenu (event) {
|
||||||
let self = this
|
let self = this
|
||||||
let followAction = "follow"
|
let followAction = "follow"
|
||||||
|
|
|
@ -7,81 +7,118 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="app.playlists.loadingState == 1">
|
<template v-if="app.playlists.loadingState == 1">
|
||||||
<div class="playlist-display row"
|
<div class="playlist-display"
|
||||||
:style="{
|
:style="{
|
||||||
background: (data.attributes.artwork != null && data.attributes.artwork['bgColor'] != null) ? ('#' + data.attributes.artwork.bgColor) : '',
|
'--bgColor': (data.attributes.artwork != null && data.attributes.artwork['bgColor'] != null) ? ('#' + data.attributes.artwork.bgColor) : '',
|
||||||
color: (data.attributes.artwork != null && data.attributes.artwork['textColor1'] != null) ? ('#' + data.attributes.artwork.textColor1) : ''
|
'--textColor': (data.attributes.artwork != null && data.attributes.artwork['textColor1'] != null) ? ('#' + data.attributes.artwork.textColor1) : ''
|
||||||
}">
|
}">
|
||||||
<div class="col-auto flex-center">
|
<div class="playlistInfo">
|
||||||
<div style="width: 260px;height:260px;">
|
<div class="row">
|
||||||
<mediaitem-artwork
|
<div class="col-auto flex-center">
|
||||||
:video-priority="true"
|
<div style="width: 260px;height:260px;">
|
||||||
:url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0 && data.relationships.tracks.data[0].attributes != null) ? ((data.relationships.tracks.data[0].attributes.artwork != null)? data.relationships.tracks.data[0].attributes.artwork.url : ''):'')"
|
<mediaitem-artwork
|
||||||
:video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
|
shadow="large"
|
||||||
size="260"
|
:video-priority="true"
|
||||||
></mediaitem-artwork>
|
:url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0 && data.relationships.tracks.data[0].attributes != null) ? ((data.relationships.tracks.data[0].attributes.artwork != null)? data.relationships.tracks.data[0].attributes.artwork.url : ''):'')"
|
||||||
</div>
|
:video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
|
||||||
</div>
|
size="260"
|
||||||
<div class="col playlist-info">
|
></mediaitem-artwork>
|
||||||
<template v-if="!editorialNotesExpanded">
|
|
||||||
<div>
|
|
||||||
<div class="playlist-name" @click="editPlaylistName()" v-show="!nameEditing">
|
|
||||||
{{data.attributes ? (data.attributes.name ??
|
|
||||||
(data.attributes.title ?? '') ?? '') : ''}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="playlist-name" v-show="nameEditing"><input type="text" spellcheck="false"
|
</div>
|
||||||
class="nameEdit"
|
<div class="col playlist-info">
|
||||||
v-model="data.attributes.name"
|
<template v-if="!editorialNotesExpanded">
|
||||||
@blur="editPlaylist"
|
<div>
|
||||||
@change="editPlaylist"
|
<div class="playlist-name" @click="editPlaylistName()" v-show="!nameEditing">
|
||||||
@keydown.enter="editPlaylist"/></div>
|
{{data.attributes ? (data.attributes.name ??
|
||||||
<div class="playlist-artist item-navigate"
|
(data.attributes.title ?? '') ?? '') : ''}}
|
||||||
v-if="getArtistName(data) != ''"
|
</div>
|
||||||
@click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''">
|
<div class="playlist-name" v-show="nameEditing"><input type="text" spellcheck="false"
|
||||||
{{getArtistName(data)}}
|
class="nameEdit"
|
||||||
</div>
|
v-model="data.attributes.name"
|
||||||
<div class="playlist-desc" v-if="data.attributes.description && (data.attributes.description.standard || data.attributes.description.short)">
|
@blur="editPlaylist"
|
||||||
<div v-if="data.attributes.description.short" class="content" v-html="data.attributes.description.short"></div>
|
@change="editPlaylist"
|
||||||
<div v-else-if="data.attributes.description.standard" class="content" v-html="data.attributes.description.standard"></div>
|
@keydown.enter="editPlaylist"/></div>
|
||||||
<button v-if="data.attributes.description.short" class="more-btn"
|
<div class="playlist-artist item-navigate"
|
||||||
@click="editorialNotesExpanded = !editorialNotesExpanded">
|
v-if="getArtistName(data) != ''"
|
||||||
More
|
@click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''">
|
||||||
|
{{getArtistName(data)}}
|
||||||
|
</div>
|
||||||
|
<div class="playlist-desc" v-if="data.attributes.description && (data.attributes.description.standard || data.attributes.description.short)">
|
||||||
|
<div v-if="data.attributes.description.short" class="content" v-html="data.attributes.description.short"></div>
|
||||||
|
<div v-else-if="data.attributes.description.standard" class="content" v-html="data.attributes.description.standard"></div>
|
||||||
|
<button v-if="data.attributes.description.short" class="more-btn"
|
||||||
|
@click="editorialNotesExpanded = !editorialNotesExpanded">
|
||||||
|
More
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="editorialNotesExpanded">
|
||||||
|
<div class="playlist-desc-expanded">
|
||||||
|
<div class="content"
|
||||||
|
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.standard ?? (data.attributes.editorialNotes.short ?? '') ) : (data.attributes.description ? (data.attributes.description.standard ?? (data.attributes.description.short ?? '')) : ''))"></div>
|
||||||
|
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">Less
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="playlist-controls" v-observe-visibility="{callback: isHeaderVisible}">
|
||||||
|
<button class="md-btn" style="min-width: 120px;"
|
||||||
|
@click="app.mk.shuffleMode = 0; play()">
|
||||||
|
Play
|
||||||
|
</button>
|
||||||
|
<button class="md-btn" style="min-width: 120px;"
|
||||||
|
@click="app.mk.shuffleMode = 1;play()">
|
||||||
|
Shuffle
|
||||||
|
</button>
|
||||||
|
<button class="md-btn" style="min-width: 120px;" v-if="inLibrary!=null && confirm!=true"
|
||||||
|
@click="confirmButton()">
|
||||||
|
{{ (!inLibrary) ? "Add to Library" : "Remove from Library" }}
|
||||||
|
</button>
|
||||||
|
<button class="md-btn" style="min-width: 120px;" v-if="confirm==true"
|
||||||
|
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) ">
|
||||||
|
Confirm?
|
||||||
|
</button>
|
||||||
|
<button class="more-btn-round" style="float:right;" @click="menu">
|
||||||
|
<div class="svg-icon"></div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
<template v-if="editorialNotesExpanded">
|
</div>
|
||||||
<div class="playlist-desc-expanded">
|
<div class="artworkContainer" v-if="data.attributes.artwork != null">
|
||||||
<div class="content"
|
<artwork-material :url="data.attributes.artwork.url" size="260" images="1"></artwork-material>
|
||||||
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.standard ?? (data.attributes.editorialNotes.short ?? '') ) : (data.attributes.description ? (data.attributes.description.standard ?? (data.attributes.description.short ?? '')) : ''))"></div>
|
</div>
|
||||||
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">Less
|
|
||||||
|
</div>
|
||||||
|
<div class="floating-header" :style="{opacity: (headerVisible ? 0 : 1),'pointer-events': (headerVisible ? 'none' : '')}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{data.attributes ? (data.attributes.name ??
|
||||||
|
(data.attributes.title ?? '') ?? '') : ''}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto flex-center">
|
||||||
|
<div>
|
||||||
|
<button class="md-btn" style="min-width: 120px;"
|
||||||
|
@click="app.mk.shuffleMode = 0; play()">
|
||||||
|
Play
|
||||||
|
</button>
|
||||||
|
<button class="md-btn" style="min-width: 120px;"
|
||||||
|
@click="app.mk.shuffleMode = 1;play()">
|
||||||
|
Shuffle
|
||||||
|
</button>
|
||||||
|
<button class="md-btn" style="min-width: 120px;" v-if="inLibrary!=null && confirm!=true"
|
||||||
|
@click="confirmButton()">
|
||||||
|
{{ (!inLibrary) ? "Add to Library" : "Remove from Library" }}
|
||||||
|
</button>
|
||||||
|
<button class="md-btn" style="min-width: 120px;" v-if="confirm==true"
|
||||||
|
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) ">
|
||||||
|
Confirm?
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
<div class="playlist-controls">
|
<div class="col-auto flex-center">
|
||||||
<button class="md-btn" style="min-width: 120px;"
|
<button class="more-btn-round" style="float:right;" @click="menu">
|
||||||
@click="app.mk.shuffleMode = 0; play()">
|
<div class="svg-icon"></div>
|
||||||
Play
|
|
||||||
</button>
|
|
||||||
<button class="md-btn" style="min-width: 120px;"
|
|
||||||
@click="app.mk.shuffleMode = 1;play()">
|
|
||||||
Shuffle
|
|
||||||
</button>
|
|
||||||
<button class="md-btn" style="min-width: 120px;" v-if="inLibrary!=null && confirm!=true"
|
|
||||||
@click="confirmButton()">
|
|
||||||
{{ (!inLibrary) ? "Add to Library" : "Remove from Library" }}
|
|
||||||
</button>
|
|
||||||
<button class="md-btn" style="min-width: 120px;" v-if="confirm==true"
|
|
||||||
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) ">
|
|
||||||
Confirm?
|
|
||||||
</button>
|
|
||||||
<button class="playlist-more" @click="menu">
|
|
||||||
<div style=" margin-top: -1px;
|
|
||||||
margin-left: -5px;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;">
|
|
||||||
<%- include("../svg/more.svg") %>
|
|
||||||
</div>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,6 +155,20 @@
|
||||||
style="width: 50%;">
|
style="width: 50%;">
|
||||||
{{data.attributes.copyright}}
|
{{data.attributes.copyright}}
|
||||||
</div>
|
</div>
|
||||||
|
<hr>
|
||||||
|
<template v-if="typeof data.meta != 'undefined'">
|
||||||
|
<div v-for="view in data.meta.views.order" v-if="data.views[view].data.length != 0">
|
||||||
|
<div class="row" >
|
||||||
|
<div class="col">
|
||||||
|
<h3>{{ data.views[view].attributes.title }}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mediaitem-scroller-horizontal :items="data.views[view].data"></mediaitem-scroller-horizontal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -138,7 +189,8 @@
|
||||||
confirm: false,
|
confirm: false,
|
||||||
app: this.$root,
|
app: this.$root,
|
||||||
itemBadges: [],
|
itemBadges: [],
|
||||||
badgesRequested: false
|
badgesRequested: false,
|
||||||
|
headerVisible: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
|
@ -153,6 +205,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isHeaderVisible(visible) {
|
||||||
|
this.headerVisible = visible
|
||||||
|
},
|
||||||
getBadges() {
|
getBadges() {
|
||||||
return
|
return
|
||||||
if (this.badgesRequested) {
|
if (this.badgesRequested) {
|
||||||
|
|
|
@ -14,13 +14,16 @@
|
||||||
<mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square>
|
<mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<button v-if="triggerEnabled" style="opacity:0;height: 32px;" v-observe-visibility="{callback: visibilityChanged}">Show More</button>
|
<button v-if="triggerEnabled" style="opacity:0;height: 32px;"
|
||||||
|
v-observe-visibility="{callback: visibilityChanged}">Show More
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<transition name="fabfade">
|
<transition name="fabfade">
|
||||||
<button class="top-fab" v-show="showFab" @click="scrollToTop()">
|
<button class="top-fab" v-show="showFab" @click="scrollToTop()">
|
||||||
<%- include("../svg/arrow-up.svg") %>
|
<%- include("../svg/arrow-up.svg") %>
|
||||||
</button>
|
</button>
|
||||||
</transition>
|
</transition>
|
||||||
|
<div class="well" v-show="loading"><div class="spinner"></div></div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
@ -47,16 +50,17 @@
|
||||||
canSeeTrigger: false,
|
canSeeTrigger: false,
|
||||||
showFab: false,
|
showFab: false,
|
||||||
commonKind: "song",
|
commonKind: "song",
|
||||||
api: this.$root.mk.api
|
api: this.$root.mk.api,
|
||||||
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getKind(item) {
|
getKind(item) {
|
||||||
if(typeof item.kind != "undefined") {
|
if (typeof item.kind != "undefined") {
|
||||||
this.commonKind = item.kind;
|
this.commonKind = item.kind;
|
||||||
return item.kind
|
return item.kind
|
||||||
}
|
}
|
||||||
if(typeof item.attributes.playParams != "undefined") {
|
if (typeof item.attributes.playParams != "undefined") {
|
||||||
this.commonKind = item.attributes.playParams.kind
|
this.commonKind = item.attributes.playParams.kind
|
||||||
return item.attributes.playParams.kind
|
return item.attributes.playParams.kind
|
||||||
}
|
}
|
||||||
|
@ -71,73 +75,48 @@
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getNext() {
|
getNext() {
|
||||||
// if this.data.next is not null, then we can run this.data.next() and concat to this.data.data to get the next page
|
let self = this
|
||||||
switch(this.type) {
|
this.triggerEnabled = false;
|
||||||
default:
|
if (typeof this.data.next == "undefined") {
|
||||||
case "artists":
|
return
|
||||||
if (this.data.next && this.triggerEnabled) {
|
|
||||||
this.triggerEnabled = false;
|
|
||||||
|
|
||||||
let nextFn = (data => {
|
|
||||||
console.log(data);
|
|
||||||
this.data.next = data.next;
|
|
||||||
this.data.data = this.data.data.concat(data.data);
|
|
||||||
this.triggerEnabled = true;
|
|
||||||
});
|
|
||||||
if(typeof this.data.next == "function") {
|
|
||||||
this.data.next().then(data => nextFn(data));
|
|
||||||
}else{
|
|
||||||
this.api.v3.music(this.data.next).then(data => nextFn(data));
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
console.log("No next page");
|
|
||||||
this.triggerEnabled = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "search":
|
|
||||||
if (this.data.next && this.triggerEnabled) {
|
|
||||||
this.triggerEnabled = false;
|
|
||||||
this.data.next().then(data => {
|
|
||||||
console.log(data);
|
|
||||||
this.data.next = data[this.data.groups].next;
|
|
||||||
this.data.data = this.data.data.concat(data[this.data.groups].data.data);
|
|
||||||
this.triggerEnabled = true;
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
console.log("No next page");
|
|
||||||
this.triggerEnabled = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "listen_now":
|
|
||||||
case "curator":
|
|
||||||
if (this.data.next && this.triggerEnabled) {
|
|
||||||
this.triggerEnabled = false;
|
|
||||||
app.mk.api.v3.music(this.data.next).then(data => {
|
|
||||||
console.log(data);
|
|
||||||
this.data.next = data.data.next;
|
|
||||||
this.data.data = this.data.data.concat(data.data.data);
|
|
||||||
this.triggerEnabled = true;
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
console.log("No next page");
|
|
||||||
this.triggerEnabled = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
this.api.v3.music(this.data.next, app.collectionList.requestBody).then((response) => {
|
||||||
|
console.log(response)
|
||||||
|
if (!app.collectionList.response.groups) {
|
||||||
|
if (response.data.next) {
|
||||||
|
this.data.data = this.data.data.concat(response.data.data);
|
||||||
|
this.data.next = response.data.next;
|
||||||
|
this.triggerEnabled = true;
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
}else{
|
||||||
|
if(!response.data.results[app.collectionList.response.groups]) {
|
||||||
|
this.loading = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (response.data.results[app.collectionList.response.groups].next) {
|
||||||
|
this.data.data = this.data.data.concat(response.data.results[app.collectionList.response.groups].data);
|
||||||
|
this.data.next = response.data.results[app.collectionList.response.groups].next;
|
||||||
|
this.triggerEnabled = true;
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
headerVisibility: function (isVisible, entry) {
|
headerVisibility: function (isVisible, entry) {
|
||||||
if(isVisible) {
|
if (isVisible) {
|
||||||
this.showFab = false;
|
this.showFab = false;
|
||||||
}else{
|
} else {
|
||||||
this.showFab = true;
|
this.showFab = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
visibilityChanged: function (isVisible, entry) {
|
visibilityChanged: function (isVisible, entry) {
|
||||||
if(isVisible) {
|
if (isVisible) {
|
||||||
this.canSeeTrigger = true;
|
this.canSeeTrigger = true;
|
||||||
this.getNext();
|
this.getNext();
|
||||||
}else{
|
} else {
|
||||||
this.canSeeTrigger = false;
|
this.canSeeTrigger = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@
|
||||||
async getArtistFeed() {
|
async getArtistFeed() {
|
||||||
let artists = this.followedArtists
|
let artists = this.followedArtists
|
||||||
let self = this
|
let self = this
|
||||||
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${artists.toString()}&views=featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see&extend=artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero&extend[playlists]=trackCount&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=20&art[url]=f`).then(artistData => {
|
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${artists.toString()}&views=latest-release&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=2&art[url]=f`).then(artistData => {
|
||||||
artistData.data.data.forEach(item => {
|
artistData.data.data.forEach(item => {
|
||||||
if (item.views["latest-release"].data.length != 0) {
|
if (item.views["latest-release"].data.length != 0) {
|
||||||
self.artistFeed.push(item.views["latest-release"].data[0])
|
self.artistFeed.push(item.views["latest-release"].data[0])
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
<mediaitem-square :item="getTopResult()"></mediaitem-square>
|
<mediaitem-square :item="getTopResult()"></mediaitem-square>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else style="text-align: center">
|
||||||
|
<h3>No Results</h3>
|
||||||
|
<p>Try a new search.</p>
|
||||||
|
</div>
|
||||||
<div class="col" v-if="search.results.song">
|
<div class="col" v-if="search.results.song">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
@ -108,10 +112,10 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getTopResult() {
|
getTopResult() {
|
||||||
if (this.search.results["meta"]) {
|
try {
|
||||||
return this.search.results[this.search.results.meta.results.order[0]]["data"][0]
|
return this.search.results[this.search.results.meta.results.order[0]]["data"][0]
|
||||||
} else {
|
} catch( error ) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getCategories() {
|
async getCategories() {
|
||||||
|
|
|
@ -5,5 +5,8 @@
|
||||||
{{ $store.state.test }}
|
{{ $store.state.test }}
|
||||||
<div class="spinner"></div>
|
<div class="spinner"></div>
|
||||||
<button class="md-btn">Cider Button</button>
|
<button class="md-btn">Cider Button</button>
|
||||||
|
<div style="position: relative;width: 300px;height: 300px;">
|
||||||
|
<artwork-material url="https://is3-ssl.mzstatic.com/image/thumb/Music126/v4/13/41/13/1341133b-560f-1aee-461f-c4b32ec049b4/cover.jpg/{w}x{h}bb.jpg"></artwork-material>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -504,6 +504,7 @@ var app = new Vue({
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.onmessage = (e) => {
|
socket.onmessage = (e) => {
|
||||||
|
console.log(e.data)
|
||||||
const response = JSON.parse(e.data);
|
const response = JSON.parse(e.data);
|
||||||
switch (response.type) {
|
switch (response.type) {
|
||||||
default: console.log(response);
|
default: console.log(response);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue