Merge branch 'main' into typescript
|
@ -1 +0,0 @@
|
||||||
demo.cider.sh
|
|
BIN
docs/Cider.png
Before Width: | Height: | Size: 60 KiB |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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>
|
|
|
@ -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)
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
Before Width: | Height: | Size: 33 KiB |
2
docs/app-win/sortable.min.js
vendored
2
docs/app-win/vuedraggable.umd.min.js
vendored
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>
|
|
|
@ -1,6 +0,0 @@
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
setTimeout(function() {
|
|
||||||
document.querySelector(".cider-splash").setAttribute("hidden", "true");
|
|
||||||
document.querySelector("#app-main").removeAttribute("hidden");
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
Before Width: | Height: | Size: 39 KiB |
|
@ -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",
|
||||||
|
|
6
src/renderer/assets/feather/unheart.svg
Normal 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 |
|
@ -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';}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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/,
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|