This commit is contained in:
booploops 2021-12-20 18:53:26 -08:00
commit 6d04fe69dc
200 changed files with 1389 additions and 20170 deletions

File diff suppressed because it is too large Load diff

31029
src/renderer/apple-hls.js Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 521 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 500 B

View file

@ -0,0 +1,18 @@
<?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>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="white" viewBox="0 0 320 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 d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"/></svg>

After

Width:  |  Height:  |  Size: 511 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 493 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 487 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 384 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 640 B

View file

@ -0,0 +1,47 @@
<?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>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 618 B

View file

@ -0,0 +1,45 @@
<?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>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 710 B

View file

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 1.2 KiB

27811
src/renderer/hlscider.js Normal file

File diff suppressed because it is too large Load diff

1955
src/renderer/index.js Normal file

File diff suppressed because it is too large Load diff

778
src/renderer/index_old.html Normal file
View file

@ -0,0 +1,778 @@
<!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-old.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"
@click="drawertest = !drawertest; lyricon =!lyricon; if(drawertest == true){loadLyrics();}"
></button>
</div>
<div class="app-chrome-item full-height">
<div class="window-controls">
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
<div class="minmax restore" v-if="chrome.maximized" @click="ipcRenderer.send('maximize')"></div>
<div class="minmax" v-else @click="ipcRenderer.send('maximize')"></div>
<div class="close" @click="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"
@click='app.page=`playlist_` + item.id ; showingPlaylist = [];getPlaylistFromID(app.page.substring(9))'>
{{ item.attributes.name }}
</button>
</div>
<transition name="wpfade">
<div class="app-sidebar-content" v-if="chrome.menuOpened">
<button class="app-sidebar-item" @click="chrome.hideUserInfo = !chrome.hideUserInfo">
Toggle Personal Info
</button>
<button class="app-sidebar-item">
About
</button>
<button class="app-sidebar-item">
Discord
</button>
<button class="app-sidebar-item">
Settings
</button>
<button class="app-sidebar-item">
Sign Out
</button>
</div>
</transition>
<div class="app-sidebar-footer">
<input type="range" class="display--small">
<button class="app-sidebar-button" style="width:100%"
@click="chrome.menuOpened = !chrome.menuOpened">
<template v-if="chrome.userinfo.attributes">
<img class="sidebar-user-icon" loading="lazy"
:src="getMediaItemArtwork(chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : '', 26)"
/>
</template>
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.attributes">
<div class="fullname text-overflow-elipsis">{{ chrome.userinfo.attributes.name }}</div>
<div class="handle-text text-overflow-elipsis">@{{ chrome.userinfo.attributes.handle
}}
</div>
</template>
<template v-else>
Sign in
</template>
</div>
<div class="sidebar-user-text" v-else>
Cider
</div>
</button>
</div>
<div class="app-sidebar-notification" v-if="library.songs.downloadState == 1">
<div>Updating your library...</div>
<div>{{ library.songs.meta.progress }} / {{ library.songs.meta.total }}</div>
<div style="width: 100%">
<progress style="width: 80%;" :value="library.songs.meta.progress"
:max="library.songs.meta.total"></progress>
</div>
</div>
</div>
<div id="app-content">
<!-- Playlist / Album page-->
<transition name="wpfade">
<template v-if="page.includes('playlist_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('album_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
</transition>
<!-- 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="getLibrarySongsFull()">
<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..."
@input="searchLibrarySongs"
v-model="library.songs.search"
class="search-input">
</div>
<mediaitem-list-item :item="item"
v-for="item in library.songs.displayListing"></mediaitem-list-item>
</div>
</template>
</transition>
<!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getLibraryAlbums()">
<template v-if="page == 'library-albums'">
<div class="content-inner">
<h1 class="header-text">Albums</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-square-large :item="item"
v-for="item in library.albums.listing"></mediaitem-square-large>
</div>
</template>
</transition>
</div>
<transition name="drawertransition">
<div class="app-drawer" v-if="drawertest">
<lyrics-view v-if="drawertest && lyricon" :time="lyriccurrenttime" :lyrics="lyrics"></lyrics-view>
</div>
</transition>
</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="mediaitem-artwork">
<template v-if="type == 'artists'">
<div class="mediaitem-artwork rounded"
>
<img :src="app.getMediaItemArtwork(url, size)"
class="mediaitem-artwork--img">
</div>
</template>
<template v-else>
<div class="mediaitem-artwork"
>
<img :src="app.getMediaItemArtwork(url, size)"
class="mediaitem-artwork--img">
</div>
</template>
</script>
<!-- Generic Collection of MediaItems -->
<script type="text/x-template" id="collection-view-generic">
<template>
<div class="content-inner">
</div>
</template>
</script>
<!-- Listen Now -->
<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>
<template v-if="recom.attributes.display.kind == 'MusicCoverShelf'">
<mediaitem-scroller-horizontal-large
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else-if="recom.attributes.display.kind == 'MusicSuperHeroShelf'">
</template>
<template v-else>
<mediaitem-scroller-horizontal-sp
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-sp>
</template>
</template>
</div>
</script>
<!-- Album / Playlist View -->
<script type="text/x-template" id="cider-playlist">
<div class="content-inner">
<template v-if="data != [] && data.attributes != []">
<div class="playlist-display row">
<div class="col-auto">
<mediaitem-artwork
:url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : (data.relationships.tracks.data.length > 0 ? data.relationships.tracks.data[0].attributes.artwork.url ?? '':'')"
size="200"
></mediaitem-artwork>
</div>
<div class="col playlist-info">
<div class="playlist-name">{{data.attributes.name ?? (data.attributes.title ?? '') ?? ''}}</div>
<div class="playlist-artist" v-if="data.attributes.artistName">{{data.attributes.artistName ??
''}}
</div>
<div class="playlist-desc"
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.short ?? (data.attributes.editorialNotes.standard ?? '') ) : (data.attributes.description ? (data.attributes.description.short ?? (data.attributes.description.standard ?? '')) : ''))"></div>
</div>
</div>
<mediaitem-list-item :item="item"
v-for="item in data.relationships.tracks.data"></mediaitem-list-item>
<div class="playlist-time">{{app.getTotalTime()}}</div>
</template>
</div>
</script>
<!-- Search -->
<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-large :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-large
:items="search.results.albums.data.limit(10)"></mediaitem-scroller-horizontal-large>
</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-large :item="item"
v-for="item in search.results.artists.data.limit(5)"></mediaitem-square-large>
</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-large :item="item"
v-for="item in search.results.playlists.data.limit(10)"></mediaitem-square-large>
</template>
</template>
</div>
</script>
<script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
<!-- Sidebar Item -->
<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>
<!-- Horizontal MediaItem Scroller -->
<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>
<!-- Horizontal MediaItem Scroller (Large) -->
<script type="text/x-template" id="mediaitem-scroller-horizontal-large">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square-large :item="item"
v-for="item in items"></mediaitem-square-large>
</div>
</template>
</script>
<!-- Horizontal MediaItem Scroller (SP : Special) -->
<script type="text/x-template" id="mediaitem-scroller-horizontal-sp">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square-sp :item="item"
v-for="item in items"></mediaitem-square-sp>
</div>
</template>
</script>
<!-- MediaItem List Item -->
<script type="text/x-template" id="mediaitem-list-item">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-list-item">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="34"
:type="item.type"
></mediaitem-artwork>
</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>
<!-- MediaItem Horizontal Rectangle -->
<script type="text/x-template" id="mediaitem-hrect">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-hrect">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="70"
:type="item.type"
></mediaitem-artwork>
</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>
<!-- MediaItem Square -->
<script type="text/x-template" id="mediaitem-square">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-square">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="300"
:type="item.type"
></mediaitem-artwork>
</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>
<!-- MediaItem Square (Large) -->
<script type="text/x-template" id="mediaitem-square-large">
<template>
<div style="position: relative; display: inline-flex;">
<div @click.self='app.routeView(item)'
class="cd-mediaitem-square-large">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="300"
:type="item.type"
></mediaitem-artwork>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
width: '40px',
height: '40px',} :
{margin: '35px',
width: '120px',
height: '120px',}]"
@click="app.playMediaItem(item)">
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
</svg>
</div>
</div>
<div class="title text-overflow-elipsis" @click='app.routeView(item)'>
{{ item.attributes.name ?? '' }}
</div>
<div class="subtitle text-overflow-elipsis" v-if="item.attributes.artistName">
{{ item.attributes.artistName ?? '' }}
</div>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
width: '40px',
height: '40px',} :
{margin: '35px',
width: '120px',
height: '120px',}]"
@click="app.playMediaItem(item)">
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
</svg>
</div>
</div>
</div>
</template>
</script>
<!-- MediaItem Square SP -->
<script type="text/x-template" id="mediaitem-square-sp">
<template>
<div style="position: relative; display: inline-flex;">
<div @click.self='app.routeView(item)'
class="cd-mediaitem-square-sp"
:style="{'--spcolor' : (item.attributes.artwork.bgColor != null) ? ('#'+item.attributes.artwork.bgColor) : `black`}">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="300"
:type="item.type"
></mediaitem-artwork>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
width: '40px',
height: '40px',} :
{margin: '35px',
width: '120px',
height: '120px',}]"
@click="app.playMediaItem(item)">
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
</svg>
</div>
</div>
<div class="title text-overflow-elipsis"
:style="{'color' : (item.attributes.artwork.textColor1 != null) ? ('#'+item.attributes.artwork.textColor1) : `#eee`}"
style="font-weight: 600">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis"
:style="{'color' : (item.attributes.artwork.textColor1 != null) ? ('#'+item.attributes.artwork.textColor1) : `#eee`}"
style="padding-left: 4px;padding-right: 4px; display: -webkit-box;-webkit-box-orient: vertical; -webkit-line-clamp: 2;white-space: normal;">
{{ (item.attributes.editorialNotes != null) ? item.attributes.editorialNotes.short
:(item.attributes.artistName ?? '') }}
</div>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
width: '40px',
height: '40px',} :
{margin: '35px',
width: '120px',
height: '120px',}]"
@click="app.playMediaItem(item)">
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
</svg>
</div>
</div>
</div>
</template>
</script>
<script type="text/x-template" id="lyrics-view">
<div class="md-body lyric-body">
<template v-if="lyrics">
<template v-for="lyric in lyrics" v-if="lyric.line != 'lrcInstrumental'">
<h3 class="lyric-line" @click="app.seekTo(lyric.startTime, false)"
v-bind:class="{ active: app.getLyricClass(lyric.startTime, lyric.endTime)}">
{{ lyric.line }}
<div class="lyrics-translation" v-if="lyric.translation && lyric.translation != ''">
{{ lyric.translation }}
<div>
</h3>
</template>
<template v-else>
<h3 class="lyric-line" @click="app.seekTo(lyric.startTime, false)" :start="lyric.startTime"
:end="lyric.endTime"
v-bind:class="{ active: app.getLyricClass(lyric.startTime, lyric.endTime)}">
<div class="lyricWaiting">
<div class='WaitingDot1'></div>
<div class='WaitingDot2'></div>
<div class='WaitingDot3'></div>
</div>
</h3>
</template>
</template>
<template v-else>
No Lyrics Available
</template>
</div>
</script>
<script src="https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js"></script>
<script src="index.js?v=1"></script>
</body>
</html>

11
src/renderer/less.js Normal file

File diff suppressed because one or more lines are too long

BIN
src/renderer/logotmp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

3
src/renderer/sortable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2165
src/renderer/style-old.css Normal file

File diff suppressed because it is too large Load diff

2751
src/renderer/style.less Normal file

File diff suppressed because it is too large Load diff

2
src/renderer/sw.js Normal file
View file

@ -0,0 +1,2 @@
if(!self.define){let e,i={};const s=(s,r)=>(s=new URL(s+".js",r).href,i[s]||new Promise((i=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=i,document.head.appendChild(e)}else e=s,importScripts(s),i()})).then((()=>{let e=i[s];if(!e)throw new Error(`Module ${s} didnt register its module`);return e})));self.define=(r,c)=>{const n=e||("document"in self?document.currentScript.src:"")||location.href;if(i[n])return;let o={};const t=e=>s(e,n),a={module:{uri:n},exports:o,require:t};i[n]=Promise.all(r.map((e=>a[e]||t(e)))).then((e=>(c(...e),o)))}}define(["./workbox-962786f2"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.precacheAndRoute([{url:"ameframework.css",revision:"4bcc8646bb5742638fad52b94e231601"},{url:"apple-hls.js",revision:"2b74055662676b0fcc2d4a4bf994a9dc"},{url:"hlscider.js",revision:"cf7f512e83e32694f2c94f904714fe4c"},{url:"index_old.html",revision:"c21f3e9c5b015599d3ab07639f64a7a8"},{url:"index.js",revision:"8591a69fc9c975a063eb264b7447f173"},{url:"less.js",revision:"b6e574e4d680686786a28e7e71a17bbc"},{url:"musickit.js",revision:"211d80891c3336c1795cb83df58d4b63"},{url:"sortable.min.js",revision:"5cbc31ebec32adf60e27b76418e79d93"},{url:"style-old.css",revision:"aea9ea49df13f2deee42b68654aeea06"},{url:"todo.js",revision:"18d49fabcb96de8bd11455877d8eacb6"},{url:"vue-observe-visibility.min.js",revision:"5a52e761f6aa71b4f65a7b458f698b95"},{url:"vue.js",revision:"0a9a4681294d8c5f476687eea6e74842"},{url:"vuedraggable.umd.min.js",revision:"9a84fec5263bb510cee88e1c3b9583cc"}],{ignoreURLParametersMatching:[/^utm_/,/^fbclid$/,/^X-Amz-Algorithm/,/^X-Amz-Date/,/^X-Amz-SignedHeaders/,/^X-Amz-Expires/,/^X-Amz-Credential/,/^X-Amz-Signature/]}),e.registerRoute(/\.(?:png|jpg|jpeg|svg|webp)$/,new e.CacheFirst({cacheName:"imageinternet",plugins:[]}),"GET"),e.registerRoute(/https:\/\/is[0-9]-ssl\.mzstatic\.com\/image+/,new e.CacheFirst,"GET"),e.registerRoute(/^https:\/\/store-\d{3}\.blobstore\.apple\.com\/.{65}\/image+/,new e.CacheFirst,"GET")}));
//# sourceMappingURL=sw.js.map

162
src/renderer/todo.js Normal file
View file

@ -0,0 +1,162 @@
// Apple Music Listen Now Page
// URL : https://amp-api.music.apple.com/v1/me/recommendations?timezone=+00:00
// &with=friendsMix,library,social&art[social-profiles:url]=c
// &name=listen-now&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
// &l=en-gb&platform=web
await app.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",
l:"en-gb",
platform:"web"
},
{
includeResponseMeta: !0,
reload: !0
});
// Browse page
await app.mk.api.groupings("",
{
platform: "web",
name: "music",
l: "en-gb",
"omit[resource:artists]": "relationships",
"include[albums]": "artists",
"include[songs]": "artists",
"include[music-videos]": "artists",
extend: "editorialArtwork,artistUrl",
"fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes",
"art[url]": "f"
});
// Radio page
await app.mk.api.recentRadioStations("",
{l: "en-gb",
"platform": "web",
"art[url]": "f"});
// Recently Added
await app.mk.api.library.recentlyAdded({
"platform": "web",
include: {
"library-albums": ["artists"],
"library-artists": ["catalog"]
},
fields: {
artists: ["url"],
albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url"
},
includeOnly: ["catalog", "artists"],
limit: 25
}, {
reload: !0,
includePagination: !0
})
// Songs
await app.mk.api.library.songs({limit: 100}).then((data)=>{
console.log(data)
})
// Artists
await app.mk.api.library.artists({limit: 100}).then((data)=>{
console.log(data)
})
// Artists
await app.mk.api.library.albums({limit: 100}).then((data)=>{
console.log(data)
})
// Albums
// does not like limit = 100 for some reason
await app.mk.api.library.albums({limit: 50}).then((data)=>{
console.log(data)
})
// Made For You
app.mk.api.recommendations("",{extend: "editorialArtwork,artistUrl"})
// Library with library length
await app.mk.api.library.songs("", {limit: 100}, {includeResponseMeta: !0}).then((data)=>{
console.log(data)
})
// Artist View Top Songs
app.mk.api.artistView("325096253", "top-songs", {}, {view: "top-songs", includeResponseMeta: !0})
// Artist Page Data
app.mkapi("artists", false, "412778295", {
"views": "featured-release,full-albums,appears-on-albums,featured-albums,featured-on-albums,singles,compilation-albums,live-albums,latest-release,top-music-videos,similar-artists,top-songs,playlists,more-to-hear,more-to-see",
"extend": "artistBio,bornOrFormed,editorialArtwork,editorialVideo,isGroup,origin,hero",
"extend[playlists]": "trackCount",
"omit[resource:songs]": "relationships",
"fields[albums]": "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url,trackCount",
"limit[artists:top-songs]": 20,
"art[url]": "f"
}, {includeResponseMeta: !0}).then((data)=>{
console.log(data)
})
// download entire library
var library = []
var downloaded = null;
function downloadChunk () {
if(downloaded == null) {
app.mk.api.library.songs("", {limit: 100}, {includeResponseMeta: !0}).then((response)=>{
processChunk(response)
})
}else{
downloaded.next("", {limit: 100}, {includeResponseMeta: !0}).then((response)=>{
processChunk(response)
})
}
}
function processChunk (response) {
downloaded = response
library = library.concat(downloaded.data)
if (downloaded.meta.total > library.length) {
console.log(`downloading next chunk - ${library.length} songs so far`)
downloadChunk()
} else {
console.log(library)
}
}
//Some Available Functions from MusicKit
// recentPlayed() -> recently played songs ?
// create Artist / Song/ Album stations:
app.mk.setStationQueue({artist:"1258279972"})
app.mk.setStationQueue({song:"1437308307"}) // yes the song id here can be the albumId, but just keep using the song:

View file

@ -0,0 +1,59 @@
<script type="text/x-template" id="animatedartwork-view">
<template v-if="video">
<div class="animated" v-bind:vid="app.hashCode(video).toString()">
<video ref="video" class="animated-artwork-video" loop id="animated-artwork"></video>
</div>
</template>
</script>
<script>
Vue.component('animatedartwork-view', {
template: '#animatedartwork-view',
props: ['video', 'hls'],
mounted() {
if (this.video) {
this.$nextTick(function () {
var config = {
backBufferLength: 0,
enableWebVTT: false,
enableCEA708Captions: false,
emeEnabled: false,
abrEwmaDefaultEstimate: 10000,
testBandwidth: false
};
if (this.hls) {
console.log('detached');
this.hls.detachMedia();
} else {
this.hls = new CiderHls(config);
}
// bind them together
if (this.$refs.video) {
let d = "WIDEVINE_SOFTWARE"
let h = {
initDataTypes: ["cenc", "keyids"],
distinctiveIdentifier: "optional",
persistentState: "required"
}
let p = {
platformInfo: {requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h},
appData: {serviceName: "Apple Music"}
}
this.hls.attachMedia(this.$refs.video);
this.hls.loadSource(this.video);
this.hls.loadLevel = 4;
}
})
}
},
async beforeDestroy() {
if(this.hls) {
await this.hls.destroy();
this.hls = null
console.log('killed')
}
}
});
</script>

View file

@ -0,0 +1,14 @@
<div class="modal-backdrop">
<div class="modal-dialog">
<div class="modal-header">
<button type="button" class="close">&times;</button>
<h4 class="modal-title" v-html="title"></h4>
</div>
<div class="modal-content" v-html="content">
</div>
<div class="modal-footer">
</div>
</div>
</div>

View file

@ -0,0 +1,14 @@
<script type="text/x-template" id="hello-world">
<button @click="sayHello()">Hello world!</button>
</script>
<script>
Vue.component('hello-world', {
template: '#hello-world',
methods: {
sayHello: function () {
alert('Hello world!');
}
}
});
</script>

View file

@ -0,0 +1,20 @@
<script type="text/x-template" id="karaoke-in">
<div class="karaoke-viewer">
<div class="lyric">
<template v-for="segment in lyrics" v-if="segmentInRange(segment.ts, segment.te, segment.x)">
<div class="verse-group active">
<template v-for="(verse, verseIndex) in segment.l"
v-if="verseInRange(segment.ts, segment.te, verse.o)">
<span class="verse verse-active">{{ verse.c }}</span>
</template>
<template v-else>
<span class="verse">{{ verse.c }}</span>
</template>
</div>
</template>
<template v-else>
<div class="verse-group">{{ segment.x }}</div>
</template>
</div>
</div>
</script>

View file

@ -0,0 +1,161 @@
<script type="text/x-template" id="lyrics-view">
<div ref="lyricsview" class="md-body lyric-body">
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<template v-for="(lyric, index) in lyrics" v-if="lyric && lyric.line && lyric.line != 'lrcInstrumental'">
<h3 class="lyric-line" @click="if(lyric.startTime != 9999999) app.seekTo(lyric.startTime, false)"
v-bind:line-index="index.toString()">
<template v-if="richlyrics && richlyrics != [] && richlyrics.length > 0">
<div class="richl" >
<template v-for="verse in getVerseLine(index-1)" >
<span class="verse" :lyricstart="lyric.startTime" :versestart="verse.o" >{{ verse.c }}</span>
</template>
</div>
</template>
<template v-else>
<div class="norm" >
{{ lyric.line }}
</div>
</template>
<div class="lyrics-translation" v-if="lyric.translation && lyric.translation != ''">
{{ lyric.translation }}
</div>
</h3>
</template>
<template v-else>
<h3 class="lyric-line" @click="if(lyric.startTime != 9999999) app.seekTo(lyric.startTime, false)"
:start="lyric.startTime"
:end="lyric.endTime" v-bind:line-index="index.toString()">
<div class="lyricWaiting">
<div class='WaitingDot1'></div>
<div class='WaitingDot2'></div>
<div class='WaitingDot3'></div>
</div>
</h3>
</template>
</template>
<template v-else>
<div class="no-lyrics">
Loading... / Lyrics not found./ Instrumental.
</template>
</div>
</script>
<script>
Vue.component('lyrics-view', {
template: '#lyrics-view',
props: ["time", "lyrics", "richlyrics", "translation", "onindex"],
watch: {
time: function () {
if (app.lyricon && app.drawer.open && this.$refs.lyricsview) {
let currentLine = this.$refs.lyricsview.querySelector(`.lyric-line.active`)
if (currentLine && currentLine.getElementsByClassName('lyricWaiting').length > 0) {
let duration = currentLine.getAttribute("end") - currentLine.getAttribute("start");
let u = (this.time - currentLine.getAttribute("start")) / duration;
if (u < 0.25 && !currentLine.classList.contains('mode1')) {
try {
currentLine.classList.add('mode1');
currentLine.classList.remove('mode3');
currentLine.classList.remove('mode2');
} catch (e) {
}
currentLine.getElementsByClassName('WaitingDot1')[0].style.animation = `dotOpacity ${0.25 * duration}s cubic-bezier(0.42, 0, 0.58, 1) forwards`;
currentLine.getElementsByClassName('WaitingDot2')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot3')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot2')[0].style.opacity = 0.25;
currentLine.getElementsByClassName('WaitingDot3')[0].style.opacity = 0.25;
} else if (u >= 0.25 && u < 0.5 && !currentLine.classList.contains('mode2')) {
try {
currentLine.classList.add('mode2');
currentLine.classList.remove('mode1');
currentLine.classList.remove('mode3');
} catch (e) {
}
currentLine.getElementsByClassName('WaitingDot2')[0].style.animation = `dotOpacity ${0.25 * duration}s cubic-bezier(0.42, 0, 0.58, 1) forwards`;
currentLine.getElementsByClassName('WaitingDot1')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot3')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot1')[0].style.opacity = 1;
currentLine.getElementsByClassName('WaitingDot3')[0].style.opacity = 0.25;
} else if (u >= 0.5 && u < 0.75 && !currentLine.classList.contains('mode3')) {
try {
currentLine.classList.add('mode3');
currentLine.classList.remove('mode1');
currentLine.classList.remove('mode2');
} catch (e) {
}
currentLine.getElementsByClassName('WaitingDot3')[0].style.animation = `dotOpacity ${0.25 * duration}s cubic-bezier(0.42, 0, 0.58, 1) forwards`;
currentLine.getElementsByClassName('WaitingDot1')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot2')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot1')[0].style.opacity = 1;
currentLine.getElementsByClassName('WaitingDot2')[0].style.opacity = 1;
} else if (u >= 0.75 && currentLine.classList.contains('mode3')) {
try {
currentLine.classList.remove('mode1');
currentLine.classList.remove('mode2');
currentLine.classList.remove('mode3');
} catch (e) {
}
currentLine.getElementsByClassName('WaitingDot1')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot2')[0].style.animation = ``;
currentLine.getElementsByClassName('WaitingDot1')[0].style.opacity = 1;
currentLine.getElementsByClassName('WaitingDot2')[0].style.opacity = 1;
}
}
}
this.getActiveLyric();
}
},
methods: {
getActiveLyric() {
// console.log(this.time);
const delayfix = 0.35
const prevLine = app.currentLyricsLine;
for (var i = 0; i < this.lyrics.length; i++) {
if (this.time + delayfix >= this.lyrics[i].startTime && this.time + delayfix <= app.lyrics[i].endTime) {
if (app.currentLyricsLine != i) {
app.currentLyricsLine = i;
if (app.lyricon && app.drawer.open && this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)) {
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).classList.remove("active");
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).classList.add("active")
if (checkIfScrollIsStatic) {
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).scrollIntoView({
behavior: "smooth",
block: "center"
})
}
}
} else if (app.currentLyricsLine == 0 && app.drawer.open) {
if (this.$refs.lyricsview.querySelector(`.lyric-line[line-index="0"]`) && !this.$refs.lyricsview.querySelector(`.lyric-line[line-index="0"]`).classList.contains("active"))
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="0"]`).classList.add("active");
}
break;
}
}
try{
if (app.drawer.open){
try{this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).childNodes.classList.remove("verse-active");} catch(e){}
for (child of this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${app.currentLyricsLine}"]`).querySelectorAll(".verse")){
if (this.time + 0.1 >= child.getAttribute("lyricstart") * 1 + child.getAttribute("versestart") * 1){
child.classList.add("verse-active");
} else {child.classList.remove("verse-active");}}
}
} catch(e){}
},
getActiveVerse(timeStart, timeEnd, verseTime) {
let relativeTime = this.time - timeStart
console.log(this.time,timeEnd,timeStart,relativeTime >= verseTime && relativeTime <= timeEnd - timeStart)
return relativeTime >= verseTime && relativeTime <= timeEnd - timeStart
},
getVerseLine(index) {
if (this.richlyrics[index] != null && this.richlyrics[index].l != null) {
return this.richlyrics[index].l
}
else return []
}
}
});
</script>

View file

@ -0,0 +1,62 @@
<script type="text/x-template" id="mediaitem-artwork">
<template v-if="type == 'artists'">
<div class="mediaitem-artwork rounded" :key="url" :style="{'box-shadow': shadow ? 'var(--mediaItemShadow-Shadow)' : ''}">
<img :src="app.getMediaItemArtwork(url, size)"
class="mediaitem-artwork--img">
</div>
</template>
<template v-else>
<div class="mediaitem-artwork" :key="url" :style="{'box-shadow': shadow ? 'var(--mediaItemShadow-Shadow)' : ''}"
v-observe-visibility="{callback: visibilityChanged}">
<img :src="app.getMediaItemArtwork(url, size)" v-if="isVisible"
class="mediaitem-artwork--img">
<div v-if="video && isVisible" class="animatedartwork-view-box">
<animatedartwork-view :video="video"></animatedartwork-view>
</div>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-artwork', {
template: '#mediaitem-artwork',
props: {
size: {
type: String,
default: '120'
},
url: {
type: String,
default: ''
},
type: {
type: String,
default: ''
},
video: {
type: String,
required: false
},
shadow: {
type: Boolean,
default: false
}
},
data: function () {
return {
isVisible: false
}
},
methods: {
getArtworkStyle() {
return {
width: this.size + 'px',
height: this.size + 'px'
};
},
visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible
}
}
});
</script>

View file

@ -0,0 +1,32 @@
<script type="text/x-template" id="mediaitem-hrect">
<template>
<div @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-hrect">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="70"
:type="item.type"></mediaitem-artwork>
</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>
Vue.component('mediaitem-hrect', {
template: '#mediaitem-hrect',
props: ['item'],
methods: {}
});
</script>

View file

@ -0,0 +1,269 @@
<script type="text/x-template" id="mediaitem-list-item">
<template>
<div v-observe-visibility="{callback: visibilityChanged}"
@contextmenu="contextMenu"
@click="select"
:data-id="item.attributes.playParams.id ?? item.id"
:data-type="item.attributes.playParams.kind ?? item.type"
:data-index="index"
:data-guid="guid"
class="cd-mediaitem-list-item"
:class="{'mediaitem-selected': app.select_hasMediaItem(guid)}">
<template v-if="isVisible">
<div class="isLibrary" v-if="showLibraryStatus == true">
<button @click="addToLibrary()"
v-if="!app.isInLibrary(item.attributes.playParams) && !addedToLibrary">🖤
</button>
<button v-else>❤️</button>
</div>
<div class="artwork" v-if="showArtwork == true">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="50"
:type="item.type"></mediaitem-artwork>
<button class="overlay-play" @click="playTrack()"><%- include("../svg/play.svg") %></button>
</div>
<div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}" @dblclick="playTrack()">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis" style="-webkit-box-orient: horizontal;">
<template v-if="item.attributes.artistName">
<div class="artist item-navigate text-overflow-elipsis"
@click="app.searchAndNavigate(item,'artist')">
{{ item.attributes.artistName }}
</div>
<template v-if="item.attributes.albumName">&nbsp;-&nbsp;</template>
<template v-if="item.attributes.albumName">
<div class="artist item-navigate text-overflow-elipsis"
@click="app.searchAndNavigate(item,'album')">
{{ item.attributes.albumName }}
</div>
</template>
</template>
</div>
</div>
<div class="content-rating" v-if="item.attributes.contentRating" @dblclick="playTrack()">
{{ item.attributes.contentRating }}
</div>
<template v-if="showMetaData == true" @dblclick="playTrack()">
<div class="metainfo">
{{ item.attributes.releaseDate ? new Date(item.attributes.releaseDate).toLocaleDateString()
: "" }}
</div>
<div class="metainfo">
{{ item.attributes.genreNames[0] ?? "" }}
</div>
</template>
<div class="duration" v-if="showDuration" @dblclick="playTrack()">
{{ msToMinSec(item.attributes.durationInMillis ?? 0) }}
</div>
</template>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-list-item', {
template: '#mediaitem-list-item',
data: function () {
return {
isVisible: false,
addedToLibrary: false,
guid: uuidv4()
}
},
props: {
'item': {type: Object, required: true},
'parent': {type: Object, required: false},
'index': {type: Object, required: false, default: -1},
'show-artwork': {type: Boolean, default: true},
'show-library-status': {type: Boolean, default: true},
'show-meta-data': {type: Boolean, default: false},
'show-duration': {type: Boolean, default: true},
},
methods: {
select(e) {
if (e.shiftKey) {
if (this.index != -1) {
if(app.selectedMediaItems.length == 0) {
app.select_selectMediaItem(this.item.attributes.playParams.id ?? this.item.id, this.item.attributes.playParams.kind ?? this.item.type, this.index, this.guid)
}
let allMediaItems = document.querySelectorAll(".cd-mediaitem-list-item[data-index]")
let startIndex = Math.min(...app.selectedMediaItems.map(item => item.index))
let endIndex = Math.max(...app.selectedMediaItems.map(item => item.index))
if (this.index < startIndex) {
for (let i = this.index; i <= endIndex; i++) {
let item = allMediaItems[i]
if (item) {
app.select_selectMediaItem(item.getAttribute("data-id"),
item.getAttribute("data-type"),
item.getAttribute("data-index"),
item.getAttribute("data-guid"))
}
}
} else if (this.index > endIndex) {
for (let i = startIndex; i <= this.index; i++) {
let item = allMediaItems[i]
if (item) {
app.select_selectMediaItem(item.getAttribute("data-id"),
item.getAttribute("data-type"),
item.getAttribute("data-index"),
item.getAttribute("data-guid"))
}
}
} else {
for (let i = startIndex; i <= endIndex; i++) {
let item = allMediaItems[i]
if (item) {
app.select_selectMediaItem(item.getAttribute("data-id"),
item.getAttribute("data-type"),
item.getAttribute("data-index"),
item.getAttribute("data-guid"))
}
}
}
}
} else if (e.ctrlKey) {
if (app.select_hasMediaItem(this.guid)) {
app.select_removeMediaItem(this.guid)
} else {
app.select_selectMediaItem(this.item.attributes.playParams.id ?? this.item.id, this.item.attributes.playParams.kind ?? this.item.type, this.index, this.guid)
}
} else {
if (app.select_hasMediaItem(this.guid)) {
app.selectedMediaItems = []
} else {
app.selectedMediaItems = []
app.select_selectMediaItem(this.item.attributes.playParams.id ?? this.item.id, this.item.attributes.playParams.kind ?? this.item.type, this.index, this.guid)
}
}
},
contextMenu(event) {
let self = this
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
app.selectedMediaItems = []
app.select_selectMediaItem(this.item.attributes.playParams.id ?? this.item.id, this.item.attributes.playParams.kind ?? this.item.type, this.index, this.guid)
} else {
useMenu = "multiple"
}
let menus = {
multiple: {
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
}
}
console.log(itemsToPlay)
app.selectedMediaItems = []
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
}
}
app.selectedMediaItems = []
}
},
]
},
normal: {
items: [
{
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({song: self.item.attributes.playParams.id ?? self.item.id}).then(() => {
app.mk.play()
app.selectedMediaItems = []
})
}
},
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"name": "Go to Artist",
"action": function () {
app.searchAndNavigate(self.item, 'artist')
}
},
{
"name": "Go to Album",
"action": function () {
app.searchAndNavigate(self.item, 'album')
}
},
]
}
}
CiderContextMenu.Create(event, menus[useMenu])
},
visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible
},
addToLibrary() {
let item = this.item
if (item.attributes.playParams.id) {
console.log('adding to library', item.attributes.playParams.id)
app.addToLibrary(item.attributes.playParams.id.toString())
this.addedToLibrary = true
} else if (item.id) {
console.log('adding to library', item.id)
app.addToLibrary(item.id.toString())
this.addedToLibrary = true
}
},
playTrack() {
let item = this.item
let parent = this.parent
let childIndex = this.index
console.log(item,parent,childIndex)
if (parent != null && childIndex != null) {
app.queueParentandplayChild(parent, childIndex, item);
} else {
app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)
}
}
}
});
</script>

View file

@ -0,0 +1,61 @@
<script type="text/x-template" id="mediaitem-mvview-sp">
<template>
<div style="position: relative; display: inline-flex;">
<div @click.self='app.routeView(item)'
class="cd-mediaitem-mvview">
<div class="title-browse-sp bold " @click='app.routeView(item)'>
{{ badge ? badge.designBadge : ""}}
</div>
<div class="title-browse-sp " >
{{ (badge != null && badge.designTag != null) ? badge.designTag : (item.attributes.name ?? '') }}
</div>
<div class="title-browse-sp semibold" v-if="!(badge != null && badge.designTag != null)" >
{{ (item.attributes.artistName ?? (item.attributes.curatorName ?? '')) }}
</div>
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
:video="(item.attributes != null && item.attributes.editorialVideo != null) ? (item.attributes.editorialVideo.motionDetailSquare ? item.attributes.editorialVideo.motionDetailSquare.video : (item.attributes.editorialVideo.motionSquareVideo1x1 ? item.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
:size="imagesize ?? 300"
></mediaitem-artwork>
</div>
<div class="cd-mediaitem-mvview-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '200px',
'margin-left': '250px',
width: '40px',
height: '40px',} :
{margin: '35px', 'margin-left': '95px',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
</div>
</div>
<div class="cd-mediaitem-mvview-overlay" @click.self='app.routeView(item)' tabindex="0">
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '200px',
'margin-left': '250px',
width: '40px',
height: '40px',} :
{margin: '35px', 'margin-left': '95px',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
</div>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-mvview-sp', {
template: '#mediaitem-mvview-sp',
props: ['item', "imagesize","badge"],
methods: {}
});
</script>

View file

@ -0,0 +1,59 @@
<script type="text/x-template" id="mediaitem-mvview">
<template>
<div style="position: relative; display: inline-flex;">
<div @click.self='app.routeView(item)'
class="cd-mediaitem-mvview">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
:video="(item.attributes != null && item.attributes.editorialVideo != null) ? (item.attributes.editorialVideo.motionDetailSquare ? item.attributes.editorialVideo.motionDetailSquare.video : (item.attributes.editorialVideo.motionSquareVideo1x1 ? item.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
:size="imagesize ?? 300"
></mediaitem-artwork>
</div>
<div class="cd-mediaitem-mvview-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
'margin-left': '250px',
width: '40px',
height: '40px',} :
{margin: '35px', 'margin-left': '95px',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
</div>
<div class="title text-overflow-elipsis" @click='app.routeView(item)'>
{{ item.attributes.name ?? '' }}
</div>
<div class="subtitle text-overflow-elipsis item-navigate" v-if="item.attributes.artistName" :style = "{'z-index': ((item.attributes.editorialNotes == null) && item.attributes.artistName) ? '4' : ''}" @click="if(item.attributes.artistName)app.searchAndNavigate(item,'artist')">
{{ item.attributes.artistName ?? '' }}
</div>
</div>
<div class="cd-mediaitem-mvview-overlay" @click.self='app.routeView(item)' tabindex="0">
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px',
'margin-left': '250px',
width: '40px',
height: '40px',} :
{margin: '35px', 'margin-left': '95px',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
</div>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-mvview', {
template: '#mediaitem-mvview',
props: ['item', "imagesize"],
methods: {}
});
</script>

