Imported changes to web-remote from AME
This commit is contained in:
parent
45623f257b
commit
ab6c24f9be
11 changed files with 1265 additions and 595 deletions
1
index.js
1
index.js
|
@ -48,7 +48,6 @@ function CreateWindow() {
|
|||
app.on('ready', () => {
|
||||
if (app.isQuiting) { app.quit(); return; }
|
||||
|
||||
require('vue-devtools').install()
|
||||
// Apple Header tomfoolery.
|
||||
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
||||
if(details.url.match(/^https:\/\/store-\d{3}\.blobstore\.apple\.com/) || details.url.startsWith("https://store-037.blobstore.apple.com")){
|
||||
|
|
|
@ -180,7 +180,7 @@
|
|||
</div>
|
||||
<div class="bg-artwork"></div>
|
||||
</div>
|
||||
<script src="https://js-cdn.music.apple.com/musickit/v3/amp/musickit.js"></script>
|
||||
<script src="https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js"></script>
|
||||
<script src="index.js?v=1"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const {app, BrowserWindow} = require("electron")
|
||||
const {app, BrowserWindow, ipcMain} = require("electron")
|
||||
const {join, resolve} = require("path")
|
||||
|
||||
const CiderWin = {
|
||||
|
@ -34,6 +34,8 @@ const CiderWin = {
|
|||
win.on("closed", () => {
|
||||
win = null
|
||||
})
|
||||
},
|
||||
SetupHandlers() {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ const path = require('path');
|
|||
const port = process.argv[2] || 9000;
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const getPort = require('get-port');
|
||||
const {
|
||||
ipcMain,
|
||||
app,
|
||||
|
@ -32,7 +33,7 @@ const wsapi = {
|
|||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
InitWebSockets() {
|
||||
async InitWebSockets () {
|
||||
ipcMain.on('wsapi-updatePlaybackState', (event, arg) => {
|
||||
wsapi.updatePlaybackState(arg);
|
||||
})
|
||||
|
@ -49,12 +50,20 @@ const wsapi = {
|
|||
wsapi.returnSearchLibrary(JSON.parse(arg));
|
||||
});
|
||||
|
||||
ipcMain.on('wsapi-returnDynamic', (event, arg, type) => {
|
||||
wsapi.returnDynamic(JSON.parse(arg), type);
|
||||
});
|
||||
|
||||
ipcMain.on('wsapi-returnMusicKitApi', (event, arg, method) => {
|
||||
wsapi.returnMusicKitApi(JSON.parse(arg), method);
|
||||
});
|
||||
|
||||
ipcMain.on('wsapi-returnLyrics', (event, arg) => {
|
||||
wsapi.returnLyrics(JSON.parse(arg));
|
||||
});
|
||||
|
||||
var safeport = await getPort({port : 26369});
|
||||
wss = new WebSocketServer({
|
||||
port: 26369,
|
||||
port: safeport,
|
||||
perMessageDeflate: {
|
||||
zlibDeflateOptions: {
|
||||
// See zlib defaults.
|
||||
|
@ -75,10 +84,11 @@ const wsapi = {
|
|||
// should not be compressed if context takeover is disabled.
|
||||
}
|
||||
})
|
||||
console.log(`WebSocketServer started on port: ${safeport}`);
|
||||
|
||||
const defaultResponse = new wsapi.standardResponse(0, {}, "OK");
|
||||
|
||||
console.log(`WebSocketServer started on port: ${this.port}`);
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.id = wsapi.createId();
|
||||
console.log(`Client ${ws.id} connected`)
|
||||
|
@ -127,6 +137,13 @@ const wsapi = {
|
|||
case "shuffle":
|
||||
app.win.webContents.executeJavaScript(`wsapi.toggleShuffle()`);
|
||||
break;
|
||||
case "set-shuffle":
|
||||
if(data.shuffle == true) {
|
||||
app.win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 1`);
|
||||
}else{
|
||||
app.win.webContents.executeJavaScript(`MusicKit.getInstance().shuffleMode = 0`);
|
||||
}
|
||||
break;
|
||||
case "repeat":
|
||||
app.win.webContents.executeJavaScript(`wsapi.toggleRepeat()`);
|
||||
break;
|
||||
|
@ -167,10 +184,9 @@ const wsapi = {
|
|||
response.message = "Previous";
|
||||
break;
|
||||
case "musickit-api":
|
||||
|
||||
app.win.webContents.executeJavaScript(`wsapi.musickitApi(\`${data.method}\`, \`${data.id}\`, ${JSON.stringify(data.params)})`);
|
||||
break;
|
||||
case "musickit-library-api":
|
||||
|
||||
break;
|
||||
case "set-autoplay":
|
||||
app.win.webContents.executeJavaScript(`wsapi.setAutoplay(${data.autoplay})`);
|
||||
|
@ -200,7 +216,7 @@ const wsapi = {
|
|||
app.win.hide()
|
||||
break;
|
||||
case "play-mediaitem":
|
||||
app.win.webContents.executeJavaScript(`wsapi.playTrackById(${data.id})`);
|
||||
app.win.webContents.executeJavaScript(`wsapi.playTrackById(${data.id}, \`${data.kind}\`)`);
|
||||
response.message = "Playing track";
|
||||
break;
|
||||
case "get-status":
|
||||
|
@ -249,6 +265,18 @@ const wsapi = {
|
|||
client.send(JSON.stringify(response));
|
||||
});
|
||||
},
|
||||
returnMusicKitApi(results, method) {
|
||||
const response = new wsapi.standardResponse(0, results, "OK", `musickitapi.${method}`);
|
||||
wsapi.clients.forEach(function each(client) {
|
||||
client.send(JSON.stringify(response));
|
||||
});
|
||||
},
|
||||
returnDynamic(results, type) {
|
||||
const response = new wsapi.standardResponse(0, results, "OK", type);
|
||||
wsapi.clients.forEach(function each(client) {
|
||||
client.send(JSON.stringify(response));
|
||||
});
|
||||
},
|
||||
returnLyrics(results) {
|
||||
const response = new wsapi.standardResponse(0, results, "OK", "lyrics");
|
||||
wsapi.clients.forEach(function each(client) {
|
||||
|
@ -274,7 +302,8 @@ const wsapi = {
|
|||
});
|
||||
},
|
||||
webRemotePort: 8090,
|
||||
InitWebServer() {
|
||||
async InitWebServer() {
|
||||
const webRemotePort = await getPort({port : wsapi.webRemotePort});
|
||||
// Web Remote
|
||||
// express server that will serve static files in the "../web-remote" folder
|
||||
const webapp = express();
|
||||
|
@ -283,8 +312,8 @@ const wsapi = {
|
|||
webapp.get('/', function (req, res) {
|
||||
res.sendFile(path.join(webRemotePath, 'index.html'));
|
||||
});
|
||||
webapp.listen(wsapi.webRemotePort, function () {
|
||||
console.log(`Web Remote listening on port ${wsapi.webRemotePort}`);
|
||||
webapp.listen(webRemotePort, function () {
|
||||
console.log(`Web Remote listening on port ${webRemotePort}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,14 @@ const wsapi = {
|
|||
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());
|
||||
},
|
||||
|
@ -38,8 +46,8 @@ const wsapi = {
|
|||
love() {
|
||||
|
||||
},
|
||||
playTrackById(id) {
|
||||
MusicKit.getInstance().setQueue({ song: id }).then(function (queue) {
|
||||
playTrackById(id, kind = "song") {
|
||||
MusicKit.getInstance().setQueue({ [kind]: id }).then(function (queue) {
|
||||
MusicKit.getInstance().play()
|
||||
})
|
||||
},
|
||||
|
|
22
resources/web-remote/assets/Grabber.svg
Normal file
22
resources/web-remote/assets/Grabber.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 28 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-11,-10)">
|
||||
<path d="M39,12.24C39,11.004 37.996,10 36.76,10L13.24,10C12.004,10 11,11.004 11,12.24L11,71.76C11,72.996 12.004,74 13.24,74L36.76,74C37.996,74 39,72.996 39,71.76L39,12.24Z" style="fill:rgb(108,108,108);fill-opacity:0.43;"/>
|
||||
<g transform="matrix(0.714286,0,0,1,7.14286,0)">
|
||||
<rect x="18" y="41" width="14" height="2" style="fill:rgb(231,231,231);fill-opacity:0.77;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.714286,0,0,1,7.14286,-5)">
|
||||
<rect x="18" y="41" width="14" height="2" style="fill:rgb(231,231,231);fill-opacity:0.77;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.714286,0,0,1,7.14286,5)">
|
||||
<rect x="18" y="41" width="14" height="2" style="fill:rgb(231,231,231);fill-opacity:0.77;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.571429,0,0,0.6,10.7143,10.4)">
|
||||
<path d="M25,26L32,36L18,36L25,26Z" style="fill:rgb(231,231,231);fill-opacity:0.77;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.571429,0,0,-0.6,10.7143,73.6)">
|
||||
<path d="M25,26L32,36L18,36L25,26Z" style="fill:rgb(231,231,231);fill-opacity:0.77;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
1
resources/web-remote/assets/infinity.svg
Normal file
1
resources/web-remote/assets/infinity.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" fill="white"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M471.1 96C405 96 353.3 137.3 320 174.6 286.7 137.3 235 96 168.9 96 75.8 96 0 167.8 0 256s75.8 160 168.9 160c66.1 0 117.8-41.3 151.1-78.6 33.3 37.3 85 78.6 151.1 78.6 93.1 0 168.9-71.8 168.9-160S564.2 96 471.1 96zM168.9 320c-40.2 0-72.9-28.7-72.9-64s32.7-64 72.9-64c38.2 0 73.4 36.1 94 64-20.4 27.6-55.9 64-94 64zm302.2 0c-38.2 0-73.4-36.1-94-64 20.4-27.6 55.9-64 94-64 40.2 0 72.9 28.7 72.9 64s-32.7 64-72.9 64z"/></svg>
|
After Width: | Height: | Size: 684 B |
|
@ -14,8 +14,40 @@
|
|||
</head>
|
||||
|
||||
<body oncontextmenu="return false;">
|
||||
<div id="app" :style="{'--artwork': getAlbumArtUrl()}">
|
||||
<div id="app" :style="{'--artwork': getAlbumArtUrl()}">
|
||||
<!-- App view when connected -->
|
||||
<template v-if="connectedState == 1">
|
||||
<!-- Streamer Overlay -->
|
||||
<template></template>
|
||||
<!-- Mini Player -->
|
||||
<template v-if="screen == 'miniplayer'">
|
||||
<div class="miniplayer-main">
|
||||
<div class="media-artwork--miniplayer" :class="artworkPlaying()"
|
||||
:style="{'--artwork': getAlbumArtUrl()}">
|
||||
</div>
|
||||
<div class="miniplayer-draggable">
|
||||
|
||||
</div>
|
||||
<div class="miniplayer-controls">
|
||||
<button class="md-btn playback-button--small repeat" @click="repeat()"
|
||||
v-if="player.currentMediaItem.repeatMode == 0"></button>
|
||||
<button class="md-btn playback-button--small repeat active" @click="repeat()"
|
||||
v-else-if="player.currentMediaItem.repeatMode == 2"></button>
|
||||
<button class="md-btn playback-button--small repeat repeatOne" @click="repeat()"
|
||||
v-else-if="player.currentMediaItem.repeatMode == 1"></button>
|
||||
<button class="md-btn playback-button previous" @click="previous()"></button>
|
||||
<button class="md-btn playback-button pause" @click="pause()"
|
||||
v-if="player.currentMediaItem.status"></button>
|
||||
<button class="md-btn playback-button play" @click="play()" v-else></button>
|
||||
<button class="md-btn playback-button next" @click="next()"></button>
|
||||
<button class="md-btn playback-button--small shuffle" @click="shuffle()"
|
||||
v-if="player.currentMediaItem.shuffleMode == 0"></button>
|
||||
<button class="md-btn playback-button--small shuffle active" @click="shuffle()"
|
||||
v-else></button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Player -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel player-panel" v-if="screen == 'player'">
|
||||
<div class="player_top">
|
||||
|
@ -76,11 +108,11 @@
|
|||
<div class="player_bottom" v-if="player.lowerPanelState == 'controls'">
|
||||
<div class="md-footer">
|
||||
<div class="row player-track-info">
|
||||
<div class="col nopadding">
|
||||
<div class="player-song-title">
|
||||
<div class="col nopadding text-overflow-elipsis">
|
||||
<div class="player-song-title text-overflow-elipsis">
|
||||
{{ player.currentMediaItem.name }}
|
||||
</div>
|
||||
<div class="player-song-artist" @click="searchArtist()">
|
||||
<div class="player-song-artist text-overflow-elipsis" @click="searchArtist()">
|
||||
{{ player.currentMediaItem.artistName }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -143,11 +175,12 @@
|
|||
<button class="md-btn playback-button--small lyrics"
|
||||
v-if="checkOrientation() == 'landscape'" @click="showLyricsInline()"></button>
|
||||
<button class="md-btn playback-button--small queue" @click="showQueue()"></button>
|
||||
<button class="md-btn playback-button--small search" @click="screen = 'search'"></button>
|
||||
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Search -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel search-panel" v-if="screen == 'search'">
|
||||
<div class="search-header">
|
||||
|
@ -204,7 +237,10 @@
|
|||
</div>
|
||||
</transition>
|
||||
<transition name="wpfade">
|
||||
<div class="md-body search-body" style="overflow-y:auto;" v-if="search.state == 2">
|
||||
<div class="md-body search-body"
|
||||
ref="searchBody"
|
||||
@scroll="searchScroll"
|
||||
style="overflow-y:auto;" v-if="search.state == 2">
|
||||
<template v-if="canShowSearchTab('songs')">
|
||||
<div class="list-entry-header">Songs</div>
|
||||
|
||||
|
@ -222,6 +258,7 @@
|
|||
</div>
|
||||
<div class="list-entry-artist">
|
||||
{{ song.artistName }}
|
||||
<span class="lossless-badge" v-if="song.audioTraits.includes('lossless')">Lossless</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -250,7 +287,8 @@
|
|||
<template v-if="canShowSearchTab('albums')">
|
||||
<div class="list-entry-header">Albums</div>
|
||||
|
||||
<div class="list-entry" v-for="album in search.results.albums">
|
||||
<div class="list-entry" v-for="album in search.results.albums"
|
||||
@click="showAlbum(album.id)">
|
||||
<div class="row">
|
||||
<div class="col-auto flex-center">
|
||||
<div class="list-entry-image" v-if="album.artwork"
|
||||
|
@ -263,6 +301,7 @@
|
|||
</div>
|
||||
<div class="list-entry-artist">
|
||||
{{ album.artistName }}
|
||||
<span class="lossless-badge" v-if="album.audioTraits.includes('lossless')">Lossless</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -290,7 +329,10 @@
|
|||
<template v-if="canShowSearchTab('artists')">
|
||||
<div class="list-entry-header">Artists</div>
|
||||
|
||||
<div class="list-entry" v-for="artist in search.results.artists">
|
||||
<div class="list-entry"
|
||||
@click="showArtist(artist.id)"
|
||||
v-for="artist in search.results.artists"
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-auto flex-center">
|
||||
<div class="list-entry-image artist" v-if="artist.artwork"
|
||||
|
@ -330,8 +372,10 @@
|
|||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<footer-player></footer-player>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Track Select Actions -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel context-menu" style="overflow-y:auto;"
|
||||
v-if="search.trackSelect">
|
||||
|
@ -409,6 +453,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Song Actions -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel context-menu" v-if="player.songActions">
|
||||
<div class="md-header">
|
||||
|
@ -461,31 +506,84 @@
|
|||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Artist Page -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel" v-if="screen == 'queue'">
|
||||
<div class="md-container md-container_panel" v-if="screen == 'artist-page'" v-if="artistPage.data['name']">
|
||||
<div class="md-header">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<button class="back-button" @click="screen = 'player'"></button>
|
||||
<button class="back-button" @click="showSearch(true)"></button>
|
||||
</div>
|
||||
<div class="col" style="display: flex;align-items: center;">
|
||||
<div class="col">
|
||||
Queue
|
||||
<div class="col flex-center">
|
||||
{{ artistPage.data["name"] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
</div>
|
||||
<div class="album-body-container" :style="getMediaPalette(artistPage.data)">
|
||||
<div class="artist-header" v-if="artistPage.data['artwork']"
|
||||
:style="getMediaPalette(artistPage.data)">
|
||||
<div class="artist-header-portrait"
|
||||
:style="{'--artwork': getAlbumArtUrlList(artistPage.data['artwork']['url'], 600)}"></div>
|
||||
<h2>{{ artistPage.data["name"] }}</h2>
|
||||
</div>
|
||||
<div class="md-body artist-body">
|
||||
<h2>Songs</h2>
|
||||
<div class="song-scroller-horizontal">
|
||||
<button v-for="song in artistPage.data['songs']" class="song-placeholder"
|
||||
@click="trackSelect(song)">
|
||||
{{ song.name }}
|
||||
</button>
|
||||
</div>
|
||||
<h2>Albums</h2>
|
||||
<div class="mediaitem-scroller-horizontal">
|
||||
<button v-for="album in artistPage.data['albums']" class="album-placeholder"
|
||||
@click="showAlbum(album.id)">
|
||||
{{ album.name }}
|
||||
</button>
|
||||
</div>
|
||||
<h2>Playlists</h2>
|
||||
<div class="mediaitem-scroller-horizontal">
|
||||
<button v-for="playlist in artistPage.data['playlists']" class="album-placeholder">
|
||||
{{ playlist.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer-player></footer-player>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Queue -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel" v-if="screen == 'queue'">
|
||||
<div class="md-header">
|
||||
<div class="list-entry" @click="screen = 'player'">
|
||||
<div class="row">
|
||||
<div class="col-auto flex-center">
|
||||
<div class="list-entry-image" :style="{'--artwork': getAlbumArtUrl()}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col flex-center">
|
||||
<div class="list-entry-name">
|
||||
{{ player.currentMediaItem.name }}
|
||||
</div>
|
||||
<div class="list-entry-artist">
|
||||
{{ player.currentMediaItem.artistName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-header" style="text-align: right;padding: 5px 16px;">
|
||||
<button
|
||||
class="md-btn playback-button--small"
|
||||
class="md-btn playback-button--small autoplay"
|
||||
v-if="!player.currentMediaItem.autoplayEnabled"
|
||||
@click="setAutoplay(true)"
|
||||
>♾️</button>
|
||||
></button>
|
||||
<button
|
||||
class="md-btn playback-button--small active"
|
||||
class="md-btn playback-button--small autoplay activeColor"
|
||||
v-else
|
||||
@click="setAutoplay(false)"
|
||||
>♾️</button>
|
||||
</div>
|
||||
</div>
|
||||
></button>
|
||||
</div>
|
||||
<div class="md-body queue-body" v-if="!player.queue['_queueItems']">
|
||||
Empty
|
||||
|
@ -496,14 +594,14 @@
|
|||
handle=".handle"
|
||||
filter=".passed"
|
||||
@change="queueMove">
|
||||
<template v-for="(song, position) in queue.temp">
|
||||
<template
|
||||
v-for="(song, position) in queue.temp"
|
||||
v-if="position > player.queue['_position']"
|
||||
>
|
||||
<div class="list-entry" :class="getQueuePositionClass(position)">
|
||||
<div class="row" style="width:100%;">
|
||||
<div class="col-auto">
|
||||
<div class="handle">
|
||||
⬆️
|
||||
<br>
|
||||
⬇️
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto flex-center">
|
||||
|
@ -528,14 +626,21 @@
|
|||
</draggable>
|
||||
</div>
|
||||
<div class="md-footer">
|
||||
|
||||
<button class="md-btn playback-button--small lyrics" v-if="checkOrientation() == 'portrait'"
|
||||
@click="showLyrics()"></button>
|
||||
<button class="md-btn playback-button--small lyrics"
|
||||
v-if="checkOrientation() == 'landscape'"
|
||||
@click="screen = 'player';showLyricsInline()"></button>
|
||||
<button class="md-btn playback-button--small queue active" @click="screen = 'player'"></button>
|
||||
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Lyrics -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel" v-if="screen == 'lyrics'">
|
||||
<div class="md-header">
|
||||
<div class="list-entry">
|
||||
<div class="list-entry" @click="screen = 'player'">
|
||||
<div class="row">
|
||||
<div class="col-auto flex-center">
|
||||
<div class="list-entry-image" :style="{'--artwork': getAlbumArtUrl()}">
|
||||
|
@ -578,11 +683,114 @@
|
|||
<div class="md-footer">
|
||||
<button class="md-btn playback-button--small lyrics active" @click="screen = 'player'"></button>
|
||||
<button class="md-btn playback-button--small queue" @click="showQueue()"></button>
|
||||
<button class="md-btn playback-button--small search" @click="screen = 'search'"></button>
|
||||
<button class="md-btn playback-button--small search" @click="showSearch()"></button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Album Page -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel md-container_album"
|
||||
v-if="screen == 'album-page' && albumPage.data['name']"
|
||||
>
|
||||
<div class="md-header">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<button class="back-button" @click="showSearch(true)"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="album-body-container">
|
||||
<div class="md-header">
|
||||
<div class="albumpage-artwork"
|
||||
:style="{'--artwork': getAlbumArtUrlList(albumPage.data['artwork']['url'], 300)}">
|
||||
</div>
|
||||
<div class="albumpage-album-name">
|
||||
{{ albumPage.data["name"] }}
|
||||
</div>
|
||||
<div class="albumpage-artist-name" @click="showArtist(albumPage.data['artists'][0]['id'])">
|
||||
{{ albumPage.data["artistName"] }}
|
||||
</div>
|
||||
<div class="albumpage-misc-info">
|
||||
{{ albumPage.data.genreNames[0] }} ∙ {{ new Date(albumPage.data.releaseDate).getFullYear()
|
||||
}}
|
||||
</div>
|
||||
<div class="row" style="margin-top: 20px;">
|
||||
<div class="col">
|
||||
<button class="wr-btn"
|
||||
@click="playAlbum(albumPage.data.id, false)"
|
||||
style="width:100%;">Play
|
||||
</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="wr-btn" style="width:100%;"
|
||||
@click="playAlbum(albumPage.data.id, true)"
|
||||
>Shuffle
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="albumpage-album-notes" v-if="albumPage.data['editorialNotes']">
|
||||
<div class="notes-preview" v-html="albumPage.data['editorialNotes']['standard']">
|
||||
</div>
|
||||
<button @click="albumPage.editorsNotes = true" class="notes-more">More</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="md-body artist-body">
|
||||
<div class="list-entry-header">Tracks</div>
|
||||
<div class="list-entry" v-for="song in albumPage.data['tracks']"
|
||||
@click="trackSelect(song)">
|
||||
<div class="row">
|
||||
<div class="col-auto flex-center">
|
||||
<div class="list-entry-image" v-if="song.artwork"
|
||||
:style="{'--artwork': getAlbumArtUrlList(song.artwork.url)}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col flex-center">
|
||||
<div class="list-entry-name">
|
||||
{{ song.name }}
|
||||
</div>
|
||||
<div class="list-entry-artist">
|
||||
{{ song.artistName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md-footer">
|
||||
<div>{{ albumPage.data['tracks'].length }} Tracks</div>
|
||||
<div>
|
||||
{{ albumPage.data['copyright'] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer-player></footer-player>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Album Page - Editorial Notes -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel context-menu" v-if="albumPage.editorsNotes"
|
||||
style="padding-top: 42px;">
|
||||
<div class="md-header"
|
||||
:style="getMediaPalette(albumPage.data)"
|
||||
style="font-size: 18px;background:var(--bgColor);color:var(--textColor1);text-align: center;border-radius: 10px 10px 0 0;border-top: 1px solid #ffffff1f;"
|
||||
>
|
||||
{{ albumPage.data["name"] }}
|
||||
</div>
|
||||
<div class="md-body album-page-fullnotes-body"
|
||||
:style="getMediaPalette(albumPage.data)"
|
||||
style="background:var(--bgColor);color:var(--textColor1);"
|
||||
v-html="albumPage.data['editorialNotes']['standard']">
|
||||
</div>
|
||||
<div class="md-footer"
|
||||
:style="getMediaPalette(albumPage.data)"
|
||||
style="background:var(--bgColor);color:var(--textColor1);"
|
||||
>
|
||||
<button class="context-menu-item" @click="albumPage.editorsNotes = false">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<!-- Loading -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel connection-error-panel" v-if="connectedState != 1">
|
||||
<div class="md-header">
|
||||
|
@ -605,6 +813,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<!-- Template -->
|
||||
<transition name="wpfade">
|
||||
<div class="md-container md-container_panel" v-if="false">
|
||||
<div class="md-header">
|
||||
|
@ -618,8 +827,33 @@
|
|||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<script type="text/x-template" id="footer-player">
|
||||
<div class="footer-player" v-show="$parent.player.currentMediaItem['name']">
|
||||
<div class="row" style="width:100%;margin:0px;">
|
||||
<div class="col-auto flex-center" style="padding:0 6px;" @click="$parent.screen = 'player'">
|
||||
<div class="list-entry-image" :style="{'--artwork': $parent.getAlbumArtUrl()}">
|
||||
</div>
|
||||
<script src="index.js?v=1"></script>
|
||||
</div>
|
||||
<div class="col flex-center text-overflow-elipsis" @click="$parent.screen = 'player'">
|
||||
<div class="list-entry-name text-overflow-elipsis">
|
||||
{{ $parent.player.currentMediaItem.name }}
|
||||
</div>
|
||||
<div class="list-entry-artist text-overflow-elipsis">
|
||||
{{ $parent.player.currentMediaItem.artistName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="md-btn playback-button pause" @click="$parent.pause()"
|
||||
v-if="$parent.player.currentMediaItem.status"></button>
|
||||
<button class="md-btn playback-button play" @click="$parent.play()" v-else></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script src="index.js?v=1"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
var socket;
|
||||
|
||||
Vue.component('footer-player', {
|
||||
template: '#footer-player'
|
||||
});
|
||||
|
||||
// vue instance
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
|
@ -22,6 +26,14 @@ var app = new Vue({
|
|||
queue: {
|
||||
temp: []
|
||||
},
|
||||
artistPage: {
|
||||
data: {},
|
||||
editorsNotes: false
|
||||
},
|
||||
albumPage: {
|
||||
data: {},
|
||||
editorsNotes: false
|
||||
},
|
||||
search: {
|
||||
query: "",
|
||||
results: [],
|
||||
|
@ -31,12 +43,29 @@ var app = new Vue({
|
|||
trackSelect: false,
|
||||
selected: {},
|
||||
queue: {},
|
||||
lastPage: "search",
|
||||
lastY: 0
|
||||
},
|
||||
lastPage: "player",
|
||||
connectedState: 0,
|
||||
url: window.location.hostname,
|
||||
mode: "default",
|
||||
// url: "localhost",
|
||||
},
|
||||
methods: {
|
||||
searchScroll(e) {
|
||||
this.search.lastY = e.target.scrollTop;
|
||||
},
|
||||
musicKitAPI(method, id, params) {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
action: "musickit-api",
|
||||
method: method,
|
||||
id: id,
|
||||
params: params
|
||||
})
|
||||
)
|
||||
},
|
||||
resetPlayerUI() {
|
||||
this.player.lowerPanelState = "controls";
|
||||
},
|
||||
|
@ -111,7 +140,7 @@ var app = new Vue({
|
|||
setTimeout(() => {
|
||||
this.getQueue()
|
||||
}, 1000)
|
||||
}else{
|
||||
} else {
|
||||
this.getQueue()
|
||||
}
|
||||
},
|
||||
|
@ -171,10 +200,11 @@ var app = new Vue({
|
|||
getArtworkColor(hex) {
|
||||
return `#${hex}`
|
||||
},
|
||||
playMediaItemById(id) {
|
||||
playMediaItemById(id, kind = "song") {
|
||||
socket.send(JSON.stringify({
|
||||
action: "play-mediaitem",
|
||||
id: id
|
||||
id: id,
|
||||
kind: kind
|
||||
}))
|
||||
this.screen = "player";
|
||||
},
|
||||
|
@ -309,6 +339,38 @@ var app = new Vue({
|
|||
return ["passed"]
|
||||
}
|
||||
},
|
||||
showSearch(reset = false) {
|
||||
if(reset) {
|
||||
this.search.lastPage = "search"
|
||||
}
|
||||
switch(this.search.lastPage) {
|
||||
case "search":
|
||||
this.screen = "search"
|
||||
break;
|
||||
case "album":
|
||||
this.screen = "album-page"
|
||||
break;
|
||||
case "artist":
|
||||
this.screen = "artist-page"
|
||||
break;
|
||||
case "playlist":
|
||||
this.screen = "playlist-page"
|
||||
break;
|
||||
}
|
||||
},
|
||||
showArtistByName(name) {
|
||||
this.musicKitAPI("search", name, {types: "artists"})
|
||||
},
|
||||
showAlbum(id) {
|
||||
this.search.lastPage = "album"
|
||||
this.screen = "album-page"
|
||||
this.musicKitAPI("album", id, {})
|
||||
},
|
||||
showArtist(id) {
|
||||
this.search.lastPage = "artist"
|
||||
this.screen = "artist-page"
|
||||
this.musicKitAPI("artist", id, {include: "songs,playlists,albums"})
|
||||
},
|
||||
showQueue() {
|
||||
this.queue.temp = this.player["queue"]["_queueItems"]
|
||||
this.screen = "queue"
|
||||
|
@ -338,6 +400,31 @@ var app = new Vue({
|
|||
}))
|
||||
this.getCurrentMediaItem()
|
||||
},
|
||||
setShuffle(val) {
|
||||
socket.send(JSON.stringify({
|
||||
action: "set-shuffle",
|
||||
shuffle: val
|
||||
}))
|
||||
this.getCurrentMediaItem()
|
||||
},
|
||||
getMediaPalette(data) {
|
||||
var palette = {
|
||||
'--bgColor': `#${data['artwork']['bgColor']}`,
|
||||
'--textColor1': `#${data['artwork']['textColor1']}`,
|
||||
'--textColor2': `#${data['artwork']['textColor2']}`,
|
||||
'--textColor3': `#${data['artwork']['textColor3']}`,
|
||||
'--textColor4': `#${data['artwork']['textColor4']}`
|
||||
}
|
||||
return palette
|
||||
},
|
||||
playAlbum(id, shuffle = false) {
|
||||
if(shuffle) {
|
||||
this.setShuffle(true)
|
||||
}else{
|
||||
this.setShuffle(false)
|
||||
}
|
||||
this.playMediaItemById(id, 'album');
|
||||
},
|
||||
getLyrics() {
|
||||
socket.send(JSON.stringify({
|
||||
action: "get-lyrics",
|
||||
|
@ -373,6 +460,19 @@ var app = new Vue({
|
|||
action: "get-currentmediaitem"
|
||||
}))
|
||||
},
|
||||
setStreamerOverlay() {
|
||||
document.body.classList.add("streamer-overlay")
|
||||
},
|
||||
setMode(mode) {
|
||||
switch(mode) {
|
||||
default:
|
||||
this.screen = "player"
|
||||
break;
|
||||
case "miniplayer":
|
||||
this.screen = "miniplayer"
|
||||
break;
|
||||
}
|
||||
},
|
||||
connect() {
|
||||
let self = this;
|
||||
this.connectedState = 0;
|
||||
|
@ -384,7 +484,11 @@ var app = new Vue({
|
|||
console.log(e);
|
||||
console.log('connected');
|
||||
app.connectedState = 1;
|
||||
self.screen = "player"
|
||||
if(getParameterByName("mode")) {
|
||||
self.setMode(getParameterByName("mode"))
|
||||
}else{
|
||||
self.setMode("default")
|
||||
}
|
||||
self.clearSelectedTrack()
|
||||
}
|
||||
|
||||
|
@ -404,20 +508,25 @@ var app = new Vue({
|
|||
const response = JSON.parse(e.data);
|
||||
switch (response.type) {
|
||||
default:
|
||||
|
||||
console.log(response);
|
||||
break;
|
||||
case "musickitapi.search":
|
||||
self.showArtist(response.data["artists"][0]["id"]);
|
||||
break;
|
||||
case "musickitapi.album":
|
||||
if(self.screen == "album-page") {
|
||||
self.albumPage.data = response.data
|
||||
}
|
||||
break;
|
||||
case "musickitapi.artist":
|
||||
if(self.screen == "artist-page") {
|
||||
self.artistPage.data = response.data
|
||||
}
|
||||
break;
|
||||
case "queue":
|
||||
self.player.queue = response.data;
|
||||
self.queue.temp = response.data["_queueItems"];
|
||||
self.$forceUpdate()
|
||||
if (self.screen == "queue") {
|
||||
setTimeout(() => {
|
||||
document.querySelector(".playing").scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "start"
|
||||
})
|
||||
}, 200)
|
||||
}
|
||||
break;
|
||||
case "lyrics":
|
||||
self.player.lyrics = response.data;
|
||||
|
@ -455,6 +564,16 @@ var app = new Vue({
|
|||
},
|
||||
});
|
||||
|
||||
function getParameterByName(name, url) {
|
||||
if (!url) url = window.location.href;
|
||||
name = name.replace(/[\[\]]/g, '\\$&');
|
||||
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
|
||||
results = regex.exec(url);
|
||||
if (!results) return null;
|
||||
if (!results[2]) return '';
|
||||
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function xmlToJson(xml) {
|
||||
|
||||
// Create the return object
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "AME",
|
||||
"short_name": "AME",
|
||||
"description": "AME",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/icon.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icon.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"display": "fullscreen",
|
||||
"start_url": "/web-remote/index.html",
|
||||
"orientation": "portrait"
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
:root {
|
||||
--appleEase: cubic-bezier(0.42, 0, 0.58, 1);
|
||||
--mediaItemShadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55);
|
||||
--keyColor: #fa586a;
|
||||
--keyColor-rgb: 250, 88, 106;
|
||||
--keyColor-rollover: #ff8a9c;
|
||||
|
@ -85,6 +86,18 @@ body {
|
|||
filter: blur(32px) saturate(180%);
|
||||
}
|
||||
|
||||
body.streamer-overlay {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.streamer-overlay #app {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body.streamer-overlay #app:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #111;
|
||||
font-family: "Segoe UI Variable Display", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
|
@ -124,6 +137,24 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
font-family: inherit;
|
||||
}
|
||||
|
||||
.wr-btn {
|
||||
font-family: inherit;
|
||||
appearance: none;
|
||||
border:0px;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
font-weight: 600;
|
||||
background: rgb(80 80 80 / 70%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.footer-player {
|
||||
padding: 6px;
|
||||
display:flex;
|
||||
border-top: 1px solid rgb(200 200 200 / 15%);
|
||||
background: rgb(200 200 200 / 10%);
|
||||
}
|
||||
|
||||
.player-duration-time {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
@ -151,7 +182,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55);
|
||||
box-shadow: var(--mediaItemShadow);
|
||||
transition: transform .10s var(--appleEase);
|
||||
}
|
||||
|
||||
|
@ -160,6 +191,14 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
transform: scale(0.85);
|
||||
}
|
||||
|
||||
.lossless-badge {
|
||||
background: rgb(150 150 150);
|
||||
border-radius: 6px;
|
||||
padding: 0px 6px;
|
||||
color: rgb(30 30 30);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.playback-slider {
|
||||
width: 90%;
|
||||
}
|
||||
|
@ -218,6 +257,10 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
background-color: rgb(200 200 200 / 10%);
|
||||
}
|
||||
|
||||
.playback-button--small.activeColor {
|
||||
background-color: var(--keyColor);
|
||||
}
|
||||
|
||||
.playback-button--small.search {
|
||||
background-image: url("./assets/search.svg");
|
||||
}
|
||||
|
@ -230,6 +273,10 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
background-image: url("./assets/quote-right.svg");
|
||||
}
|
||||
|
||||
.playback-button--small.autoplay {
|
||||
background-image: url("./assets/infinity.svg");
|
||||
}
|
||||
|
||||
.playback-button--small.shuffle {
|
||||
background-image: url("./assets/shuffle.svg");
|
||||
}
|
||||
|
@ -392,8 +439,8 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 8px;
|
||||
box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--mediaItemShadow);
|
||||
}
|
||||
|
||||
.list-entry-image.artist {
|
||||
|
@ -424,10 +471,48 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
.list-entry .handle {
|
||||
height: 100%;
|
||||
width: 28px;
|
||||
background:var(--keyColor);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url('./assets/Grabber.svg');
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.list-entry .handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.song-scroller-horizontal {
|
||||
display:flex;
|
||||
overflow-y: scroll;
|
||||
overflow-x:hidden;
|
||||
overflow-y: overlay;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
/*! flex-flow: row; */
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.song-placeholder {
|
||||
height: 60px;
|
||||
width: 50%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mediaitem-scroller-horizontal {
|
||||
display:flex;
|
||||
overflow-y: hidden;
|
||||
overflow-x:scroll;
|
||||
overflow-x: overlay;
|
||||
width: 100%;
|
||||
}
|
||||
.album-placeholder {
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.md-container {
|
||||
|
@ -485,6 +570,8 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
overflow-y: overlay;
|
||||
}
|
||||
|
||||
.queue-body {
|
||||
|
@ -492,6 +579,130 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.text-overflow-elipsis {
|
||||
min-width: 0px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
.album-body-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
overflow-y: overlay;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
.albumpage-artwork {
|
||||
--artwork: url("");
|
||||
width: 30vh;
|
||||
height: 30vh;
|
||||
margin: 0 auto;
|
||||
border-radius: 6px;
|
||||
background:black;
|
||||
margin-bottom: 18px;
|
||||
box-shadow: var(--mediaItemShadow);
|
||||
background-image: var(--artwork);
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.albumpage-misc-info {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
opacity: 0.50;
|
||||
}
|
||||
|
||||
.albumpage-album-notes {
|
||||
margin: 0 auto;
|
||||
margin-top: 16px;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.albumpage-album-notes>.notes-preview {
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
-webkit-mask-image: -webkit-gradient(linear, left 95%, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0)));
|
||||
}
|
||||
|
||||
.albumpage-album-notes>.notes-more {
|
||||
position: absolute;
|
||||
bottom:0px;
|
||||
right:0px;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
color: var(--keyColor);
|
||||
font-weight: bold;
|
||||
font-family: inherit;
|
||||
text-transform: uppercase;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
border:0px;
|
||||
display:flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.album-page-fullnotes-body {
|
||||
padding: 22px;
|
||||
font-size: 17px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: overlay;
|
||||
}
|
||||
|
||||
.albumpage-album-name {
|
||||
margin: 0 auto;
|
||||
font-size: 17px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.albumpage-artist-name {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: var(--keyColor);
|
||||
}
|
||||
|
||||
.albumpage-artist-name:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.artist-header {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
margin: 12px auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
flex-direction: column;
|
||||
background: rgb(0 0 0 / 40%);
|
||||
}
|
||||
|
||||
.artist-header .artist-header-portrait {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
background: var(--artwork);
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 100%;
|
||||
box-shadow: var(--mediaItemShadow);
|
||||
}
|
||||
|
||||
.search-body {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
@ -499,6 +710,12 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
padding-top: 220px;
|
||||
}
|
||||
|
||||
.artist-body {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.search-tab {
|
||||
background: rgb(20 20 20 / 0.85);
|
||||
border-radius: 50px;
|
||||
|
@ -691,6 +908,48 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.miniplayer-main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.miniplayer-main .miniplayer-controls {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
background:rgb(0 0 0 / 50%);
|
||||
height: 80px;
|
||||
z-index: 2;
|
||||
width:100%;
|
||||
display:flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
}
|
||||
|
||||
.miniplayer-main .miniplayer-draggable {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: calc(100% - 80px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.miniplayer-main .miniplayer-controls .md-btn {
|
||||
width: 40px;
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
.media-artwork--miniplayer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--artwork);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* Small Screen */
|
||||
@media only screen and (max-height: 668px) {
|
||||
#app {
|
||||
|
@ -706,6 +965,23 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
.album-body-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.album-body-container>.md-header {
|
||||
min-width: 300px;
|
||||
flex: 0 0 auto;
|
||||
/*! max-width: 300px; */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.artist-body {
|
||||
position: relative;
|
||||
/*! flex: 0 0 auto; */
|
||||
}
|
||||
|
||||
.player-panel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue