Merge branch 'main' into typescript

This commit is contained in:
Core 2022-01-08 15:17:27 +00:00
commit 332ac4d0d7
No known key found for this signature in database
GPG key ID: 1B77805746C47C28
36 changed files with 295 additions and 3573 deletions

View file

@ -1 +0,0 @@
demo.cider.sh

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- 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 fill="white" d="M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z"/></svg>

Before

Width:  |  Height:  |  Size: 521 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- 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 fill="white" d="M11.5 280.6l192 160c20.6 17.2 52.5 2.8 52.5-24.6V96c0-27.4-31.9-41.8-52.5-24.6l-192 160c-15.3 12.8-15.3 36.4 0 49.2zm256 0l192 160c20.6 17.2 52.5 2.8 52.5-24.6V96c0-27.4-31.9-41.8-52.5-24.6l-192 160c-15.3 12.8-15.3 36.4 0 49.2z"/></svg>

Before

Width:  |  Height:  |  Size: 500 B

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>ic_cast_black_24dp</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" sketch:type="MSPage">
<g id="ic_cast_black_24dp" sketch:type="MSArtboardGroup">
<g id="ic_remove_circle_white_24dp" sketch:type="MSLayerGroup">
<path d="M1,18 L1,21 L4,21 C4,19.34 2.66,18 1,18 L1,18 Z M1,14 L1,16 C3.76,16 6,18.24 6,21 L8,21 C8,17.13 4.87,14 1,14 L1,14 Z M1,10 L1,12 C5.97,12 10,16.03 10,21 L12,21 C12,14.92 7.07,10 1,10 L1,10 Z M21,3 L3,3 C1.9,3 1,3.9 1,5 L1,8 L3,8 L3,5 L21,5 L21,19 L14,19 L14,21 L21,21 C22.1,21 23,20.1 23,19 L23,5 C23,3.9 22.1,3 21,3 L21,3 Z" id="cast" fill="white" sketch:type="MSShapeGroup"></path>
<rect id="bounds" sketch:type="MSShapeGroup" x="0" y="0" width="24" height="24"></rect>
</g>
</g>
<g id="assets" sketch:type="MSLayerGroup" transform="translate(-208.000000, -106.000000)">
<g id="64px" transform="translate(0.000000, 114.000000)"></g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- 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 fill="white" d="M500.5 231.4l-192-160C287.9 54.3 256 68.6 256 96v320c0 27.4 31.9 41.8 52.5 24.6l192-160c15.3-12.8 15.3-36.4 0-49.2zm-256 0l-192-160C31.9 54.3 0 68.6 0 96v320c0 27.4 31.9 41.8 52.5 24.6l192-160c15.3-12.8 15.3-36.4 0-49.2z"/></svg>

Before

Width:  |  Height:  |  Size: 493 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- 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 fill="white" d="M80 368H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm0-320H16A16 16 0 0 0 0 64v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16zm0 160H16a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h64a16 16 0 0 0 16-16v-64a16 16 0 0 0-16-16zm416 176H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"/></svg>

Before

Width:  |  Height:  |  Size: 831 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- 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 fill="white" d="M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z"/></svg>

Before

Width:  |  Height:  |  Size: 487 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- 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 fill="white" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"/></svg>

Before

Width:  |  Height:  |  Size: 384 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- 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 fill="white" d="M464 32H336c-26.5 0-48 21.5-48 48v128c0 26.5 21.5 48 48 48h80v64c0 35.3-28.7 64-64 64h-8c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24h8c88.4 0 160-71.6 160-160V80c0-26.5-21.5-48-48-48zm-288 0H48C21.5 32 0 53.5 0 80v128c0 26.5 21.5 48 48 48h80v64c0 35.3-28.7 64-64 64h-8c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24h8c88.4 0 160-71.6 160-160V80c0-26.5-21.5-48-48-48z"/></svg>

Before

Width:  |  Height:  |  Size: 640 B

View file

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 60 60" style="enable-background:new 0 0 60 60;" xml:space="preserve">
<g>
<path fill="white" d="M42,12H20.414l7.293-7.293c0.391-0.391,0.391-1.023,0-1.414s-1.023-0.391-1.414,0l-8.999,8.999
c-0.093,0.092-0.166,0.203-0.217,0.326c-0.101,0.244-0.101,0.52,0,0.764c0.051,0.123,0.124,0.234,0.217,0.326l8.999,8.999
C26.488,22.902,26.744,23,27,23s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L20.414,14H42c8.822,0,16,7.178,16,16
c0,4.252-1.668,8.264-4.696,11.295c-0.391,0.391-0.391,1.024,0,1.414c0.195,0.195,0.451,0.293,0.707,0.293s0.512-0.098,0.707-0.293
C58.124,39.3,60,34.786,60,30C60,20.075,51.925,12,42,12z"/>
<path fill="white" d="M35.707,37.293c-0.391-0.391-1.023-0.391-1.414,0s-0.391,1.023,0,1.414L41.586,46H18C9.178,46,2,38.822,2,30
c0-3.783,1.359-7.46,3.828-10.354c0.358-0.421,0.309-1.052-0.111-1.41c-0.419-0.359-1.052-0.31-1.41,0.111
C1.529,21.604,0,25.741,0,30c0,9.925,8.075,18,18,18h23.586l-7.293,7.293c-0.391,0.391-0.391,1.023,0,1.414
C34.488,56.902,34.744,57,35,57s0.512-0.098,0.707-0.293l9-9c0.391-0.391,0.391-1.023,0-1.414L35.707,37.293z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- 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 fill="white" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>

Before

Width:  |  Height:  |  Size: 618 B