View file

@ -0,0 +1,17 @@
<script type="text/x-template" id="mediaitem-scroller-horizontal-large">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square-large :item="item"
v-for="item in items"></mediaitem-square-large>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-scroller-horizontal-large', {
template: '#mediaitem-scroller-horizontal-large',
props: ['items'],
methods: {}
});
</script>

View file

@ -0,0 +1,23 @@
<script type="text/x-template" id="mediaitem-scroller-horizontal-mvview">
<template>
<div class="cd-hmedia-scroller">
<template v-if="browsesp">
<mediaitem-mvview-sp :item="item ? (item.attributes.kind ? item : ((item.relationships && item.relationships.contents ) ? item.relationships.contents.data[0] : item)) : []" :imagesize="imagesize"
:badge="item.attributes" v-for="item in items"></mediaitem-mvview-sp>
</template>
<template v-else>
<mediaitem-mvview :item="item ? (item.attributes.kind ? item : ((item.relationships && item.relationships.contents ) ? item.relationships.contents.data[0] : item)) : []" :imagesize="imagesize"
v-for="item in items"></mediaitem-mvview>
</template>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-scroller-horizontal-mvview', {
template: '#mediaitem-scroller-horizontal-mvview',
props: ['items',"imagesize","browsesp"],
methods: {}
});
</script>

View file

@ -0,0 +1,16 @@
<script type="text/x-template" id="mediaitem-scroller-horizontal-sp">
<template>
<div class="cd-hmedia-scroller">
<mediaitem-square-sp :item="item"
v-for="item in items"></mediaitem-square-sp>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-scroller-horizontal-sp', {
template: '#mediaitem-scroller-horizontal-sp',
props: ['items'],
methods: {}
});
</script>

View file

@ -0,0 +1,16 @@
<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>
Vue.component('mediaitem-scroller-horizontal', {
template: '#mediaitem-scroller-horizontal',
props: ['items'],
methods: {}
});
</script>

View file

@ -0,0 +1,171 @@
<script type="text/x-template" id="mediaitem-square-large">
<template>
<div ref="main" style="position: relative; display: inline-flex;" @contextmenu="contextMenu">
<div @click.self='app.routeView(item)'
class="cd-mediaitem-square-large" ref="main2">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
:video="(item.attributes != null && item.attributes.editorialVideo != null) ? (item.attributes.editorialVideo.motionDetailSquare ? item.attributes.editorialVideo.motionDetailSquare.video : (item.attributes.editorialVideo.motionSquareVideo1x1 ? item.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
size="300"
:type="item.type"></mediaitem-artwork>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px','position': 'absolute',
width: '40px',
height: '40px',} :
{margin: '35px', 'position': 'absolute',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'position': 'absolute','margin': '140px',
width: '40px', 'margin-left': '10px',
height: '40px',} :
{display: 'none',margin: '35px','position': 'absolute',
width: '120px',
height: '120px',}]" @click="clickContext() ">
<%- include("../svg/more.svg") %>
</div>
</div>
<div class="title text-overflow-elipsis" @click='app.routeView(item)'>
{{ item.attributes.name ?? '' }}
</div>
<div class="subtitle text-overflow-elipsis item-navigate" v-if="item.attributes.artistName" :style = "{'z-index': ((item.attributes.editorialNotes == null) && item.attributes.artistName) ? '4' : ''}" @click="if(item.attributes.artistName)app.searchAndNavigate(item,'artist')">
{{ item.attributes.artistName ?? '' }}
</div>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)' tabindex="0">
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px','position': 'absolute',
width: '40px',
height: '40px',} :
{margin: '35px','position': 'absolute',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'position': 'absolute','margin': '140px',
width: '40px', 'margin-left': '10px',
height: '40px',} :
{display: 'none',margin: '35px','position': 'absolute',
width: '120px',
height: '120px',}]" @click="console.log('as');contextMenu()">
<%- include("../svg/more.svg") %>
</div>
</div>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-square-large', {
template: '#mediaitem-square-large',
props: ['item'],
methods: {
clickContext() {
var evt = document.createEvent('MouseEvent');
var rect = this.$refs.main2.getBoundingClientRect();
evt.initMouseEvent(
"contextmenu",
true /* bubble */, true /* cancelable */,
window, null,
0, 0, rect.x + 100, rect.y +100, /* coordinates */
false, false, false, false, /* modifier keys */
0 /*left*/, null
);
this.$refs.main.dispatchEvent(evt);
}
,contextMenu(event) {
if (!event){event = this.$refs.main} else {console.log(event)}
let self = this
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
app.selectedMediaItems = []
app.select_selectMediaItem(this.item.attributes.playParams.id ?? this.item.id, this.item.attributes.playParams.kind ?? this.item.type, this.index, this.guid)
} else {
useMenu = "multiple"
}
let menus = {
multiple: {
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
}
}
console.log(itemsToPlay)
app.selectedMediaItems = []
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
}
}
app.selectedMediaItems = []
}
},
]
},
normal: {
items: [
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
]
}
}
CiderContextMenu.Create(event, menus[useMenu])
},}
});
</script>

View file

@ -0,0 +1,179 @@
<script type="text/x-template" id="mediaitem-square-sp">
<template>
<div ref="main" style="position: relative; display: inline-flex;" @contextmenu="contextMenu">
<div @click.self='app.routeView(item)'
class="cd-mediaitem-square-sp" ref="main2" :style="{'--spcolor' : (item.attributes.artwork.bgColor != null) ? ('#'+item.attributes.artwork.bgColor) : `black`}">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="300"
:video="(item.attributes != null && item.attributes.editorialVideo != null) ? (item.attributes.editorialVideo.motionDetailSquare ? item.attributes.editorialVideo.motionDetailSquare.video : (item.attributes.editorialVideo.motionSquareVideo1x1 ? item.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
:type="item.type"></mediaitem-artwork>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)'>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px','position': 'absolute',
width: '40px',
height: '40px',} :
{margin: '35px','position': 'absolute',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'position': 'absolute','margin': '140px',
width: '40px', 'margin-left': '10px',
height: '40px',} :
{display: 'none',margin: '35px',
width: '120px',
height: '120px',}]" @click="clickContext()">
<%- include("../svg/more.svg") %>
</div>
</div>
<div class="title text-overflow-elipsis"
:style="{'color' : (item.attributes.artwork.textColor1 != null) ? ('#'+item.attributes.artwork.textColor1) : `#eee`}" style="font-weight: 600">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis "
:class="{'item-navigate': ((item.attributes.editorialNotes == null) && item.attributes.artistName)}"
:style="{ 'z-index': ((item.attributes.editorialNotes == null) && item.attributes.artistName) ? '4' : '' ,'color' : (item.attributes.artwork.textColor1 != null) ? ('#'+item.attributes.artwork.textColor1) : `#eee`}" style="padding-left: 4px;padding-right: 4px; display: -webkit-box;-webkit-box-orient: vertical; -webkit-line-clamp: 2;white-space: normal;"
@click="if((item.attributes.editorialNotes == null) && item.attributes.artistName)app.searchAndNavigate(item,'artist')"
>
{{ (item.attributes.editorialNotes != null) ? item.attributes.editorialNotes.short
:(item.attributes.artistName ?? '') }}
</div>
</div>
<div class="cd-mediaitem-square-large-overlay" @click.self='app.routeView(item)' tabindex="0">
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'margin': '140px','position': 'absolute',
width: '40px',
height: '40px',} :
{margin: '35px','position': 'absolute',
width: '120px',
height: '120px',}]" @click="app.playMediaItem(item)">
<%- include("../svg/play.svg") %>
</div>
<div class="button" style="
border-radius: 50%;
background: rgba(50,50,50,0.7);"
:style="[(!(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('radioStation') && !(item.attributes.playParams ? (item.attributes.playParams.kind ?? (item.type ?? '')): (item.type ?? '')).includes('song')) ? {'position': 'absolute','margin': '140px',
width: '40px', 'margin-left': '10px',
height: '40px',} :
{display: 'none',margin: '35px','position': 'absolute',
width: '120px',
height: '120px',}]" @click="clickContext()">
<%- include("../svg/more.svg") %>
</div>
</div>
</div>
</template>
</script>
<script>
Vue.component('mediaitem-square-sp', {
template: '#mediaitem-square-sp',
props: ['item'],
methods: { clickContext() {
var evt = document.createEvent('MouseEvent');
var rect = this.$refs.main2.getBoundingClientRect();
evt.initMouseEvent(
"contextmenu",
true /* bubble */, true /* cancelable */,
window, null,
0, 0, rect.x + 100, rect.y +100, /* coordinates */
false, false, false, false, /* modifier keys */
0 /*left*/, null
);
this.$refs.main.dispatchEvent(evt);
}
,contextMenu(event) {
if (!event){event = this.$refs.main} else {console.log(event)}
let self = this
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
app.selectedMediaItems = []
app.select_selectMediaItem(this.item.attributes.playParams.id ?? this.item.id, this.item.attributes.playParams.kind ?? this.item.type, this.index, this.guid)
} else {
useMenu = "multiple"
}
let menus = {
multiple: {
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playNext({[kind + "s"]: itemsToPlay[kind]})
}
}
console.log(itemsToPlay)
app.selectedMediaItems = []
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
if (!itemsToPlay[item.kind]) {
itemsToPlay[item.kind] = []
}
itemsToPlay[item.kind].push(item.id)
})
// loop through itemsToPlay
for (let kind in itemsToPlay) {
let ids = itemsToPlay[kind]
if (ids.length > 0) {
app.mk.playLater({[kind + "s"]: itemsToPlay[kind]})
}
}
app.selectedMediaItems = []
}
},
]
},
normal: {
items: [
{
"name": "Play Next",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"name": "Play Later",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
]
}
}
CiderContextMenu.Create(event, menus[useMenu])
},}
});
</script>

View file

@ -0,0 +1,27 @@
<script type="text/x-template" id="mediaitem-square">
<template>
<div tabindex="0" @click="app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)"
class="cd-mediaitem-square">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="300"
:type="item.type"></mediaitem-artwork>
</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>
Vue.component('mediaitem-square', {
template: '#mediaitem-square',
props: ['item'],
methods: {}
});
</script>

View file

@ -0,0 +1,60 @@
<script type="text/x-template" id="queue-item">
<template>
<div v-observe-visibility="{callback: visibilityChanged}"
@contextmenu="contextMenu"
class="cd-mediaitem-list-item">
<template v-if="isVisible">
<div class="artwork">
<mediaitem-artwork
:url="item.attributes.artwork ? item.attributes.artwork.url : ''"
size="34"
:type="item.type"></mediaitem-artwork>
</div>
<div class="info-rect" :style="{'padding-left': '16px'}">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="subtitle text-overflow-elipsis" style="-webkit-box-orient: horizontal;">
<template v-if="item.attributes.artistName" >
<div class="artist item-navigate text-overflow-elipsis" @click="app.searchAndNavigate(item,'artist')">
{{ item.attributes.artistName }}
</div>
<template v-if="item.attributes.albumName">&nbsp;-&nbsp;</template>
<template v-if="item.attributes.albumName">
<div class="artist item-navigate text-overflow-elipsis" @click="app.searchAndNavigate(item,'album')">
{{ item.attributes.albumName }}
</div>
</template>
</template>
</div>
</div>
<div class="duration">
{{ msToMinSec(item.attributes.durationInMillis ?? 0) }}
</div>
</template>
</div>
</template>
</script>
<script>
Vue.component('queue-item', {
template: '#queue-item',
props: ['item'],
data: function () {
return {}
},
methods: {
contextMenu(event) {
let self = this
CiderContextMenu.Create(event, {
items: [{
"name": "Remove from queue",
"action": function () {
}
}]
});
}
}
});
</script>

View file

@ -0,0 +1,137 @@
<script type="text/x-template" id="cider-queue">
<div class="queue-panel">
<div class="row">
<div class="col">
<h3 class="queue-header-text">Queue</h3>
</div>
<div class="col-auto flex-center">
<button class="autoplay" :style="{'background': app.mk.autoplayEnabled ? 'var(--keyColor)' : ''}" @click="app.mk.autoplayEnabled = !app.mk.autoplayEnabled">AUTO</button>
</div>
</div>
<div class="queue-body">
<draggable v-model="queueItems" @start="drag=true" @end="drag=false;move()">
<template v-for="(queueItem, position) in queueItems">
<div v-if="position <= queuePosition" style="display: none;">{{ position }}</div>
<div class="cd-queue-item"
:class="{selected: selectedItems.includes(position)}"
@click="select($event, position)"
@dblclick="playQueueItem(position)" v-else :key="position"
@contextmenu="selected = position;queueContext($event, queueItem.item, position)">
<div class="row">
<div class="col-auto flex-center">
<div class="artwork">
<mediaitem-artwork :url="queueItem.item.attributes.artwork ? queueItem.item.attributes.artwork.url : ''" :size="32"></mediaitem-artwork>
</div>
</div>
<div class="col queue-info">
<div class="queue-title text-overflow-elipsis">{{ queueItem.item.attributes.name }}</div>
<div class="queue-subtitle text-overflow-elipsis">{{ queueItem.item.attributes.albumName }} - {{ queueItem.item.attributes.artistName }}</div>
</div>
</div>
</div>
</template>
</draggable>
</div>
<div class="queue-footer">
<button class="md-btn" style="width:100%;" v-if="queueItems.length > 1" @click="app.mk.clearQueue();updateQueue()">Clear All</button>
</div>
</div>
</script>
<script>
Vue.component('cider-queue', {
template: '#cider-queue',
data: function () {
return {
drag: false,
queuePosition: 0,
queueItems: [],
selected: -1,
selectedItems: []
}
},
mounted() {
this.updateQueue()
},
methods: {
select(e, position) {
if(e.ctrlKey || e.shiftKey) {
if(this.selectedItems.indexOf(position) == -1) {
this.selectedItems.push(position)
} else {
this.selectedItems.splice(this.selectedItems.indexOf(position), 1)
}
} else {
this.selectedItems = [position]
}
},
queueContext(event, item, position) {
let self = this
let useMenu = "single"
if(this.selectedItems.length > 1) {
useMenu = "multiple"
}
let menus = {
single: {
items: [{
"name": "Remove from queue",
"action": function () {
self.queueItems.splice(position, 1)
app.mk.queue._queueItems = self.queueItems;
app.mk.queue._reindex()
}
},
{
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({
song: item.attributes.playParams.id ?? item.id
}).then(() => {
app.mk.play()
})
}
},
]
},
multiple: {
items: [{
"name": `Remove ${self.selectedItems.length} tracks from queue`,
"action": function () {
// add property to items to be removed
self.selectedItems.forEach(function (item) {
self.queueItems[item].remove = true
})
// remove items
self.queueItems = self.queueItems.filter(function (item) {
return !item.remove
})
app.mk.queue._reindex()
self.selectedItems = []
}
}]
}
}
CiderContextMenu.Create(event, menus[useMenu]);
},
playQueueItem(index) {
app.mk.changeToMediaAtIndex(index)
},
updateQueue() {
this.selected = -1
if (app.mk.queue) {
this.queuePosition = app.mk.queue.position;
this.queueItems = app.mk.queue._queueItems;
} else {
this.queuePosition = 0;
this.queueItems = [];
}
},
move() {
this.selected = -1
app.mk.queue._queueItems = this.queueItems;
app.mk.queue._reindex()
}
}
});
</script>

527
src/renderer/views/main.ejs Normal file
View file