View file

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 230.055 230.055" style="enable-background:new 0 0 230.055 230.055;" xml:space="preserve">
<path fill="white" d="M199.419,124.497c-3.516-3.515-9.213-3.515-12.729,0c-3.515,3.515-3.515,9.213,0,12.728l12.637,12.636h-8.406
c-8.177,0-16.151-2.871-22.453-8.083l-32.346-26.751l32.345-26.751c6.303-5.212,14.277-8.083,22.454-8.083h8.406L186.69,92.83
c-3.515,3.515-3.515,9.213,0,12.728c1.758,1.757,4.061,2.636,6.364,2.636s4.606-0.879,6.364-2.636l28-28
c3.515-3.515,3.515-9.213,0-12.728l-28-28c-3.516-3.515-9.213-3.515-12.729,0c-3.515,3.515-3.515,9.213,0,12.728l12.637,12.636
h-8.406c-12.354,0-24.403,4.337-33.926,12.211L122,103.348L82.564,70.733c-6.658-5.507-15.084-8.54-23.724-8.54H9
c-4.971,0-9,4.029-9,9s4.029,9,9,9h49.841c4.462,0,8.813,1.566,12.252,4.411l36.786,30.423L71.094,145.45
c-3.439,2.844-7.791,4.411-12.253,4.411H9c-4.971,0-9,4.029-9,9s4.029,9,9,9h49.841c8.64,0,17.065-3.033,23.725-8.54L122,126.707
l34.996,28.943c9.521,7.875,21.57,12.211,33.925,12.211h8.406l-12.637,12.636c-3.515,3.515-3.515,9.213,0,12.728
c1.758,1.757,4.061,2.636,6.364,2.636s4.606-0.879,6.364-2.636l28-28c3.515-3.515,3.515-9.213,0-12.728L199.419,124.497z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!-- 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 fill="white" d="M215.03 72.04L126.06 161H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V89.02c0-21.47-25.96-31.98-40.97-16.98zm123.2 108.08c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 229.28 336 242.62 336 257c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.87z"/></svg>

Before

Width:  |  Height:  |  Size: 710 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- 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 fill="white" d="M215.03 71.05L126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97zm233.32-51.08c-11.17-7.33-26.18-4.24-33.51 6.95-7.34 11.17-4.22 26.18 6.95 33.51 66.27 43.49 105.82 116.6 105.82 195.58 0 78.98-39.55 152.09-105.82 195.58-11.17 7.32-14.29 22.34-6.95 33.5 7.04 10.71 21.93 14.56 33.51 6.95C528.27 439.58 576 351.33 576 256S528.27 72.43 448.35 19.97zM480 256c0-63.53-32.06-121.94-85.77-156.24-11.19-7.14-26.03-3.82-33.12 7.46s-3.78 26.21 7.41 33.36C408.27 165.97 432 209.11 432 256s-23.73 90.03-63.48 115.42c-11.19 7.14-14.5 22.07-7.41 33.36 6.51 10.36 21.12 15.14 33.12 7.46C447.94 377.94 480 319.54 480 256zm-141.77-76.87c-11.58-6.33-26.19-2.16-32.61 9.45-6.39 11.61-2.16 26.2 9.45 32.61C327.98 228.28 336 241.63 336 256c0 14.38-8.02 27.72-20.92 34.81-11.61 6.41-15.84 21-9.45 32.61 6.43 11.66 21.05 15.8 32.61 9.45 28.23-15.55 45.77-45 45.77-76.88s-17.54-61.32-45.78-76.86z"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,442 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="preconnect" href="https://amp-api.music.apple.com/" crossorigin/>
<link rel="preconnect" href="https://api.music.apple.com/" crossorigin/>
<link rel="preconnect" href="https://is1-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is2-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is3-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is4-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://is5-ssl.mzstatic.com/" crossorigin/>
<link rel="preconnect" href="https://play.itunes.apple.com/" crossorigin/>
<link rel="preconnect" href="https://aod-ssl.itunes.apple.com/" crossorigin/>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Cider</title>
<link rel="stylesheet" href="style.css?v=2">
<script src="vue.js"></script>
<script src="sortable.min.js"></script>
<script src="vuedraggable.umd.min.js"></script>
<link rel="manifest" href="./manifest.json?v=2">
</head>
<body oncontextmenu="return false;" loading="1">
<div id="app">
<div id="app-main">
<div class="app-chrome">
<div class="app-chrome--left">
<div class="app-chrome-item full-height">
<div class="app-title"></div>
</div>
<div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
@click="mk.shuffleMode = 1"></button>
<button class="playback-button--small shuffle active" v-else @click="mk.shuffleMode = 0"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button previous" @click="mk.skipToPreviousItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button pause" @click="mk.pause()" v-if="mk.isPlaying"></button>
<button class="playback-button play" @click="mk.play()" v-else></button>
</div>
<div class="app-chrome-item">
<button class="playback-button next" @click="mk.skipToNextItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
v-else-if="mk.repeatMode == 1"></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 0"
v-else-if="mk.repeatMode == 2"></button>
</div>
</div>
<div class="app-chrome--center">
<div class="app-chrome-item playback-controls">
<template v-if="mkReady()">
<div class="app-playback-controls">
<div class="artwork" :style="{'--artwork': getNowPlayingArtwork(42)}"></div>
<div class="playback-info">
<div class="song-name">
{{ mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist">
{{ mk.nowPlayingItem["attributes"]["artistName"] }} - {{
mk.nowPlayingItem["attributes"]["albumName"] }}
</div>
<div class="song-progress">
<input type="range" step="0.01" min="0"
@change="mk.seekToTime($event.target.value)"
:max="mk.currentPlaybackDuration"
:value="playerLCD.playbackDuration">
</div>
</div>
<div class="actions">❤️</div>
</div>
</template>
</div>
</div>
<div class="app-chrome--right">
<div class="app-chrome-item volume display--large">
<input type="range" class="" step="0.01" min="0" max="1" v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'">
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 22" version="1.1" fill="#fff"
style="width: 100%; height: 100%; fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421">
<path
d="M16.811,12.75c0.245,-0.355 0.389,-0.786 0.389,-1.25c0,-1.215 -0.985,-2.2 -2.2,-2.2c-1.215,0 -2.2,0.985 -2.2,2.2c0,0.466 0.145,0.898 0.392,1.254l-0.83,1.047c-0.537,-0.616 -0.862,-1.42 -0.862,-2.301c0,-1.933 1.567,-3.5 3.5,-3.5c1.933,0 3.5,1.567 3.5,3.5c0,0.879 -0.324,1.683 -0.859,2.297l-0.83,-1.047Zm1.271,1.604c0.694,-0.749 1.118,-1.752 1.118,-2.854c0,-2.32 -1.88,-4.2 -4.2,-4.2c-2.32,0 -4.2,1.88 -4.2,4.2c0,1.103 0.425,2.107 1.121,2.857l-0.814,1.028c-0.993,-0.995 -1.607,-2.368 -1.607,-3.885c0,-3.038 2.462,-5.5 5.5,-5.5c3.038,0 5.5,2.462 5.5,5.5c0,1.515 -0.613,2.887 -1.604,3.882l-0.814,-1.028Zm1.252,1.58c1.151,-1.126 1.866,-2.697 1.866,-4.434c0,-3.424 -2.776,-6.2 -6.2,-6.2c-3.424,0 -6.2,2.776 -6.2,6.2c0,1.739 0.716,3.311 1.869,4.437l-0.811,1.023c-1.452,-1.368 -2.358,-3.308 -2.358,-5.46c0,-4.142 3.358,-7.5 7.5,-7.5c4.142,0 7.5,3.358 7.5,7.5c0,2.15 -0.905,4.089 -2.355,5.457l-0.811,-1.023Zm-0.227,2.066l-8.219,0c-0.355,0 -0.515,-0.434 -0.27,-0.717l4.058,-5.12c0.178,-0.217 0.474,-0.217 0.652,0l4.058,5.12c0.237,0.283 0.085,0.717 -0.279,0.717Z"
style="fill-rule:nonzero"></path>
</svg>
</button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue"></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small lyrics"></button>
</div>
<div class="app-chrome-item full-height">
<div class="window-controls">
<div class="minimize" onclick="ipcRenderer.send('minimize')"></div>
<div class="minmax" onclick="ipcRenderer.send('maximize')"></div>
<div class="close" onclick="ipcRenderer.send('close')"></div>
</div>
</div>
</div>
</div>
<div class="app-navigation">
<div id="app-sidebar">
<div class="app-sidebar-header">
<div class="search-input-container">
<div class="search-input--icon"></div>
<input type="search"
spellcheck="false"
@click="showSearch()"
@change="showSearch();searchQuery()"
placeholder="Search..."
v-model="search.term"
class="search-input">
</div>
</div>
<div class="app-sidebar-content">
<div class="app-sidebar-header-text">
Apple Music
</div>
<sidebar-library-item name="Listen Now" page="listen_now"></sidebar-library-item>
<sidebar-library-item name="Browse" page="browse"></sidebar-library-item>
<sidebar-library-item name="Radio" page="radio"></sidebar-library-item>
<div class="app-sidebar-header-text">
Library
</div>
<sidebar-library-item name="Songs" page="library-songs"></sidebar-library-item>
<sidebar-library-item name="Albums" page="library-albums"></sidebar-library-item>
<sidebar-library-item name="Artists" page="library-artists"></sidebar-library-item>
<sidebar-library-item name="Made For You" page="library-madeforyou"></sidebar-library-item>
<div class="app-sidebar-header-text">
Playlists
</div>
<button class="app-sidebar-item" v-for="item in playlists.listing" :key="item.id" href="item.href">
{{ item.attributes.name }}
</button>
</div>
<div class="app-sidebar-footer">
<input type="range" class="display--small">
<button class="app-sidebar-button" style="width:100%">
<div class="sidebar-user-icon">
</div>
<div class="sidebar-user-text">Cider User</div>
</button>
</div>
</div>
<div id="app-content">
<!-- Browse -->
<transition name="wpfade">
<template v-if="page == 'browse'">
<div class="content-inner">
<button id="apple-music-authorize" class="md-btn md-btn-primary" @click="init()">Start MusicKit
</button>
<button id="apple-music-unauthorize" class="md-btn md-btn-primary" @click="unauthorize()">Stop
MusicKit
</button>
<br>
<template v-if="mk.nowPlayingItem">
currentPlaybackProgress: {{ app.mk.currentPlaybackProgress }}
<br>
currentPlaybackDuration: {{ app.mk.currentPlaybackDuration }}
</template>
<div><input type="text" v-model="quickPlayQuery">
<button @click="quickPlay(quickPlayQuery)">Play</button>
</div>
<h1 class="header-text">Browse</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu tincidunt
consectetur, nisl nunc euismod nisi, eu porttitor nisl nisi euismod nisi.
</p>
<div class="media-item--small">
<div class="artwork">
</div>
<div class="text">
Text
</div>
<div class="subtext">
Subtext
</div>
</div>
<br>
<br>
<h1 class="header-text">Listen Now</h1>
<div class="winbox">
<div class="fancy">990kbps</div>
<div class="">
<button class="md-btn md-btn-primary">Audio Quality Settings</button>
</div>
</div>
<button class="md-btn" @click="drawertest = !drawertest">Toggle Drawer</button>
<button class="md-btn">Button</button>
<button class="md-btn md-btn-primary">Button</button>
</div>
</template>
</transition>
<!-- Listen Now -->
<transition v-on:enter="getListenNow()" name="wpfade">
<template v-if="page == 'listen_now'" @created="console.log('listennow')">
<cider-listen-now :data="listennow"></cider-listen-now>
</template>
</transition>
<!-- Radio -->
<transition v-on:enter="getRadioStations()" name="wpfade">
<template v-if="page == 'radio'" @created="console.log('radio')">
<div class="content-inner">
<h1 class="header-text">Radio</h1>
<h3>Recent Stations</h3>
<mediaitem-square :item="item" v-for="item in radio.personal"></mediaitem-square>
</div>
</template>
</transition>
<!-- Search -->
<transition name="wpfade">
<template v-if="page == 'search'">
<cider-search :search="search"></cider-search>
</template>
</transition>
<!-- Library - Songs -->
<transition name="wpfade" v-on:enter="getLibrarySongs()">
<template v-if="page == 'library-songs'">
<div class="content-inner">
<h1 class="header-text">Songs</h1>
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
class="search-input">
</div>
<mediaitem-list-item :item="item"
v-for="item in library.songs.listing"></mediaitem-list-item>
</div>
</template>
</transition>
</div>
<div class="app-drawer" v-if="drawertest">
</div>
</div>
</div>
<transition name="wpfade">
<img v-show="chrome.artworkReady"
@load="chrome.artworkReady = true"
class="bg-artwork"
:src="getNowPlayingArtworkBG(32)">
</transition>
<transition name="wpfade">
<div class="bg-artwork--placeholder" v-else></div>
</transition>
</div>
<script type="text/x-template" id="cider-listen-now">
<div class="content-inner">
<h1 class="header-text">Listen Now</h1>
<template v-for="recom in data.data">
<div class="row">
<div class="col">
<h3>{{ recom.attributes.title ? recom.attributes.title.stringForDisplay : ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="recom.relationships.contents.data.length >= 10">
<button class="cd-btn-seeall">See All</button>
</div>
</div>
<mediaitem-scroller-horizontal :items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal>
</template>
</div>
</script>
<script type="text/x-template" id="cider-search">
<div class="content-inner">
<div class="row">
<div class="col-sm" style="width: auto;" v-if="getTopResult()">
<template >
<h3>Top Result</h3>
<mediaitem-square :item="getTopResult()"></mediaitem-square>
</template>
</div>
<div class="col" v-if="search.results.songs">
<div class="row">
<div class="col">
<h3>Songs</h3>
</div>
<div class="col-auto flex-center" v-if="search.results.songs.data.length >= 6">
<button class="cd-btn-seeall">See All</button>
</div>
</div>
<div>
<mediaitem-list-item :item="item"
v-for="item in search.results.songs.data.limit(6)"></mediaitem-list-item>
</div>
</div>
</div>
<template v-if="search.results['meta']">
<template v-if="search.results.albums">
<div class="row">
<div class="col">
<h3>Albums</h3>
</div>
<div class="col-auto flex-center" v-if="search.results.albums.data.length >= 10">
<button class="cd-btn-seeall">See All</button>
</div>
</div>
<mediaitem-scroller-horizontal :items="search.results.albums.data.limit(10)"></mediaitem-scroller-horizontal>
</template>
<template v-if="search.results.artists">
<div class="row">
<div class="col">
<h3>Artists</h3>
</div>
<div class="col-auto flex-center" v-if="search.results.artists.data.length >= 5">
<button class="cd-btn-seeall">See All</button>
</div>
</div>
<mediaitem-square :item="item"
v-for="item in search.results.artists.data.limit(5)"></mediaitem-square>
</template>
<template v-if="search.results.playlists">
<div class="row">
<div class="col">
<h3>Playlists</h3>
</div>
<div class="col-auto flex-center" v-if="search.results.playlists.data.length >= 10">
<button class="cd-btn-seeall">See All</button>
</div>
</div>
<mediaitem-square :item="item"
v-for="item in search.results.playlists.data.limit(10)"></mediaitem-square>
</template>
</template>
</div>
</script>
<script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
<script type="text/x-template" id="sidebar-library-item">
<button class="app-sidebar-item"
:class="$parent.getSidebarItemClass(page)"
@click="$parent.page = page">{{ name }}
</button>
</script>
<script type="text/x-template" id="mediaitem-scroller-horizontal">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square :item="item"
v-for="item in items"></mediaitem-square>
</div>
</template>
</script>
<script type="text/x-template" id="mediaitem-list-item">
<template>
<div @click="app.playMediaItemById(item.id, item.type)"
class="cd-mediaitem-list-item">
<div class="artwork"
:class="{'round': item.type == 'artists'}"
:style="{'--artwork': app.getMediaItemArtwork(item.attributes.artwork.url, 34)}
"></div>
<div class="info-rect">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis">
<template v-if="item.attributes.artistName">
{{ item.attributes.artistName }}
<template v-if="item.attributes.albumName">
- {{ item.attributes.albumName }}
</template>
</template>
</div>
</div>
<div class="content-rating" v-if="item.attributes.contentRating">
{{ item.attributes.contentRating }}
</div>
</div>
</template>
</script>
<script type="text/x-template" id="mediaitem-hrect">
<template>
<div @click="app.playMediaItemById(item.id, item.type)"
class="cd-mediaitem-hrect">
<div class="artwork"
:class="{'round': item.type == 'artists'}"
:style="{'--artwork': app.getMediaItemArtwork(item.attributes.artwork.url, 70)}
"></div>
<div class="info-rect">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis">
{{ item.type }}
<template v-if="item.attributes.artistName">
∙ {{ item.attributes.artistName }}
</template>
</div>
</div>
</div>
</template>
</script>
<script type="text/x-template" id="mediaitem-square">
<template>
<div @click="app.playMediaItemById(item.id, item.type)"
class="cd-mediaitem-square">
<div class="artwork"
:class="{'round': item.type == 'artists'}"
:style="{'--artwork': app.getMediaItemArtwork(item.attributes.artwork.url, 128)}"></div>
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis" v-if="item.attributes.artistName">
{{ item.attributes.artistName }}
</div>
</div>
</template>
</script>
<script src="https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js"></script>
<script src="index.js?v=1"></script>
</body>
</html>

View file

@ -1,318 +0,0 @@
Vue.component('sidebar-library-item', {
template: '#sidebar-library-item',
props: ['name', 'page', 'cd-click'],
methods: {}
});
Vue.component('mediaitem-scroller-horizontal', {
template: '#mediaitem-scroller-horizontal',
props: ['items'],
methods: {}
});
Vue.component('mediaitem-square', {
template: '#mediaitem-square',
props: ['item'],
methods: {}
});
Vue.component('mediaitem-hrect', {
template: '#mediaitem-hrect',
props: ['item'],
methods: {}
});
Vue.component('mediaitem-list-item', {
template: '#mediaitem-list-item',
props: ['item'],
methods: {}
});
Vue.component('cider-search', {
template: "#cider-search",
props: ['search'],
methods: {
getTopResult() {
if (this.search.results["meta"]) {
return this.search.results[this.search.results.meta.results.order[0]]["data"][0]
} else {
return false;
}
}
}
})
Vue.component('cider-listen-now', {
template: "#cider-listen-now",
props: ["data"]
})
const MusicKitTools = {
getHeader() {
return new Headers({
Authorization: 'Bearer ' + MusicKit.getInstance().developerToken,
Accept: 'application/json',
'Content-Type': 'application/json',
'Music-User-Token': '' + MusicKit.getInstance().musicUserToken
});
}
}
// limit an array to a certain number of items
Array.prototype.limit = function (n) {
return this.slice(0, n);
};
const app = new Vue({
el: "#app",
data: {
drawertest: false,
mk: {},
quickPlayQuery: "",
search: {
term: "",
results: {},
limit: 10
},
playerLCD: {
playbackDuration: 0
},
listennow: [],
radio: {
personal: []
},
library: {
songs: {
listing: [],
meta: {total: 0}
}
},
playlists: {
listing: [],
details: {}
},
chrome: {
artworkReady: false
},
page: "browse"
},
methods: {
init() {
let self = this
this.mk = MusicKit.getInstance()
this.mk.authorize()
this.$forceUpdate()
this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => {
self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime)
})
this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => {
self.chrome.artworkReady = false
})
this.apiCall('https://api.music.apple.com/v1/me/library/playlists', res => {
self.playlists.listing = res.data
})
document.body.removeAttribute("loading")
},
getSidebarItemClass(page) {
if (this.page == page) {
return ["active"]
} else {
return []
}
},
async mkapi(method, library = false, term, params = {}, params2 = {}, attempts = 0) {
if (attempts > 3) {
return
}
try {
if (library) {
return await this.mk.api.library[method](term, params, params2)
} else {
return await this.mk.api[method](term, params, params2)
}
} catch (e) {
console.log(e)
return await this.mkapi(method, library, term, params, params2, attempts + 1)
}
},
async getLibrarySongs() {
var response = await this.mkapi("songs", true, "", {limit: 100}, {includeResponseMeta: !0})
this.library.songs.listing = response.data
this.library.songs.meta = response.meta
},
async getListenNow(attempt = 0) {
if (attempt > 3) {
return
}
try {
this.listennow = await this.mk.api.personalRecommendations("",
{
name: "listen-now",
with: "friendsMix,library,social",
"art[social-profiles:url]": "c",
"art[url]": "c,f",
"omit[resource]": "autos",
"relate[editorial-items]": "contents",
extend: ["editorialCard", "editorialVideo"],
"extend[albums]": ["artistUrl"],
"extend[library-albums]": ["artistUrl"],
"extend[playlists]": ["artistNames", "editorialArtwork"],
"extend[library-playlists]": ["artistNames", "editorialArtwork"],
"extend[social-profiles]": "topGenreNames",
"include[albums]": "artists",
"include[songs]": "artists",
"include[music-videos]": "artists",
"fields[albums]": ["artistName", "artistUrl", "artwork", "contentRating", "editorialArtwork", "editorialVideo", "name", "playParams", "releaseDate", "url"],
"fields[artists]": ["name", "url"],
"extend[stations]": ["airDate", "supportsAirTimeUpdates"],
"meta[stations]": "inflectionPoints",
types: "artists,albums,editorial-items,library-albums,library-playlists,music-movies,music-videos,playlists,stations,uploaded-audios,uploaded-videos,activities,apple-curators,curators,tv-shows,social-profiles,social-upsells",
platform: "web"
},
{
includeResponseMeta: !0,
reload: !0
});
console.log(this.listennow)
} catch (e) {
console.log(e)
this.getListenNow(attempt + 1)
}
},
async getRadioStations(attempt = 0) {
if (attempt > 3) {
return
}
try {
this.radio.personal = await this.mkapi("recentRadioStations", false, "",
{
"platform": "web",
"art[url]": "f"
});
} catch (e) {
console.log(e)
this.getRadioStations(attempt + 1)
}
},
unauthorize() {
this.mk.unauthorize()
},
showSearch() {
this.page = "search"
},
playMediaItemById(id, kind) {
this.mk.setQueue({[kind]: [id]}).then(function (queue) {
MusicKit.getInstance().play()
})
},
searchQuery() {
let self = this
this.mk.api.search(this.search.term,
{
types: "songs,artists,albums,playlists",
limit: self.search.limit
}).then(function (results) {
self.search.results = results
})
},
mkReady() {
if (this.mk["nowPlayingItem"]) {
return true
} else {
return false
}
},
getMediaItemArtwork(url, size = 64) {
return `url("${url.replace('{w}', size).replace('{h}', size).replace('{f}', "webp").replace('{c}', "cc")}")`;
},
getNowPlayingArtworkBG(size = 600) {
if(!this.mkReady()) {
return ""
}
if (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"]) {
return `${this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"].replace('{w}', size).replace('{h}', size)}`;
} else {
return "";
}
},
getNowPlayingArtwork(size = 600) {
if (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"]) {
return `url("${this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"].replace('{w}', size).replace('{h}', size)}")`;
} else {
return "";
}
},
quickPlay(query) {
let self = this
MusicKit.getInstance().api.search(query, {limit: 2, types: 'songs'}).then(function (data) {
MusicKit.getInstance().setQueue({song: data["songs"]['data'][0]["id"]}).then(function (queue) {
MusicKit.getInstance().play()
setTimeout(() => {
self.$forceUpdate()
}, 1000)
})
})
},
apiCall(url, callback) {
const xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = (e) => {
if (xmlHttp.readyState !== 4) {
return;
}
if (xmlHttp.status === 200) {
console.log('SUCCESS', xmlHttp.responseText);
callback(JSON.parse(xmlHttp.responseText));
} else {
console.warn('request_error');
}
};
xmlHttp.open("GET", url);
xmlHttp.setRequestHeader("Authorization", "Bearer " + MusicKit.getInstance().developerToken);
xmlHttp.setRequestHeader("Music-User-Token", "" + MusicKit.getInstance().musicUserToken);
xmlHttp.setRequestHeader("Accept", "application/json");
xmlHttp.setRequestHeader("Content-Type", "application/json");
xmlHttp.responseType = "text";
xmlHttp.send();
},
fetchPlaylist(id, callback) {
// id can be found in playlist.attributes.playParams.globalId
this.mk.api.playlist(id).then(res => {
callback(res)
})
// tracks are found in relationship.data
}
}
})
document.addEventListener('musickitloaded', function () {
// MusicKit global is now defined
fetch("https://beta.music.apple.com/")
.then(response => response.text())
.then(data => {
var el = document.createElement("html");
el.innerHTML = data;
var u = el.querySelector(`[name="desktop-music-app/config/environment"]`)
var amwebCFG = JSON.parse(decodeURIComponent(u.getAttribute("content")));
console.log(amwebCFG.MEDIA_API.token)
// eh fuck it lets just expose the token
MusicKit.configure({
developerToken: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IldlYlBsYXlLaWQifQ.eyJpc3MiOiJBTVBXZWJQbGF5IiwiaWF0IjoxNjM2NTYwMjc1LCJleHAiOjE2NTIxMTIyNzV9.is4KeAN_M9FWTfuw9zMV2lgHSSdPqEV2SX-XfCuEYY4qtmjbo-NjebHCageS28z0P0erksqql9rtsoizE4hsJg",
app: {
name: 'My Cool Web App',
build: '1978.4.1'
}
});
setTimeout(() => {
app.init()
}, 1000)
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,71 +0,0 @@
:root {
--appleEase: cubic-bezier(.42, 0, .58, 1);
--appleTransition: .2s var(--appleEase);
}
body, html {
height: 100%;
margin: 0;
width: 100%;
padding: 0px;
}
body {
background: #333;
background-image: url("./wallpaper.webp");
background-size: cover;
background-repeat: no-repeat;
}
#wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#app-main {
width: 90%;
height: 90%;
max-width: 1366px;
max-height: 768px;
overflow: hidden;
border-radius: 10px;
background: rgb(100 100 100 / 20%);
box-shadow: 0px 0px 0px 1px rgb(200 200 200 / 20%),
0px 8px 15px 4px rgb(0 0 0 / 25%);
backdrop-filter: blur(64px) saturate(180%);
animation: windowIn var(--appleEase) .35s;
}
[hidden] {
display: none;
}
.cider-splash {
width: 200px;
height: 200px;
background-image: url("./Cider.png");
background-size: contain;
background-repeat: no-repeat;
filter: drop-shadow(0px 10px 10px rgb(255, 42, 85));
animation: windowIn var(--appleEase) .35s;
}
#app-main>iframe {
width: 100%;
height: 100%;
border:0px;
}
@keyframes windowIn {
0% {
opacity: 0;
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}

View file

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cider UI Preview</title>
<link rel="stylesheet" href="index.css" type="text/css"/>
</head>
<body>
<div id="wrapper">
<div class="cider-splash"></div>
<div id="app-main" hidden>
<iframe src="./app-win/index.html"></iframe>
</div>
</div>
<script src="./index.js"></script>
</body>
</html>

View file

@ -1,6 +0,0 @@
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
document.querySelector(".cider-splash").setAttribute("hidden", "true");
document.querySelector("#app-main").removeAttribute("hidden");
}, 1000);
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View file

@ -28,6 +28,7 @@
"discord-rpc": "^4.0.1", "discord-rpc": "^4.0.1",
"ejs": "^3.1.6", "ejs": "^3.1.6",
"electron-acrylic-window": "^0.5.11", "electron-acrylic-window": "^0.5.11",
"electron-fetch": "^1.7.4",
"electron-log": "^4.4.4", "electron-log": "^4.4.4",
"electron-store": "^8.0.1", "electron-store": "^8.0.1",
"electron-updater": "^4.6.1", "electron-updater": "^4.6.1",

View file

@ -0,0 +1,6 @@
<?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 24 24" 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:currentColor;fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;">
<path d="M20.84,4.61C19.809,3.578 18.409,2.998 16.95,2.998C15.491,2.998 14.091,3.578 13.06,4.61L12,5.67L10.94,4.61C9.909,3.579 8.508,2.999 7.05,2.999C4.032,2.999 1.549,5.482 1.549,8.5C1.549,9.958 2.129,11.359 3.16,12.39L12,21.23L20.84,12.39C21.872,11.359 22.452,9.959 22.452,8.5C22.452,7.041 21.872,5.641 20.84,4.61Z" style="fill:none;fill-rule:nonzero;stroke:currentColor;stroke-width:2px;"/>
<path d="M12,5.67L14.614,9.632L10.399,12.114L14.536,14.354L12,20.013" style="fill:none;stroke:currentColor;stroke-width:2px;stroke-miterlimit:1.5;"/>
</svg>

After

Width:  |  Height:  |  Size: 1,021 B

View file

@ -1189,19 +1189,87 @@ const app = new Vue({
this.getArtistFromID(id) this.getArtistFromID(id)
//this.getTypeFromID("artist",id,isLibrary,query) //this.getTypeFromID("artist",id,isLibrary,query)
}, },
playMediaItem(item) { playMediaItem(item) {
let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? '')); let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? ''));
let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? '')); let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? ''));
; ;
let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false; let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false;
console.log(kind, id, isLibrary) console.log(kind, id, isLibrary)
app.mk.stop().then(() => {
if (kind.includes("artist")) { if (kind.includes("artist")) {
app.mk.setStationQueue({artist: 'a-' + id}).then(() => { app.mk.setStationQueue({artist: 'a-' + id}).then(() => {
app.mk.play() app.mk.play()
}) })
} else { } else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))){
/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
app.mk.clearQueue().then(function () { {
app.mk.setQueue({[item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id}).then(function () {
app.mk.play().then(function (){
app.mk.clearQueue().then(function (){
var playlistId = id
const params = {
include: "tracks",
platform: "web",
"include[library-playlists]": "catalog,tracks",
"fields[playlists]": "curatorName,playlistType,name,artwork,url",
"include[library-songs]": "catalog,artists,albums",
"fields[catalog]": "artistUrl,albumUrl",
"fields[songs]": "artistUrl,albumUrl"
}
var playlistId = ''
try {
function getPlaylist(id, params, isLibrary){
if (isLibrary){
return app.mk.api.library.playlist(id, params)
} else { return app.mk.api.playlist(id, params)}
}
getPlaylist(id, params, isLibrary).then(res => {
let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
app.mk.queue.append(query)
if (!res.relationships.tracks.next) {
return
} else {
getPlaylistTracks(res.relationships.tracks.next)
}
function getPlaylistTracks(next) {
app.apiCall(app.musicBaseUrl + next, res => {
if (res.id != playlistId) {
return
}
let query = res.data.map(item => new MusicKit.MediaItem(item))
if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
app.mk.queue.append(query)
if (res.next) {
getPlaylistTracks(res.next)
}
})
}
})
} catch (e) {}
})
})
})
}
})
} else {
app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '') app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
} }
})
}, },
async getTypeFromID(kind, id, isLibrary = false, params = {}, params2 = {}) { async getTypeFromID(kind, id, isLibrary = false, params = {}, params2 = {}) {
let a; let a;
@ -2260,6 +2328,16 @@ const app = new Vue({
} }
}, },
queueParentandplayChild(parent, childIndex, item) { queueParentandplayChild(parent, childIndex, item) {
/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
let kind = parent.substring(0, parent.indexOf(":")) let kind = parent.substring(0, parent.indexOf(":"))
let id = parent.substring(parent.indexOf(":") + 1, parent.length) let id = parent.substring(parent.indexOf(":") + 1, parent.length)
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
@ -2278,6 +2356,7 @@ const app = new Vue({
} catch (e) { } catch (e) {
} }
this.mk.clearQueue().then(function (_) { this.mk.clearQueue().then(function (_) {
if (app.mk.shuffleMode == 1){ shuffleArray(query)}
app.mk.queue.append(query) app.mk.queue.append(query)
if (childIndex != -1) { if (childIndex != -1) {
app.mk.changeToMediaAtIndex(childIndex) app.mk.changeToMediaAtIndex(childIndex)
@ -2302,10 +2381,12 @@ const app = new Vue({
app.mk.clearQueue().then(function () { app.mk.clearQueue().then(function () {
if ((app.showingPlaylist && app.showingPlaylist.id == id)) { if ((app.showingPlaylist && app.showingPlaylist.id == id)) {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item)); let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (app.mk.shuffleMode == 1){ shuffleArray(query)}
app.mk.queue.append(query) app.mk.queue.append(query)
} else { } else {
app.getPlaylistFromID(id, true).then(function () { app.getPlaylistFromID(id, true).then(function () {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item)); let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (app.mk.shuffleMode == 1){ shuffleArray(query)}
app.mk.queue.append(query) app.mk.queue.append(query)
}) })
} }
@ -2856,7 +2937,7 @@ const app = new Vue({
} }
}, },
{ {
"icon": "./assets/feather/x-circle.svg", "icon": "./assets/feather/unheart.svg",
"id": "unlove", "id": "unlove",
"name": "Unlove", "name": "Unlove",
"disabled": true, "disabled": true,
@ -2967,6 +3048,11 @@ const app = new Vue({
gain: gain, gain: gain,
peak: peak peak: peak
} }
},
fullscreen(flag){
if (flag){
ipcRenderer.send('setFullScreen', true); app.appMode = 'fullscreen';}
else { ipcRenderer.send('setFullScreen', false); app.appMode = 'player';}
} }
} }

View file

@ -3583,6 +3583,52 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
height: 50vh; height: 50vh;
} }
@media only screen and (max-width: 1023px) {
.display--large {
display: flex !important;
}
}
.display--large {
display: flex;
.slider {
width: 100%;
z-index: 1;
}
.input-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%
}
input[type=range] {
appearance: none;
width: 100%;
height: 4px;
background-color: rgb(200 200 200 / 10%);
border-radius: 2px;
&::-webkit-slider-thumb {
opacity: 1;
transform: scale(0.5);
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 12px;
border-radius: 100%;
background: rgba(236, 234, 234, 0.733);
cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
}
}
}
.background{ .background{
position: absolute; position: absolute;
background-size: cover; background-size: cover;
@ -3612,8 +3658,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyrics-col{ .lyrics-col{
width: 60vh; height: 75vh;
height: 50vh; display: flex;
justify-content: center;
align-content: center;
width: 80%;
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
box-shadow: unset; box-shadow: unset;
@ -3641,6 +3690,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
width: 60vh; width: 60vh;
height: 50vh; height: 50vh;
.queue-title{
opacity: 0.6;
}
.queue-panel > * {
z-index: 3;
}
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
box-shadow: unset; box-shadow: unset;
} }
@ -3723,8 +3780,12 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
font-size: 0.875em; font-size: 0.875em;
} }
.song-name { .song-name {
width: unset !important;
margin-top: 0.15vh; margin-top: 0.15vh;
display: contents; display: -webkit-box;
line-height: 1.2;
text-overflow: ellipsis;
text-align: center;
} }
} }
@ -3775,7 +3836,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
> input[type=range] { input[type=range] {
appearance: none; appearance: none;
width: 100%; width: 100%;
height: 4px; height: 4px;

View file

@ -11,7 +11,7 @@
</div> </div>
<div class="row fs-row"> <div class="row fs-row">
<div class="col artwork-col"> <div class="col artwork-col">
<div class="artwork" @click="app.appMode = 'player'"> <div class="artwork" @click="app.fullscreen(false)">
<mediaitem-artwork <mediaitem-artwork
:size="600" :size="600"
:url="image ?? ''" :url="image ?? ''"
@ -26,7 +26,7 @@
{{ app.mk.nowPlayingItem["attributes"]["name"] }} {{ app.mk.nowPlayingItem["attributes"]["name"] }}
</div> </div>
<div <div
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;"> style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap; margin-top: 0.25vh;">
<div class="item-navigate song-artist" style="display: inline-block;" <div class="item-navigate song-artist" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed(`artist`)"> @click="app.getNowPlayingItemDetailed(`artist`)">
{{ app.mk.nowPlayingItem["attributes"]["artistName"] }} {{ app.mk.nowPlayingItem["attributes"]["artistName"] }}
@ -77,7 +77,13 @@
v-else-if="app.mk.repeatMode == 2"></button> v-else-if="app.mk.repeatMode == 2"></button>
</div> </div>
</div> </div>
<div class="app-chrome-item volume display--large">
<div class="app-chrome-item volume-icon"></div>
<div class="input-container">
<input type="range" class="slider" @wheel="app.volumeWheel" step="0.01" min="0" max="1" v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'">
</div>
</div>
</div> </div>
</template> </template>
</div> </div>

View file

@ -289,7 +289,7 @@
} }
}, },
{ {
"icon": "./assets/feather/x-circle.svg", "icon": "./assets/feather/unheart.svg",
"id": "unlove", "id": "unlove",
"name": "Unlove", "name": "Unlove",
"disabled": true, "disabled": true,
@ -348,19 +348,15 @@
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple) menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
} }
} }
try{ let rating = await app.getRating(self.item)
let rating = await app.getRating(self.item).catch(); if (rating == 0) {
if (rating) { menus.normal.items.find(x => x.id == 'love').disabled = false
if(rating == 0) { menus.normal.items.find(x => x.id == 'dislike').disabled = false
menus.normal.items.find(x => x.id == 'love').disabled = false } else if (rating == 1) {
menus.normal.items.find(x => x.id == 'dislike').disabled = false menus.normal.items.find(x => x.id == 'unlove').disabled = false
}else if(rating == 1) { } else if (rating == -1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
}else if(rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
}
} }
} catch(_){}
CiderContextMenu.Create(event, menus[useMenu]) CiderContextMenu.Create(event, menus[useMenu])
}, },
visibilityChanged: function (isVisible, entry) { visibilityChanged: function (isVisible, entry) {
@ -402,12 +398,81 @@
let item = this.item let item = this.item
let parent = this.parent let parent = this.parent
let childIndex = this.index let childIndex = this.index
console.log(item, parent, childIndex) let kind = (item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')) : (item.type ?? ''));
let id = (item.attributes.playParams ? (item.attributes.playParams.id ?? (item.id ?? '')) : (item.id ?? ''));;
let isLibrary = item.attributes.playParams ? (item.attributes.playParams.isLibrary ?? false) : false;
console.log(item, parent, childIndex, kind, id, isLibrary, kind == "playlists", id.startsWith("p.") || id.startsWith("pl.u"))
app.mk.stop().then(() => {
if (parent != null && childIndex != null) { if (parent != null && childIndex != null) {
app.queueParentandplayChild(parent, childIndex, item); app.queueParentandplayChild(parent, childIndex, item);
} else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))){
/* Randomize array in-place using Durstenfeld shuffle algorithm */
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
app.mk.clearQueue().then(function () {
app.mk.setQueue({[item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id}).then(function () {
app.mk.play().then(function (){
app.mk.clearQueue().then(function (){
var playlistId = id
const params = {
include: "tracks",
platform: "web",
"include[library-playlists]": "catalog,tracks",
"fields[playlists]": "curatorName,playlistType,name,artwork,url",
"include[library-songs]": "catalog,artists,albums",
"fields[catalog]": "artistUrl,albumUrl",
"fields[songs]": "artistUrl,albumUrl"
}
var playlistId = ''
function getPlaylist(id, params, isLibrary){
if (isLibrary){
return app.mk.api.library.playlist(id, params)
} else { return app.mk.api.playlist(id, params)}
}
try {
getPlaylist(id, params, isLibrary).then(res => {
let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
app.mk.queue.append(query)
if (!res.relationships.tracks.next) {
return
} else {
getPlaylistTracks(res.relationships.tracks.next)
}
function getPlaylistTracks(next) {
app.apiCall(app.musicBaseUrl + next, res => {
if (res.id != playlistId) {
return
}
let query = res.data.map(item => new MusicKit.MediaItem(item))
if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
app.mk.queue.append(query)
if (res.next) {
getPlaylistTracks(res.next)
}
})
}
})
} catch (e) {}
})
})
})
})
} else { } else {
app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url) app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)
} }})
} }
} }
}); });