@ -0,0 +1,527 @@
<!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/less" type="text/css" href="style.less"/>
<script src="less.js"></script>
<script src="vue.js"></script>
<script src="sortable.min.js"></script>
<script src="vue-observe-visibility.min.js"></script>
<script src="sortable.min.js"></script>
<script src="vuedraggable.umd.min.js"></script>
<link rel="manifest" href="./manifest.json?v=2">
<script src="https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js"></script>
<script src="hlscider.js"></script>
</head>
<body oncontextmenu="return false;" loading="1" platform="<%= env.platform %>">
<div id="app">
<div id="app-main">
<div class="mv-chrome" v-if="chrome.topChromeVisible == false"></div>
<div class="app-chrome" :style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<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" @mouseover="chrome.progresshover = true" @mouseleave="chrome.progresshover = false">
<div class="artwork"></div>
<div class="playback-info">
<div class="song-name" >
{{ mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist " style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;">
<div class="item-navigate song-artist" style="display: inline-block;" @click="app.getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block;" @click="app.getNowPlayingItemDetailed('album')">
{{ (mk.nowPlayingItem["attributes"]["albumName"]) ? (" - " + mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
</div>
<div class="song-progress">
<div class="song-duration" style="justify-content: space-between; height: 1px;" :style="[chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]">
<p style="width: auto">{{ convertToMins(getSongProgress()) }}</p>
<p style="width: auto">{{ convertToMins(mk.currentPlaybackDuration) }}</p>
</div>
<input type="range" step="0.01" min="0" :style="progressBarStyle()"
@input="playerLCD.desiredDuration = $event.target.value;playerLCD.userInteraction = true"
@mouseup="mk.seekToTime($event.target.value);playerLCD.desiredDuration = 0;playerLCD.userInteraction = false"
:max="mk.currentPlaybackDuration" :value="getSongProgress()">
</div>
</div>
<template v-if="mk.nowPlayingItem['attributes']['playParams']">
<div class="actions" v-if="isInLibrary(mk.nowPlayingItem['attributes']['playParams'])">
❤️
</div>
<div class="actions" v-else>🖤</div>
</template>
</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">
<%- include("svg/cast.svg") %>
</button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue" :class="{'active': drawer.panel == 'queue'}" @click="invokeDrawer('queue')"></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small lyrics" :class="{'active': drawer.panel == 'lyrics'}"
@click="invokeDrawer('lyrics')"></button>
</div>
<div class="app-chrome-item full-height">
<div class="window-controls">
<div class="minimize" @click="ipcRenderer.send('minimize')"></div>
<div class="minmax restore" v-if="chrome.maximized" @click="ipcRenderer.send('maximize')">
</div>
<div class="minmax" v-else @click="ipcRenderer.send('maximize')"></div>
<div class="close" @click="ipcRenderer.send('close')"></div>
</div>
</div>
</div>
</div>
<div class="app-navigation" v-cloak>
<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()"
@focus="search.showHints = true"
@blur="setTimeout(()=>{search.showHints = false}, 100)"
v-on:keyup.enter="searchQuery();search.showHints = false"
@change="showSearch();" @input="getSearchHints()" placeholder="Search..."
v-model="search.term"
ref="searchInput"
class="search-input">
</div>
</div>
<div class="search-hints-container" v-if="search.showHints && search.hints.length != 0">
<div class="search-hints">
<button class="search-hint" v-for="hint in search.hints"
@click="search.term = hint;search.showHints = false;searchQuery(hint)">
{{ hint }}
</button>
</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="Recently Added" page="library-recentlyadded"></sidebar-library-item>
<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"
@click='appRoute(`playlist_` + item.id); showingPlaylist = [];getPlaylistFromID(app.page.substring(9))'>
{{ item.attributes.name }}
</button>
</div>
<transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened">
<div class="usermenu-body">
<button class="usermenu-item" @click="chrome.hideUserInfo = !chrome.hideUserInfo">
Toggle Personal Info
</button>
<button class="usermenu-item">
About
</button>
<button class="usermenu-item" @click="window.open('https://discord.gg/CezHYdXHEM')">
Discord
</button>
<button class="usermenu-item" @click="page = 'settings'">
Settings
</button>
<button class="usermenu-item" @click="mk.unauthorize()">
Sign Out
</button>
</div>
</div>
</transition>
<div class="app-sidebar-footer">
<input type="range" class="web-slider display--small" step="0.01" min="0" max="1"
v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'">
<button class="app-sidebar-button" style="width:100%"
:class="{active: chrome.menuOpened}"
@blur="setTimeout(()=>{chrome.menuOpened = false}, 100)"
@click="(chrome.userinfo.id) ? chrome.menuOpened = !chrome.menuOpened : false">
<img class="sidebar-user-icon" loading="lazy"
:src="getMediaItemArtwork(chrome.hideUserInfo ? 'http://localhost:9000/assets/logocut.png' : (chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : ''), 26)"/>
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.id">
<div class="fullname text-overflow-elipsis">{{ chrome.userinfo.attributes.name }}
</div>
<div class="handle-text text-overflow-elipsis">@{{ chrome.userinfo.attributes.handle
}}
</div>
</template>
<template v-else>
<div @click="mk.authorize()">
Sign In
</div>
</template>
</div>
<div class="sidebar-user-text" v-else>
Cider
</div>
</button>
</div>
<div class="app-sidebar-notification" v-if="library.downloadNotification.show">
<div>{{ library.downloadNotification.message }}</div>
<div>{{ library.downloadNotification.progress }} / {{ library.downloadNotification.total }}</div>
<div style="width: 100%">
<progress style="width: 80%;" :value="library.downloadNotification.progress"
:max="library.downloadNotification.total"></progress>
</div>
</div>
</div>
<div id="app-content">
<div id="navigation-bar">
<button class="nav-item" @click="history.back()"><%- include('svg/chevron-left.svg') %></button>
<button class="nav-item" @click="history.forward()"><%- include('svg/chevron-right.svg') %></button>
</div>
<!-- Artist Page -->
<transition name="wpfade">
<template v-if="page == 'artist-page' && artistPage.data.attributes">
<cider-artist :data="artistPage.data"></cider-artist>
</template>
</transition>
<transition name="wpfade">
<%- include('pages/zoo') %>
</transition>
<transition name="wpfade">
<%- include('pages/webview') %>
</transition>
<!-- Collection List -->
<transition name="wpfade">
<template v-if="page == 'collection-list'">
<cider-collection-list :data="collectionList.response" :type="collectionList.type" :title="collectionList.title"></cider-collection-list>
</template>
</transition>
<!-- Playlist / Album page-->
<transition name="wpfade">
<template v-if="page.includes('playlist_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('album_')">
<cider-playlist :data="showingPlaylist"></cider-playlist>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('recordLabel_')">
<cider-recordlabel :data="showingPlaylist"></cider-recordlabel>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('curator_')">
<cider-recordlabel :data="showingPlaylist"></cider-recordlabel>
</template>
</transition>
<!-- Browse -->
<transition v-on:enter="app.getBrowsePage(); console.log('browse')" 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> -->
<cider-browse :data="browsepage"></cider-browse>
</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>
<!-- Settings -->
<transition name="wpfade">
<template v-if="page == 'settings'">
<cider-settings></cider-settings>
</template>
</transition>
<!-- Search -->
<transition name="wpfade">
<template v-if="page == 'search'">
<cider-search :search="search"></cider-search>
</template>
</transition>
<!-- Library - Recently Added -->
<transition name="wpfade" v-on:enter="getLibraryAlbumsFull(null, 0); searchLibraryAlbums(0);">
<%- include('pages/library-recentlyadded') %>');
</transition>
<!-- Library - Songs -->
<transition name="wpfade" v-on:enter="getLibrarySongsFull()">
<%- include('pages/library-songs') %>
</transition>
<!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getLibraryAlbumsFull(null, 1); searchLibraryAlbums(1);">
<%- include('pages/library-albums') %>');
%>
</transition>
<!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getMadeForYou()">
<template v-if="page == 'library-madeforyou'" >
<%- include('pages/madeforyou') %>');
%>
</template>
</transition>
<transition name="wpfade">
<template v-if="page.includes('appleCurator')" >
<cider-applecurator :data="showingPlaylist"></cider-applecurator>
</template>
</transition>
</div>
<transition name="drawertransition">
<div class="app-drawer" v-if="drawer.open">
<lyrics-view v-if="drawer.panel == 'lyrics'" :time="lyriccurrenttime" :lyrics="lyrics" :richlyrics="richlyrics"></lyrics-view>
<cider-queue ref="queue" v-if="drawer.panel == 'queue'"></cider-queue>
</div>
</transition>
</div>
</div>
<transition name="wpfade">
<img v-show="chrome.artworkReady" @load="chrome.artworkReady = true" class="bg-artwork"
>
</transition>
<transition name="wpfade">
<div class="bg-artwork--placeholder" v-else></div>
</transition>
<div id="apple-music-video-container">
<div id="apple-music-video-player-controls">
<div id="player-exit" title="Close" onclick="app.exitMV()">
<svg fill="white" xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21"
aria-role="presentation" focusable="false">
<path
d="M10.5 21C4.724 21 0 16.275 0 10.5S4.724 0 10.5 0 21 4.725 21 10.5 16.276 21 10.5 21zm-3.543-5.967a.96.96 0 00.693-.295l2.837-2.842 2.85 2.842c.167.167.41.295.693.295.552 0 1.001-.461 1.001-1.012 0-.281-.115-.512-.295-.704L11.899 10.5l2.85-2.855a.875.875 0 00.295-.68c0-.55-.45-.998-1.001-.998a.871.871 0 00-.668.295l-2.888 2.855-2.862-2.843a.891.891 0 00-.668-.281.99.99 0 00-1.001.986c0 .269.116.512.295.678L9.088 10.5l-2.837 2.843a.926.926 0 00-.295.678c0 .551.45 1.012 1.001 1.012z"
fill-rule="nonzero"/>
</svg>
</div>
<div id="captions">{{((lyricon) ? ((lyrics.length > 0 ) ?
lyrics[currentLyricsLine].line.replace('lrcInstrumental','') : "") : '') + ((lyricon) ? ((lyrics.length > 0 ) ?
(lyrics[currentLyricsLine].translation ? ('\n\r' + lyrics[currentLyricsLine].translation) : ""): "") : '')}}
</div>
<div id="player-pip" @click="document.querySelector('video#apple-music-video-player').requestPictureInPicture()"
title="Picture-in-Picture">
<%- include("svg/fullscreen.svg") %>
</div>
<div id="player-fullscreen" @click="document.querySelector('video#apple-music-video-player').requestFullscreen()"
title="Fullscreen">
<%- include("svg/fullscreen.svg") %>
</div>
</div>
<div id="apple-music-video-player"></div>
</div>
</div>
<%- include("components/mediaitem-artwork"); %>
<!-- Generic Collection of MediaItems -->
<script type="text/x-template" id="collection-view-generic">
<template>
<div class="content-inner">
</div>
</template>
</script>
<!-- Browse -->
<%- include('pages/browse') %>
<!-- Settings -->
<%- include('pages/settings') %>
<!-- Listen Now -->
<%- include('pages/listen_now') %>
<!-- Playlists / Albums -->
<%- include('pages/cider-playlist') %>
<!-- Record Label -->
<%- include('pages/recordLabel') %>
<!-- Collection List -->
<%- include('pages/collection-list') %>
<!-- Apple Curator -->
<%- include('pages/apple-curator') %>
<!-- Artist Page -->
<%- include('pages/artist') %>
<!-- Search -->
<%- include('pages/search') %>
<script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
<!-- Sidebar Item -->
<script type="text/x-template" id="sidebar-library-item">
<button class="app-sidebar-item"
:class="$parent.getSidebarItemClass(page)" @click="app.appRoute(page)">
{{ name }}
</button>
</script>
<!-- Queue -->
<%- include('components/queue') %>
<!-- Queue Item -->
<%- include('components/queue-item') %>
<!-- Horizontal MediaItem Scroller -->
<%- include('components/mediaitem-scroller-horizontal') %>
<!-- Horizontal MediaItem Scroller (Large) -->
<%- include('components/mediaitem-scroller-horizontal-large') %>
<!-- Horizontal MediaItem Scroller (SP : Special) -->
<%- include('components/mediaitem-scroller-horizontal-sp') %>
<!-- Horizontal MediaItem Scroller (MV) -->
<%- include('components/mediaitem-scroller-horizontal-mvview') %>
<!-- MediaItem List Item -->
<%- include('components/mediaitem-list-item') %>
<!-- MediaItem Horizontal Rectangle -->
<%- include('components/mediaitem-hrect') %>
<!-- MediaItem Square -->
<%- include('components/mediaitem-square') %>
<!-- MediaItem Square (Large) -->
<%- include('components/mediaitem-square-large') %>
<!-- MediaItem Square SP -->
<%- include('components/mediaitem-square-sp') %>
<!-- MediaItem MusicVideo -->
<%- include('components/mediaitem-mvview') %>
<!-- MediaItem MusicVideo SP -->
<%- include('components/mediaitem-mvview-sp') %>
<!-- Animated Artwork View -->
<%- include('components/animatedartwork-view') %>
<!-- Lyrics View -->
<%- include('components/lyrics-view') %>
<script src="musickit.js?v=1"></script>
<script>
if (typeof MusicKit == 'undefined') {
document.write(unescape("%3Cscript src='https://js-cdn.music.apple.com/musickit/v2/amp/musickit.js' type='text/javascript'%3E%3C/script%3E"));
}
</script>
<script src="index.js?v=1"></script>
</body>
</html>

View file

@ -0,0 +1,19 @@
Major thanks to the Cider Development Team and all of our contributors.
<p>"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
All Rights
Reserved.</p>
cryptofyre - Developer - https://github.com/cryptofyre
Core - Developer - https://github.com/coredev-uk
Quacksire - Developer - https://github.com/child-duckling
booploops - Developer - https://github.com/booploops
vapormusic - Developer - https://github.com/vapormusic
Void - Social Communications Team - https://twitter.com/MoonyVoid
NoseySG - Social Communications Team - https://twitter.com/noah_grose
<img class="md-contributors"
onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
src="https://contrib.rocks/image?repo=ciderapp/Cider"/>

View file

@ -0,0 +1,37 @@
<script type="text/x-template" id="cider-applecurator">
<div class="content-inner">
<h1 class="header-text">{{ data.attributes.shortName ?? data.attributes.name}}</h1>
<template v-if="data.relationships && data.relationships.grouping">
<template v-for="(recom,index) in data.relationships.grouping.data[0].relationships.tabs.data[0].relationships.children.data">
<div class="row">
<div class="col" v-if="recom.attributes.name != 'Chart Set'">
<h3>{{ recom.attributes.name ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="index != 0 && recom.relationships && ((recom.relationships.children && recom.relationships.children.data.length > 10) || (recom.relationships.contents && recom.relationships.contents.data.length > 10))">
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.children ? recom.relationships.children : recom.relationships.contents, recom.attributes.name ?? '', 'listen_now')" >See All</button>
</div>
</div>
<template v-if="recom.relationships && ((recom.relationships.children && recom.relationships.children.data) || (recom.relationships.contents && recom.relationships.contents.data))">
<template v-if="(recom.attributes.name && recom.attributes.name.includes('ideo')) || index === 0">
<mediaitem-scroller-horizontal-mvview :imagesize="800" :browsesp="index == 0"
:items="recom.relationships.children ? recom.relationships.children.data.limit(10) : recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
<template v-else-if="recom.attributes.name == 'Chart Set'">
</template>
<template v-else>
<mediaitem-scroller-horizontal-large
:items="recom.relationships.children ? recom.relationships.children.data.limit(10) : recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
</template>
</template>
</template>
</div>
</script>
<script>
Vue.component('cider-applecurator', {
template: "#cider-applecurator",
props: ["data"]
})
</script>

View file

@ -0,0 +1,180 @@
<script type="text/x-template" id="cider-artist">
<div class="content-inner artist-page">
<div class="artist-header" :style="getArtistPalette(data)">
<animatedartwork-view
v-if="data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)"
:video="data.attributes.editorialVideo.motionArtistWide16x9.video ?? (data.attributes.editorialVideo.motionArtistFullscreen16x9.video ?? '')">
</animatedartwork-view>
<div class="row">
<div class="col-sm" style="width: auto;">
<div class="artist-image" v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9))">
<mediaitem-artwork
:shadow="true"
:url="data.attributes.artwork ? data.attributes.artwork.url : ''"
size="220" type="artists"></mediaitem-artwork>
<button class="overlay-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
app.mk.play()
})">
<%- include("../svg/play.svg") %>
</button>
</div>
</div>
<div class="col flex-center artist-title"
:class="{'artist-animation-on': (data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) }"
>
<button class="artist-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
app.mk.play()
})"><%- include("../svg/play.svg") %></button>
<h1>{{ data.attributes.name }}</h1>
</div>
</div>
<button class="artist-more" @click="artistMenu">
<div style=" margin-top: -1px;
margin-left: -5px;
width: 36px;
height: 36px;">
<%- include("../svg/more.svg") %>
</div>
</button>
</div>
<div class="artist-body">
<div class="row well">
<div class="col">
<div class="row">
<div class="col-auto" v-if="data.views['latest-release'].data.length != 0">
<h3>Latest Release</h3>
<div style="width: auto;margin: 0 auto;">
<mediaitem-square-sp v-for="song in data.views['latest-release'].data"
:item="song">
</mediaitem-square-sp>
</div>
</div>
<div class="col" v-if="data.views['top-songs']">
<div class="row">
<div class="col" style="padding:0px;">
<h3>Top Songs</h3>
</div>
<div class="col-auto flex-center" v-if="data.views['top-songs'].data.length >= 10" style="padding:0px;">
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - Top Songs', 'top-songs')">See All</button>
</div>
</div>
<mediaitem-list-item
v-for="(song, index) in data.views['top-songs'].data.limit(topSongsExpanded ? 10 : 5)"
:index="index"
:item="song"></mediaitem-list-item>
<button class="showmoreless"
@click="topSongsExpanded = !topSongsExpanded">
<template v-if="!topSongsExpanded">
Show more
</template>
<template v-else>
Show less
</template>
</button>
</div>
</div>
</div>
</div>
<div class="row well">
<div class="col">
<template v-for="(view) in data.meta.views.order"
v-if="(data.views[view].data.length != 0) && (view != 'latest-release') && (view != 'top-songs')">
<div class="row">
<div class="col">
<h3>{{ data.views[view].attributes.title ?
data.views[view].attributes.title : "???"}}
</h3>
</div>
<div class="col-auto flex-center" v-if="data.views[view].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - ' + data.views[view].attributes.title, view)">See All</button>
</div>
</div>
<template v-if="!((data.views[view].attributes.title ?
data.views[view].attributes.title : '???').includes('Video') || (data.views[view].attributes.title ?
data.views[view].attributes.title : '???').includes('More To See'))">
<mediaitem-scroller-horizontal-large :items="data.views[view].data.limit(10)">
</mediaitem-scroller-horizontal-large>
</template>
<template v-else>
<mediaitem-scroller-horizontal-mvview
:items="data.views[view].data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
</template>
<div class="row">
<div class="col" v-if="data.attributes.artistBio">
<h3>About {{ data.attributes.name }}</h3>
<p v-html="data.attributes.artistBio"></p>
</div>
<div class="col">
<div v-if="data.attributes.origin">
<h3>{{ data.attributes.isGroup ? "Origin" : "Hometown" }}</h3>
{{ data.attributes.origin }}
</div>
<div v-if="data.attributes.bornOrFormed">
<h3>{{ data.attributes.isGroup ? "Formed" : "Born" }}</h3>
{{ data.attributes.bornOrFormed }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('cider-artist', {
template: "#cider-artist",
props: ['data'],
data: function () {
return {
topSongsExpanded: false
}
},
methods: {
artistMenu (event) {
let self = this
CiderContextMenu.Create(event, {
items: [
{
name: "Play Artist Radio",
action: ()=>{
app.mk.setStationQueue({artist:self.data.id}).then(()=>{
app.mk.play()
})
}
},
{
name: "Follow Artist",
action: ()=>{}
},
{
name: "Share",
action: ()=>{}
}
]
})
},
getArtistPalette(artist) {
if (artist["attributes"]["artwork"]) {
return {
"background": "#" + artist["attributes"]["artwork"]["bgColor"],
"color": "#" + artist["attributes"]["artwork"]["textColor1"],
}
} else {
return {
"background": "#000000",
"color": "#ffffff",
}
}
},
getTopResult() {
if (this.search.results["meta"]) {
return this.search.results[this.search.results.meta.results.order[0]]["data"][0]
} else {
return false;
}
}
}
})
</script>

View file

@ -0,0 +1,37 @@
<script type="text/x-template" id="cider-browse">
<div class="content-inner">
<h1 class="header-text">Browse</h1>
<template v-if="data.relationships && data.relationships.tabs">
<template v-for="(recom,index) in data.relationships.tabs.data[0].relationships.children.data">
<div class="row">
<div class="col" v-if="recom.attributes.name != 'Chart Set'">
<h3>{{ recom.attributes.name ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="index != 0 && recom.relationships && ((recom.relationships.children && recom.relationships.children.data.length > 10) || (recom.relationships.contents && recom.relationships.contents.data.length > 10))">
<button class="cd-btn-seeall" @click="app.showCollection(recom.relationships.children ? recom.relationships.children : recom.relationships.contents, recom.attributes.name ?? '', 'listen_now')" >See All</button>
</div>
</div>
<template v-if="recom.relationships && ((recom.relationships.children && recom.relationships.children.data) || (recom.relationships.contents && recom.relationships.contents.data))">
<template v-if="(['385']).includes(recom.attributes.editorialElementKind) || index === 0">
<mediaitem-scroller-horizontal-mvview :imagesize="800" :browsesp="index == 0"
:items="recom.relationships.children ? recom.relationships.children.data.limit(10) : recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
<template v-else-if="recom.attributes.name == 'Chart Set'">
<!-- ignored -->
</template>
<template v-else>
<mediaitem-scroller-horizontal-large
:items="recom.relationships.children ? recom.relationships.children.data.limit(10) : recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
</template>
</template>
</template>
</div>
</script>
<script>
Vue.component('cider-browse', {
template: "#cider-browse",
props: ["data"]
})
</script>

View file

@ -0,0 +1,90 @@
<script type="text/x-template" id="cider-playlist">
<template v-if="data != [] && data.attributes != null">
<div class="content-inner playlist-page" :style="{'--bgColor': (data.attributes.artwork != null && data.attributes.artwork['bgColor'] != null) ? ('#' + data.attributes.artwork.bgColor) : ''}">
<div class="playlist-display row"
:style="{
background: (data.attributes.artwork != null && data.attributes.artwork['bgColor'] != null) ? ('#' + data.attributes.artwork.bgColor) : '',
color: (data.attributes.artwork != null && data.attributes.artwork['textColor1'] != null) ? ('#' + data.attributes.artwork.textColor1) : ''
}">
<div class="col-auto flex-center">
<div style="width: 260px;height:260px;">
<mediaitem-artwork
:url="(data.attributes != null && data.attributes.artwork && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0) ? data.relationships.tracks.data[0].attributes.artwork.url ?? '':'')"
:video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
size="260"
></mediaitem-artwork>
</div>
</div>
<div class="col playlist-info">
<template v-if="!editorialNotesExpanded">
<div>
<div class="playlist-name">{{data.attributes ? (data.attributes.name ??
(data.attributes.title ?? '') ?? '') : ''}}
</div>
<div class="playlist-artist item-navigate" v-if="data.attributes && data.attributes.artistName" @click="if(data.attributes && data.attributes.artistName){ app.searchAndNavigate(data,'artist')}">
{{data.attributes ? (data.attributes.artistName ?? '') :''}}
</div>
<div class="playlist-desc" v-if="data.attributes.editorialNotes">
<div class="content"
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.standard ?? (data.attributes.editorialNotes.short ?? '') ) : (data.attributes.description ? (data.attributes.description.standard ?? (data.attributes.description.short ?? '')) : ''))"></div>
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">
More
</button>
</div>
</div>
</template>
<template v-if="editorialNotesExpanded">
<div class="playlist-desc-expanded">
<div class="content"
v-html="((data.attributes.editorialNotes) ? (data.attributes.editorialNotes.standard ?? (data.attributes.editorialNotes.short ?? '') ) : (data.attributes.description ? (data.attributes.description.standard ?? (data.attributes.description.short ?? '')) : ''))"></div>
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">Less
</button>
</div>
</template>
<div class="playlist-controls">
<button class="wr-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 0;app.playMediaItemById(data.attributes.playParams.id ?? data.id, data.attributes.playParams.kind ?? data.type, data.attributes.playParams.isLibrary ?? false, data.attributes.url)">
Play
</button>
<button class="wr-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 1;app.playMediaItemById(data.attributes.playParams.id ?? data.id, data.attributes.playParams.kind ?? data.type, data.attributes.playParams.isLibrary ?? false, data.attributes.url)">
Shuffle
</button>
</div>
</div>
</div>
<div class="playlist-body">
<div class="well">
<mediaitem-list-item :item="item" :parent="getItemParent(data)" :index="index"
v-for="(item,index) in data.relationships.tracks.data"></mediaitem-list-item>
</div>
<div class="playlist-time">
{{data.attributes.releaseDate}}
</div>
<div class="playlist-time item-navigate" @click="app.searchAndNavigate(data,'recordLabel') " style="width: 50%;">
{{data.attributes.copyright}}
</div>
<div class="playlist-time">{{app.getTotalTime()}}</div>
</div>
</div>
</template>
</script>
<script>
Vue.component('cider-playlist', {
template: "#cider-playlist",
props: ["data"],
data: function () {
return {
editorialNotesExpanded: false,
}
},
methods: {
getItemParent: function (data) {
kind = data.attributes.playParams.kind;
id = data.attributes.playParams.id;
return `${kind}:${id}`
}
}
})
</script>