View file

@ -356,7 +356,7 @@
} }
}, },
{ {
"icon": "./assets/feather/x-circle.svg", "icon": "./assets/feather/unheart.svg",
"id": "unlove", "id": "unlove",
"name": "Unlove", "name": "Unlove",
"disabled": true, "disabled": true,

View file

@ -69,7 +69,7 @@
<template v-if="mkReady()"> <template v-if="mkReady()">
<div class="app-playback-controls" @mouseover="chrome.progresshover = true" <div class="app-playback-controls" @mouseover="chrome.progresshover = true"
@mouseleave="chrome.progresshover = false" @contextmenu="nowPlayingContextMenu"> @mouseleave="chrome.progresshover = false" @contextmenu="nowPlayingContextMenu">
<div class="artwork" @click="drawer.open = false; appMode = 'fullscreen'"> <div class="artwork" @click="drawer.open = false; fullscreen(true)">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork> <mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div> </div>
<div class="playback-info"> <div class="playback-info">

View file

@ -1,39 +1,49 @@
module.exports = { module.exports = {
globDirectory: 'src/renderer/', globDirectory: 'src/renderer/',
swDest: 'src/renderer/sw.js', swDest: 'src/renderer/sw.js',
// Define runtime caching rules. // Define runtime caching rules.
runtimeCaching: [{ runtimeCaching: [{
// Match any request that ends with .png, .jpg, .jpeg or .svg. // Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/, urlPattern: /\.(?:png|jpg|jpeg|svg|webp)$/,
// Apply a cache-first strategy. // Apply a cache-first strategy.
handler: 'CacheFirst', handler: 'CacheFirst',
options: { options: {
// Use a custom cache name. // Use a custom cache name.
cacheName: 'imageinternet', cacheName: 'imageinternet',
// Only cache 10 images. // Only cache 10 images.
}, },
}, },
{ {
urlPattern: /https:\/\/is[0-9]-ssl\.mzstatic\.com\/image+/, urlPattern: /https:\/\/amp-api.music.apple.com\/v1\//,
handler: "CacheFirst", handler: 'StaleWhileRevalidate',
}, options: {
{ cacheName: 'amp-api',
urlPattern: /^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/, cacheableResponse: {
handler: "CacheFirst", statuses: [0, 200],
}, },
},
},
{
urlPattern: /https:\/\/is[0-9]-ssl\.mzstatic\.com\/image+/,
handler: "CacheFirst",
},
{
urlPattern: /^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/,
handler: "CacheFirst",
},
], ],
ignoreURLParametersMatching: [ ignoreURLParametersMatching: [
/^utm_/, /^utm_/,
/^fbclid$/, /^fbclid$/,
/^X-Amz-Algorithm/, /^X-Amz-Algorithm/,
/^X-Amz-Date/, /^X-Amz-Date/,
/^X-Amz-SignedHeaders/, /^X-Amz-SignedHeaders/,
/^X-Amz-Expires/, /^X-Amz-Expires/,
/^X-Amz-Credential/, /^X-Amz-Credential/,
/^X-Amz-Signature/, /^X-Amz-Signature/,
] ]
}; };