View file

@ -0,0 +1,124 @@
<script type="text/x-template" id="cider-collection-list">
<div class="content-inner collection-page">
<h3 class="header-text" v-observe-visibility="{callback: headerVisibility}">{{ title }}</h3>
<div v-if="data['data'] != 'null'" class="well">
<template v-for="(item, key) in data.data">
<template v-if="item.type == 'artists'">
<mediaitem-square-large :item="item"></mediaitem-square-large>
</template>
<template v-else>
<mediaitem-list-item
v-if="item.attributes.playParams.kind == 'song'"
:index="key"
:item="item"></mediaitem-list-item>
<mediaitem-mvview v-else-if="item.attributes.playParams.kind == 'musicVideo'" :item="item"></mediaitem-mvview>
<mediaitem-square-large v-else :item="item"></mediaitem-square-large>
</template>
</template>
<button v-if="triggerEnabled" style="opacity:0;height: 32px;" v-observe-visibility="{callback: visibilityChanged}">Show More</button>
</div>
<transition name="fabfade">
<button class="top-fab" v-show="showFab" @click="scrollToTop()">
<%- include("../svg/arrow-up.svg") %>
</button>
</transition>
</div>
</script>
<script>
Vue.component('cider-collection-list', {
template: "#cider-collection-list",
props: {
data: {
type: Object,
required: true
},
title: {
type: String,
required: false
},
type: {
type: String,
required: false,
default: "artists"
}
},
data: function () {
return {
triggerEnabled: true,
canSeeTrigger: false,
showFab: false
}
},
methods: {
scrollToTop() {
let target = document.querySelector(".header-text")
target.scrollIntoView({behavior: "smooth", block: "start", inline: "nearest"})
},
getNext() {
// if this.data.next is not null, then we can run this.data.next() and concat to this.data.data to get the next page
switch(this.type) {
default:
case "artists":
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
this.data.next().then(data => {
console.log(data);
this.data.next = data.next;
this.data.data = this.data.data.concat(data.data);
this.triggerEnabled = true;
});
}else{
console.log("No next page");
this.triggerEnabled = false;
}
break;
case "search":
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
this.data.next().then(data => {
console.log(data);
this.data.next = data[this.data.groups].next;
this.data.data = this.data.data.concat(data[this.data.groups].data.data);
this.triggerEnabled = true;
});
}else{
console.log("No next page");
this.triggerEnabled = false;
}
break;
case "listen_now":
case "curator":
if (this.data.next && this.triggerEnabled) {
this.triggerEnabled = false;
app.mk.api.v3.music(this.data.next).then(data => {
console.log(data);
this.data.next = data.data.next;
this.data.data = this.data.data.concat(data.data.data);
this.triggerEnabled = true;
});
}else{
console.log("No next page");
this.triggerEnabled = false;
}
break;
}
},
headerVisibility: function (isVisible, entry) {
if(isVisible) {
this.showFab = false;
}else{
this.showFab = true;
}
},
visibilityChanged: function (isVisible, entry) {
if(isVisible) {
this.canSeeTrigger = true;
this.getNext();
}else{
this.canSeeTrigger = false;
}
}
}
})
</script>

View file

@ -0,0 +1,56 @@
<template v-if="page == 'library-albums'">
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px;">
<h1 class="header-text">Albums</h1>
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 1)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<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..."
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="library.albums.sorting[1]" @change="searchLibraryAlbums(1)">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.albums.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[1]" @change="searchLibraryAlbums(1)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.viewAs">
<optgroup label="View As">
<option value="covers">Cover Art</option>
<option value="list">List</option>
</optgroup>
</select>
</div>
</div>
</div>
</div>
<mediaitem-square-large v-if="library.albums.viewAs == 'covers'" :item="item" v-for="item in library.albums.displayListing">
</mediaitem-square-large>
<mediaitem-list-item v-if="library.albums.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
</mediaitem-list-item>
</div>
</template>

View file

@ -0,0 +1,49 @@
<template v-if="page == 'library-recentlyadded'">
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px;">
<h1 class="header-text">Recently Added</h1>
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 0)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<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..."
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[0]" @change="searchLibraryAlbums(0)">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.viewAs">
<optgroup label="View As">
<option value="covers">Cover Art</option>
<option value="list">List</option>
</optgroup>
</select>
</div>
</div>
</div>
</div>
<mediaitem-square-large v-if="library.albums.viewAs == 'covers'" :item="item" v-for="item in library.albums.displayListing">
</mediaitem-square-large>
<mediaitem-list-item v-if="library.albums.viewAs == 'list'" :show-duration="false" :show-meta-data="true" :show-library-status="false" :item="item" v-for="item in library.albums.displayListing">
</mediaitem-list-item>
</div>
</template>

View file

@ -0,0 +1,46 @@
<template v-if="page == 'library-songs'">
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px;">
<h1 class="header-text">Songs</h1>
</div>
<div class="col-auto">
<button v-if="library.songs.downloadState == 2" @click="getLibrarySongsFull(true)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<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..."
@input="searchLibrarySongs"
v-model="library.songs.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="library.songs.sorting" @change="searchLibrarySongs()">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.songs.sortOrder" @change="searchLibrarySongs()">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
</div>
</div>
</div>
</div>
<div v-if="library.songs.downloadState == 3">Library contains no songs.</div>
<mediaitem-list-item :item="item" :parent="'librarysongs'" :index="index" :show-meta-data="true" :show-library-status="false" v-for="(item, index) in library.songs.displayListing"></mediaitem-list-item>
</div>
</template>

View file

@ -0,0 +1,32 @@
<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" @click="app.showCollection(recom.relationships.contents, recom.attributes.title ? recom.attributes.title.stringForDisplay : '', 'listen_now')" >See All</button>
</div>
</div>
<template v-if="recom.attributes.display.kind == 'MusicCoverShelf'">
<mediaitem-scroller-horizontal-large
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else-if="recom.attributes.display.kind == 'MusicSuperHeroShelf'">
</template>
<template v-else>
<mediaitem-scroller-horizontal-sp
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-sp>
</template>
</template>
</div>
</script>
<script>
Vue.component('cider-listen-now', {
template: "#cider-listen-now",
props: ["data"]
})
</script>

View file

@ -0,0 +1,11 @@
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px">
<h1 class="header-text">Made For You</h1>
</div>
</div>
<div class="madeforyou-body">
<mediaitem-square-large :item="item" v-for="item in madeforyou.data">
</mediaitem-square-large>
</div>
</div>

View file

View file

@ -0,0 +1,101 @@
<script type="text/x-template" id="cider-recordlabel">
<div class="content-inner artist-page">
<div class="artist-header" :style="getArtistPalette(data)">
<div class="row">
<div class="col-sm" style="width: auto;">
<div class="artist-image">
<mediaitem-artwork
:shadow="true"
:url="data.attributes.artwork ? data.attributes.artwork.url : ''"
size="220" type="artists"></mediaitem-artwork>
</div>
</div>
<div class="col flex-center"
>
<h1>{{ data.attributes.name }}</h1>
</div>
</div>
</div>
<div class="artist-body">
<div v-if = "app.showingPlaylist.attributes.description">
<div class="row">
<h3>About </h3>
</div>
<div class="row">
<div>{{ app.showingPlaylist.attributes.description.standard }}</div>
</div>
</div>
<template v-if="data.views && data.views['latest-releases']">
<div class="row">
<div class="col">
<h3>{{ data.views["latest-releases"].attributes.title ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="data.views['latest-releases'].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showRecordLabelView(data.id, data.attributes.name + ' - Latest Releases', 'latest-releases')">See All</button>
</div>
</div>
<mediaitem-square-large :item="item" v-for="item in data.views['latest-releases'].data">
</mediaitem-square-large>
</template>
<template v-if="data.views && data.views['top-releases']">
<div class="row">
<div class="col">
<h3>{{ data.views["top-releases"].attributes.title ?? ""}}</h3>
</div>
<div class="col-auto flex-center" v-if="data.views['top-releases'].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showRecordLabelView(data.id, data.attributes.name + ' - Top Releases', 'top-releases')">See All</button>
</div>
</div>
<mediaitem-square-large :item="item" v-for="item in data.views['top-releases'].data">
</mediaitem-square-large>
</template>
<template v-if="data.relationships && data.relationships.playlists && data.relationships.playlists.data.length > 0">
<div class="row">
<div class="col">
<h3>Playlists</h3>
</div>
<div class="col-auto flex-center" v-if="data.relationships.playlists.data.length >= 5">
<button class="cd-btn-seeall" @click="app.showCollection(data.relationships.playlists, data.attributes.name + ' - Playlists', 'curator')">See All</button>
</div>
</div>
<mediaitem-square-large :item="item" v-for="item in data.relationships.playlists.data.limit(5)">
</mediaitem-square-large>
</template>
</div>
</div>
</script>
<script>
Vue.component('cider-recordlabel', {
template: "#cider-recordlabel",
props: ['data'],
data: function () {
return {
topSongsExpanded: false
}
},
methods: {
getArtistPalette(artist) {
if (artist["attributes"]["artwork"]) {
return {
"background": "#" + artist["attributes"]["artwork"]["bgColor"],
"color": "#" + artist["attributes"]["artwork"]["textColor1"],
}
} else {
return {
"background": "#000000",
"color": "#ffffff",
}
}
},
getTopResult() {
if (this.search.results["meta"]) {
return this.search.results[this.search.results.meta.results.order[0]]["data"][0]
} else {
return false;
}
}
}
})
</script>

View file

@ -0,0 +1,64 @@
<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-large :item="getTopResult()"></mediaitem-square-large>
</template>
</div>
<div class="col" v-if="search.results.song">
<div class="row">
<div class="col">
<h3>Songs</h3>
</div>
<div class="col-auto flex-center" @click="app.showSearchView(app.search.term, 'song', app.friendlyTypes('song'))" v-if="search.results.song.data.length >= 6">
<button class="cd-btn-seeall">See All</button>
</div>
</div>
<div>
<mediaitem-list-item :item="item" :index="index"
v-for="(item, index) in search.results.song.data.limit(6)"></mediaitem-list-item>
</div>
</div>
</div>
<template v-if="search.results['meta']">
<template
v-for="section in search.results.meta.results.order" v-if="section != 'song' && section != 'top'">
<div class="row">
<div class="col">
<h3>{{ app.friendlyTypes(section) }}</h3>
</div>
<div class="col-auto flex-center" v-if="search.results[section].data.length >= 10">
<button class="cd-btn-seeall" @click="app.showSearchView(app.search.term, section, app.friendlyTypes(section))">See All</button>
</div>
</div>
<template v-if="!app.friendlyTypes(section).includes('Video')">
<mediaitem-scroller-horizontal-large
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else>
<mediaitem-scroller-horizontal-mvview
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
</template>
</template>
</div>
</script>
<script>
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;
}
}
}
})
</script>

View file

@ -0,0 +1,178 @@
<script type="text/x-template" id="cider-settings">
<div class="content-inner settings-page">
<h1 class="header-text">Settings <small>(non functional, stay tuned)</small></h1>
<div class="md-option-container">
<div class="md-option-line">
<div class="md-option-segment">
Audio Quality
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select" style="width:180px;">
<option value="990">Extreme</option>
<option value="256">High</option>
<option value="64">Low</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Seamless Audio Transitions
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="checkbox" switch/>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Animated Artwork
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Always</option>
<option value="1">Limit to pages and special entries</option>
<option value="2">Disable Everywhere</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Discord Rich Presence
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Disabled</option>
<option value="1">Display as 'Cider'</option>
<option value="2">Display as 'Apple Music'</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Enable AudioContext Functionality
<br>
<small>Enabling AudioContext functionality will allow for extended audio features like Equalizers and Visualizers, however on some systems this may cause stuttering in audio tracks.</small>
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="checkbox" switch/>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
LastFM Scrobbling
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="checkbox" switch/>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
LastFM Scrobble Delay
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="checkbox" switch/>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Theme
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Cider</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Theme Options
</div>
<div class="md-option-segment md-option-segment_auto">
<button class="btn">Theme Options</button>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Scrollbars
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Show on hover</option>
<option value="1">Always show</option>
<option value="2">Hidden</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Refresh Rate
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Automatic</option>
<option value='30'>30</option>
<option value='60'>60</option>
<option value='144'>144</option>
<option value='175'>175</option>
<option value='240'>240</option>
<option value='360'>30</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Enable Musixmatch Lyrics
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="checkbox" switch/>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Musixmatch Preferred Language
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="checkbox" switch/>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Close Button Behavior
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Minimize to system tray</option>
<option value='1'>Minimize to taskbar ? dock</option>
<option value='2'>Quit Cider</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Open Cider on Startup
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Never</option>
<option value='1'>Always</option>
<option value='2'>Always, minimized</option>
<option value='2'>Always, hidden in tray</option>
</select>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('cider-settings', {
template: "#cider-settings",
props: [],
data: function () {
return {
}
},
methods: {
}
})
</script>

View file

@ -0,0 +1,5 @@
<template v-if="page == 'webview'">
<div style="display:flex;width:100%;height:100%">
<webview id="foo" :src="webview.url" style="display:inline-flex; width:100%;"></webview>
</div>
</template>

View file

@ -0,0 +1,7 @@
<template v-if="page == 'zoo'">
<div class="content-inner">
<h3>Welcome to element park. *BERR NERR NERR NERR NERRRRR BERR NER NER NER NERRR BERRR NR NR NRRRR*</h3>
<button @click="app.playMediaItemById('1592151778', 'album')">Play Test Album</button>
<cider-queue ref="queue"></cider-queue>
</div>
</template>

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="currentColor"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M34.9 289.5l-22.2-22.2c-9.4-9.4-9.4-24.6 0-33.9L207 39c9.4-9.4 24.6-9.4 33.9 0l194.3 194.3c9.4 9.4 9.4 24.6 0 33.9L413 289.4c-9.5 9.5-25 9.3-34.3-.4L264 168.6V456c0 13.3-10.7 24-24 24h-32c-13.3 0-24-10.7-24-24V168.6L69.2 289.1c-9.3 9.8-24.8 10-34.3.4z"/></svg>

After

Width:  |  Height:  |  Size: 531 B

View file

@ -0,0 +1,4 @@
<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>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 320 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 d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"/></svg>

After

Width:  |  Height:  |  Size: 518 B

View file

@ -0,0 +1 @@
<svg aria-hidden="true" data-prefix="fas" data-icon="chevron-right" fill="currentColor" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" class="svg-inline--fa fa-chevron-right fa-w-10 fa-7x"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z" class=""/></svg>

After

Width:  |  Height:  |  Size: 538 B

View file

@ -0,0 +1,55 @@
<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"
fill="#eee" viewBox="0 0 384.97 384.97" style="enable-background:new 0 0 384.97 384.97;" xml:space="preserve">
<g>
<g id="Fullscreen_1_">
<path d="M372.939,216.545c-6.123,0-12.03,5.269-12.03,12.03v132.333H24.061V24.061h132.333c6.388,0,12.03-5.642,12.03-12.03
S162.409,0,156.394,0H24.061C10.767,0,0,10.767,0,24.061v336.848c0,13.293,10.767,24.061,24.061,24.061h336.848
c13.293,0,24.061-10.767,24.061-24.061V228.395C384.97,221.731,380.085,216.545,372.939,216.545z"/>
<path d="M372.939,0H252.636c-6.641,0-12.03,5.39-12.03,12.03s5.39,12.03,12.03,12.03h91.382L99.635,268.432
c-4.668,4.668-4.668,12.235,0,16.903c4.668,4.668,12.235,4.668,16.891,0L360.909,40.951v91.382c0,6.641,5.39,12.03,12.03,12.03
s12.03-5.39,12.03-12.03V12.03l0,0C384.97,5.558,379.412,0,372.939,0z"/>
</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>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,3 @@
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
<path d="M8.89547677 13.5330073c0-1.1057457-.97371638-2.0959657-2.09596577-2.0959657-1.13875305 0-2.0794621.99022-2.0794621 2.0959657 0 1.1222494.94070905 2.0794621 2.0794621 2.0794621 1.12224939 0 2.09596577-.9572127 2.09596577-2.0794621zm6.68398533 0c0-1.1057457-.9572127-2.0959657-2.0794621-2.0959657-1.1222494 0-2.0794621.99022-2.0794621 2.0959657 0 1.1222494.9572127 2.0794621 2.0794621 2.0794621 1.1222494 0 2.0794621-.9572127 2.0794621-2.0794621zm6.700489 0c0-1.1057457-.940709-2.0959657-2.0794621-2.0959657-1.1222494 0-2.0959658.99022-2.0959658 2.0959657 0 1.1222494.9737164 2.0794621 2.0959658 2.0794621 1.1387531 0 2.0794621-.9572127 2.0794621-2.0794621z"></path>
</svg>

After

Width:  |  Height:  |  Size: 772 B

View file

@ -0,0 +1,3 @@
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" class="glyph">
<path d="M11.3545232,18.4180929 L18.4676039,14.242665 C19.0452323,13.9290954 19.0122249,13.1204156 18.4676039,12.806846 L11.3545232,8.63141809 C10.7603912,8.26833741 9.98471883,8.54889976 9.98471883,9.19254279 L9.98471883,17.8404645 C9.98471883,18.5006112 10.7108802,18.7976773 11.3545232,18.4180929 Z"></path>
</svg>

After

Width:  |  Height:  |  Size: 409 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" 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 d="M500.33 0h-47.41a12 12 0 0 0-12 12.57l4 82.76A247.42 247.42 0 0 0 256 8C119.34 8 7.9 119.53 8 256.19 8.1 393.07 119.1 504 256 504a247.1 247.1 0 0 0 166.18-63.91 12 12 0 0 0 .48-17.43l-34-34a12 12 0 0 0-16.38-.55A176 176 0 1 1 402.1 157.8l-101.53-4.87a12 12 0 0 0-12.57 12v47.41a12 12 0 0 0 12 12h200.33a12 12 0 0 0 12-12V12a12 12 0 0 0-12-12z"/></svg>

After

Width:  |  Height:  |  Size: 622 B

View file

@ -0,0 +1 @@
var VueObserveVisibility=function(e){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function n(e){return function(e){if(Array.isArray(e)){for(var t=0,i=new Array(e.length);t<e.length;t++)i[t]=e[t];return i}}(e)||function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var r=function(){function e(t,i,n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.el=t,this.observer=null,this.frozen=!1,this.createObserver(i,n)}var t,r,o;return t=e,(r=[{key:"createObserver",value:function(e,t){var i=this;if(this.observer&&this.destroyObserver(),!this.frozen){var r;if(this.options="function"==typeof(r=e)?{callback:r}:r,this.callback=function(e,t){i.options.callback(e,t),e&&i.options.once&&(i.frozen=!0,i.destroyObserver())},this.callback&&this.options.throttle){var o=(this.options.throttleOptions||{}).leading;this.callback=function(e,t){var i,r,o,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},l=function(l){for(var a=arguments.length,c=new Array(a>1?a-1:0),u=1;u<a;u++)c[u-1]=arguments[u];if(o=c,!i||l!==r){var f=s.leading;"function"==typeof f&&(f=f(l,r)),i&&l===r||!f||e.apply(void 0,[l].concat(n(o))),r=l,clearTimeout(i),i=setTimeout(function(){e.apply(void 0,[l].concat(n(o))),i=0},t)}};return l._clear=function(){clearTimeout(i),i=null},l}(this.callback,this.options.throttle,{leading:function(e){return"both"===o||"visible"===o&&e||"hidden"===o&&!e}})}this.oldResult=void 0,this.observer=new IntersectionObserver(function(e){var t=e[0];if(e.length>1){var n=e.find(function(e){return e.isIntersecting});n&&(t=n)}if(i.callback){var r=t.isIntersecting&&t.intersectionRatio>=i.threshold;if(r===i.oldResult)return;i.oldResult=r,i.callback(r,t)}},this.options.intersection),t.context.$nextTick(function(){i.observer&&i.observer.observe(i.el)})}}},{key:"destroyObserver",value:function(){this.observer&&(this.observer.disconnect(),this.observer=null),this.callback&&this.callback._clear&&(this.callback._clear(),this.callback=null)}},{key:"threshold",get:function(){return this.options.intersection&&"number"==typeof this.options.intersection.threshold?this.options.intersection.threshold:0}}])&&i(t.prototype,r),o&&i(t,o),e}();function o(e,t,i){var n=t.value;if(n)if("undefined"==typeof IntersectionObserver)console.warn("[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/w3c/IntersectionObserver/tree/master/polyfill");else{var o=new r(e,n,i);e._vue_visibilityState=o}}function s(e){var t=e._vue_visibilityState;t&&(t.destroyObserver(),delete e._vue_visibilityState)}var l={bind:o,update:function(e,i,n){var r=i.value;if(!function e(i,n){if(i===n)return!0;if("object"===t(i)){for(var r in i)if(!e(i[r],n[r]))return!1;return!0}return!1}(r,i.oldValue)){var l=e._vue_visibilityState;r?l?l.createObserver(r,n):o(e,{value:r},n):s(e)}},unbind:s};function a(e){e.directive("observe-visibility",l)}var c={version:"1.0.0",install:a},u=null;return"undefined"!=typeof window?u=window.Vue:"undefined"!=typeof global&&(u=global.Vue),u&&u.use(c),e.ObserveVisibility=l,e.default=c,e.install=a,e}({});

6
src/renderer/vue.js Normal file

File diff suppressed because one or more lines are too long

2
src/renderer/vuedraggable.umd.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long