CHONKY BOY

This commit is contained in:
Core 2022-08-04 05:27:29 +01:00
parent 31ed921a1a
commit c15f55d0ee
No known key found for this signature in database
GPG key ID: FE9BF1B547F8F3C6
213 changed files with 64188 additions and 55736 deletions

View file

@ -1,5 +0,0 @@
{
"js": {
"beautify.ignore": "src/renderer/index.js"
}
}

View file

@ -1,154 +1,136 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2?v=3.19") format("woff2"),
url("Inter-Thin.woff?v=3.19") format("woff");
src: url("Inter-Thin.woff2?v=3.19") format("woff2"), url("Inter-Thin.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"),
url("Inter-ThinItalic.woff?v=3.19") format("woff");
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"), url("Inter-ThinItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"),
url("Inter-ExtraLight.woff?v=3.19") format("woff");
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"), url("Inter-ExtraLight.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"),
url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"), url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2?v=3.19") format("woff2"),
url("Inter-Light.woff?v=3.19") format("woff");
src: url("Inter-Light.woff2?v=3.19") format("woff2"), url("Inter-Light.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"),
url("Inter-LightItalic.woff?v=3.19") format("woff");
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"), url("Inter-LightItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2?v=3.19") format("woff2"),
url("Inter-Regular.woff?v=3.19") format("woff");
src: url("Inter-Regular.woff2?v=3.19") format("woff2"), url("Inter-Regular.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2?v=3.19") format("woff2"),
url("Inter-Italic.woff?v=3.19") format("woff");
src: url("Inter-Italic.woff2?v=3.19") format("woff2"), url("Inter-Italic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2?v=3.19") format("woff2"),
url("Inter-Medium.woff?v=3.19") format("woff");
src: url("Inter-Medium.woff2?v=3.19") format("woff2"), url("Inter-Medium.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"),
url("Inter-MediumItalic.woff?v=3.19") format("woff");
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"), url("Inter-MediumItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"),
url("Inter-SemiBold.woff?v=3.19") format("woff");
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"), url("Inter-SemiBold.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"), url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2?v=3.19") format("woff2"),
url("Inter-Bold.woff?v=3.19") format("woff");
src: url("Inter-Bold.woff2?v=3.19") format("woff2"), url("Inter-Bold.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"),
url("Inter-BoldItalic.woff?v=3.19") format("woff");
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"), url("Inter-BoldItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"),
url("Inter-ExtraBold.woff?v=3.19") format("woff");
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"), url("Inter-ExtraBold.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"),
url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"), url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-family: "Inter";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2?v=3.19") format("woff2"),
url("Inter-Black.woff?v=3.19") format("woff");
src: url("Inter-Black.woff2?v=3.19") format("woff2"), url("Inter-Black.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-family: "Inter";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"),
url("Inter-BlackItalic.woff?v=3.19") format("woff");
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"), url("Inter-BlackItalic.woff?v=3.19") format("woff");
}
/* -------------------------------------------------------
@ -161,23 +143,22 @@ Usage:
}
*/
@font-face {
font-family: 'Inter var';
font-family: "Inter var";
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
font-named-instance: "Regular";
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
}
@font-face {
font-family: 'Inter var';
font-family: "Inter var";
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
font-named-instance: "Italic";
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
}
/* --------------------------------------------------------------------------
[EXPERIMENTAL] Multi-axis, single variable font.
@ -192,7 +173,7 @@ explicitly, e.g.
*/
@font-face {
font-family: 'Inter var experimental';
font-family: "Inter var experimental";
font-weight: 100 900;
font-display: swap;
font-style: oblique 0deg 10deg;

View file

@ -8,9 +8,9 @@ http://scripts.sil.org/OFL
*/
@font-face {
font-family: 'Pretendard Variable';
font-weight: 45 920;
font-style: normal;
font-display: swap;
src: local('Pretendard Variable'), url('./woff2/PretendardVariable.woff2') format('woff2-variations');
font-family: "Pretendard Variable";
font-weight: 45 920;
font-style: normal;
font-display: swap;
src: local("Pretendard Variable"), url("./woff2/PretendardVariable.woff2") format("woff2-variations");
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -57,11 +57,11 @@ Vue.component("animated-number", {
});
function initMusicKit() {
if(!this.responseText) {
console.log("Using stored token")
if (!this.responseText) {
console.log("Using stored token");
this.responseText = JSON.stringify({
token: localStorage.getItem("lastToken")
})
token: localStorage.getItem("lastToken"),
});
}
let parsedJson = JSON.parse(this.responseText);
localStorage.setItem("lastToken", parsedJson.token);
@ -95,10 +95,10 @@ function capiInit() {
request.addEventListener("load", initMusicKit);
request.onreadystatechange = function (aEvt) {
if (request.readyState == 4 && request.status != 200) {
if(localStorage.getItem("lastToken") != null) {
initMusicKit()
if (localStorage.getItem("lastToken") != null) {
initMusicKit();
} else {
console.error(`Failed to load capi, cannot get token [${request.status}]`)
console.error(`Failed to load capi, cannot get token [${request.status}]`);
}
}
};
@ -110,7 +110,7 @@ document.addEventListener("musickitloaded", function () {
if (showOobe()) return;
console.log("MusicKit loaded");
// MusicKit global is now defined
capiInit()
capiInit();
});
window.addEventListener("drmUnsupported", function () {
initMusicKit();
@ -140,12 +140,7 @@ function Clone(obj) {
}
function uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
}
function xmlToJson(xml) {
@ -196,26 +191,19 @@ async function asyncForEach(array, callback) {
var checkIfScrollIsStatic = setInterval(() => {
try {
if (
position === document.getElementsByClassName("lyric-body")[0].scrollTop
) {
if (position === document.getElementsByClassName("lyric-body")[0].scrollTop) {
clearInterval(checkIfScrollIsStatic);
// do something
}
position = document.getElementsByClassName("lyric-body")[0].scrollTop;
} catch (e) { }
} catch (e) {}
}, 50);
// WebGPU Console Notification
async function webGPU() {
try {
const currentGPU = await navigator.gpu.requestAdapter();
console.log(
"WebGPU enabled on",
currentGPU.name,
"with feature ID",
currentGPU.features.size
);
console.log("WebGPU enabled on", currentGPU.name, "with feature ID", currentGPU.features.size);
} catch (e) {
console.log("WebGPU disabled / WebGPU initialization failed");
}
@ -240,9 +228,9 @@ function isJson(item) {
webGPU().then();
function showOobe() {
return false
return false;
if (localStorage.getItem("music.ampwebplay.media-user-token") && localStorage.getItem("seenOOBE")) {
return false
return false;
} else {
function waitForApp() {
if (typeof app.init !== "undefined") {
@ -252,7 +240,7 @@ function showOobe() {
}
}
waitForApp();
return true
return true;
}
}
@ -266,13 +254,7 @@ document.addEventListener("DOMContentLoaded", async function () {
document.addEventListener(
"contextmenu",
function (e) {
if (
e.target.tagName.toLowerCase() == "textarea" ||
(e.target.tagName.toLowerCase() == "input" &&
e.target.type != "checkbox" &&
e.target.type != "radio" &&
e.target.disabled == false)
) {
if (e.target.tagName.toLowerCase() == "textarea" || (e.target.tagName.toLowerCase() == "input" && e.target.type != "checkbox" && e.target.type != "radio" && e.target.disabled == false)) {
e.preventDefault();
const menuPanel = {
items: {

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
@colorMixRate: 1%;
@transparencyRate: 50%;
@keyColor : #fc3c44;
@keyColor: #fc3c44;
@ciderColor: #ff2654;
@baseColor: #1e1e1e;
@baseColorMix: mix(@baseColor, transparent, @transparencyRate);
@ -10,12 +10,12 @@
@appOpacity: 0.15;
:root {
--baseColor: @baseColor;
--baseColorMix: @baseColorMix;
--sidebarColor: @sidebarColor;
--sidebarColorMix: @sidebarColorMix;
--ciderColor: @ciderColor;
--appOpacity: @appOpacity;
--transparencyRate: @transparencyRate;
--macOSChromeColor: rgb(14 14 14 / 32%);
}
--baseColor: @baseColor;
--baseColorMix: @baseColorMix;
--sidebarColor: @sidebarColor;
--sidebarColorMix: @sidebarColorMix;
--ciderColor: @ciderColor;
--appOpacity: @appOpacity;
--transparencyRate: @transparencyRate;
--macOSChromeColor: rgb(14 14 14 / 32%);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
zoom: 0.95;
}
.app-sidebar-content {
padding:0px;
padding: 0px;
.app-sidebar-header-text {
padding: 6px 10px;
@ -56,4 +56,4 @@
#app-content {
// zoom: 0.8;
}
}
}

View file

@ -5,36 +5,35 @@
#app.twopanel {
--chromeHeight1: 46px;
--chromeHeight2: 90px;
--chromeHeight : calc(var(--chromeHeight1) + var(--chromeHeight2));
--chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2));
.modular-fs .app-drawer .lyric-footer {
bottom: var(--chromeHeight2);
}
.app-chrome {
&:not(.chrome-bottom) {
.app-chrome--center {
flex: 1;
.top-nav-group {
background : #1e1e1e99;
border : 1px solid lighten(@baseColor, 8);
background: #1e1e1e99;
border: 1px solid lighten(@baseColor, 8);
border-radius: 12px;
display : flex;
height : 32px;
display: flex;
height: 32px;
.app-sidebar-item {
background-color: #1e1e1e00;
border-radius : 10px !important;
border : 0px;
min-width : 120px;
padding : 6px;
justify-content : center;
align-items : center;
margin : 0px;
height : 100%;
position : relative;
border-radius: 10px !important;
border: 0px;
min-width: 120px;
padding: 6px;
justify-content: center;
align-items: center;
margin: 0px;
height: 100%;
position: relative;
white-space: nowrap;
._svg-icon {
@ -42,18 +41,18 @@
}
&:before {
--dist : 1px;
content : '';
position : absolute;
top : var(--dist);
left : var(--dist);
right : var(--dist);
bottom : var(--dist);
--dist: 1px;
content: "";
position: absolute;
top: var(--dist);
left: var(--dist);
right: var(--dist);
bottom: var(--dist);
background-color: #fff;
opacity : 0;
border-radius : 10px;
transform : scale(0.5);
transition : transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
opacity: 0;
border-radius: 10px;
transform: scale(0.5);
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
}
&:after {
@ -65,8 +64,8 @@
&:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1;
transform : scale(1);
opacity: 0.1;
transform: scale(1);
}
}
@ -74,15 +73,15 @@
background-color: transparent;
&:before {
opacity : .2;
opacity: 0.2;
transform: scale(1);
}
}
&.md-btn-primary {
box-shadow : 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
box-shadow: 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
background-color: lighten(@baseColor, @colorMixRate * 5);
z-index : 1;
z-index: 1;
}
}
}
@ -90,7 +89,7 @@
}
.app-mainmenu {
width : 30px;
width: 30px;
height: 30px;
}
@ -101,11 +100,10 @@
height: var(--chromeHeight1);
&.chrome-bottom {
background : var(--color2);
background: var(--color2);
-webkit-app-region: no-drag;
height : var(--chromeHeight2);
box-shadow : 0px -2px 6px rgb(20 20 20 / 12%),
0px -1px 0px 0px rgb(200 200 200 / 12%);
height: var(--chromeHeight2);
box-shadow: 0px -2px 6px rgb(20 20 20 / 12%), 0px -1px 0px 0px rgb(200 200 200 / 12%);
z-index: 4;
.app-chrome-playback-duration-bottom {
@ -116,33 +114,33 @@
}
.col-sm-auto {
width : 4em;
display : flex;
width: 4em;
display: flex;
justify-content: center;
align-items : center;
font-size : 0.8em;
align-items: center;
font-size: 0.8em;
}
input[type=range] {
appearance : none;
width : 100%;
height : 5px;
input[type="range"] {
appearance: none;
width: 100%;
height: 5px;
background-color: rgb(200 200 200 / 10%);
border-radius : 6px;
box-shadow : 0px 0px 0px 1px rgba(0 0 0 / 10%);
align-self : center;
border-radius: 6px;
box-shadow: 0px 0px 0px 1px rgba(0 0 0 / 10%);
align-self: center;
&::-webkit-slider-thumb {
opacity : 0;
transform : scale(1);
opacity: 0;
transform: scale(1);
-webkit-appearance: none;
appearance : none;
width : 16px;
height : 16px;
border-radius : 100%;
background : var(--keyColor);
cursor : default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase);
appearance: none;
width: 16px;
height: 16px;
border-radius: 100%;
background: var(--keyColor);
cursor: default;
transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
}
&:hover {
@ -169,38 +167,37 @@
.playback-button.play,
.playback-button.pause,
.playback-button.stop {
width : 42px;
height : 42px;
width: 42px;
height: 42px;
border-radius: 50%;
margin : 6px;
margin: 6px;
}
.app-chrome--center {
display : flex;
display: flex;
flex-direction: column;
.app-chrome-playback-controls {
display : flex;
z-index : 1;
display: flex;
z-index: 1;
// margin-bottom: 12px;
}
.app-chrome-playback-duration {
position : relative;
width : 80%;
position: relative;
width: 80%;
-webkit-app-region: no-drag;
height : 16px;
height: 16px;
.song-progress {
@bgColor : transparent;
height : 16px;
position : absolute;
bottom : 4px;
left : 0px;
right : 4px;
@bgColor: transparent;
height: 16px;
position: absolute;
bottom: 4px;
left: 0px;
right: 4px;
background: @bgColor;
z-index : 0;
z-index: 0;
.song-duration {
display: flex;
@ -208,64 +205,63 @@
.song-duration p {
font-weight: 400;
font-size : 10px;
height : 1.2em;
font-size: 10px;
height: 1.2em;
line-height: 1.3em;
overflow : hidden;
margin : 0 0 0 0.25em;
overflow: hidden;
margin: 0 0 0 0.25em;
}
&:hover {
>input[type=range] {
> input[type="range"] {
&::-webkit-slider-thumb {
opacity : 1;
opacity: 1;
transform: scale(1);
z-index : 1;
z-index: 1;
}
}
}
input[type=range] {
appearance : none;
width : 100%;
height : 4px;
input[type="range"] {
appearance: none;
width: 100%;
height: 4px;
background-color: rgb(200 200 200 / 10%);
border-radius : 2px;
border-radius: 2px;
&::-webkit-slider-thumb {
opacity : 0;
transform : scale(0.5);
opacity: 0;
transform: scale(0.5);
-webkit-appearance: none;
appearance : none;
width : 12px;
height : 12px;
border-radius : 100%;
background : var(--keyColor);
cursor : default;
transition : opacity .10s var(--appleEase), transform .10s var(--appleEase);
appearance: none;
width: 12px;
height: 12px;
border-radius: 100%;
background: var(--keyColor);
cursor: default;
transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
}
}
}
}
}
.app-chrome--left {
width : 30%;
justify-content : flex-start;
align-items : flex-start;
width: 30%;
justify-content: flex-start;
align-items: flex-start;
-webkit-app-region: no-drag !important;
.playback-controls {
-webkit-app-region: no-drag !important;
.artwork {
--offset : 20px;
--offset: 20px;
--marginOffset: 2;
--size : calc(var(--chromeHeight2) - var(--offset));
width : var(--size);
height : var(--size);
margin : 0 calc(var(--offset) / var(--marginOffset)) 0 calc(var(--offset) / var(--marginOffset));
--size: calc(var(--chromeHeight2) - var(--offset));
width: var(--size);
height: var(--size);
margin: 0 calc(var(--offset) / var(--marginOffset)) 0 calc(var(--offset) / var(--marginOffset));
.mediaitem-artwork,
img {
@ -275,13 +271,13 @@
.playback-info {
align-items: flex-start;
margin : 6px;
margin: 6px;
.song-name {
text-align : left;
font-size : 0.8em;
font-weight : 500;
width : 100%;
text-align: left;
font-size: 0.8em;
font-weight: 500;
width: 100%;
-webkit-mask-image: linear-gradient(-90deg, transparent 0%, transparent 10%, black 20%);
}
@ -306,23 +302,22 @@
.song-artist-album-content {
text-align: left;
font-size : 12px;
font-size: 12px;
}
}
width : 100%;
height : 100%;
width: 100%;
height: 100%;
max-width: 100%;
border : 0px;
border: 0px;
}
flex: 0 0 auto;
}
.app-chrome--right {
width : 30%;
flex : 0 0 auto;
width: 30%;
flex: 0 0 auto;
padding-right: 8px;
}
}
@ -334,7 +329,6 @@
}
}
// screen width is less than 768px
@media (max-width: 1100px) {
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center {

View file

@ -26,7 +26,6 @@
}
}
// Buttons
.md-btn {
font-family: inherit;
@ -115,7 +114,6 @@
}
}
.md-close-btn {
-webkit-mask-image: url("ameres://icons/webui/close.svg");
-webkit-mask-repeat: no-repeat;
@ -165,7 +163,7 @@
.page-btn {
align-self: center;
height: 32px;
width:max-content;
width: max-content;
}
.page-btn img {
@ -174,19 +172,19 @@
}
.md-ico-first {
content: url('./assets/angles-left.svg');
content: url("./assets/angles-left.svg");
}
.md-ico-prev {
content: url('./assets/chevron-left.svg');
content: url("./assets/chevron-left.svg");
}
.md-ico-next {
content: url('./assets/chevron-right.svg');
content: url("./assets/chevron-right.svg");
}
.md-ico-last {
content: url('./assets/angles-right.svg');
content: url("./assets/angles-right.svg");
}
.reload-btn {
@ -234,7 +232,7 @@
&:hover {
cursor: pointer;
background: rgb(200 200 200 / 10%)
background: rgb(200 200 200 / 10%);
}
}
@ -315,7 +313,6 @@
}
}
#artworkLCD img {
image-rendering: auto;
}
@ -514,7 +511,7 @@
.subtitle {
width: 90%;
font-size: .8em;
font-size: 0.8em;
opacity: 0.7;
}
@ -570,39 +567,39 @@
*/
@keyframes load-bar {
10% {
box-shadow: inset 0 -4px 0
box-shadow: inset 0 -4px 0;
}
20% {
box-shadow: inset 0 -10px 0
box-shadow: inset 0 -10px 0;
}
30% {
box-shadow: inset 0 -12px 0
box-shadow: inset 0 -12px 0;
}
40% {
box-shadow: inset 0 -8px 0
box-shadow: inset 0 -8px 0;
}
50% {
box-shadow: inset 0 -4px 0
box-shadow: inset 0 -4px 0;
}
60% {
box-shadow: inset 0 -6px 0
box-shadow: inset 0 -6px 0;
}
80% {
box-shadow: inset 0 -12px 0
box-shadow: inset 0 -12px 0;
}
90% {
box-shadow: inset 0 -6px 0
box-shadow: inset 0 -6px 0;
}
to {
box-shadow: inset 0 -2px 0
box-shadow: inset 0 -2px 0;
}
}
@ -629,17 +626,17 @@
.loadbar-sound::before {
content: "";
position: absolute;
bottom: 0
bottom: 0;
}
.loadbar-sound::before {
left: -4.5px;
animation-delay: -2.4s
animation-delay: -2.4s;
}
.loadbar-sound::after {
right: -4.2px;
animation-delay: -3.7s
animation-delay: -3.7s;
}
.isLibrary {
@ -670,7 +667,6 @@
box-shadow: var(--mediaItemShadow);
}
&:active {
background: var(--selected-click);
box-shadow: var(--mediaItemShadow);
@ -805,7 +801,6 @@
&:hover + .cd-mediaitem-square-large-overlay {
opacity: 1;
}
&:hover {
@ -813,7 +808,6 @@
}
}
/* mediaitem-square-large */
.cd-mediaitem-square-large {
width: 190px;
@ -855,12 +849,10 @@
margin: 10px;
margin-top: 0px;
opacity: 0;
}
.cd-mediaitem-square-large-overlay > * {
pointer-events: auto;
}
.cd-mediaitem-square-large > .cd-mediaitem-square-large-overlay {
@ -873,15 +865,12 @@
.cd-mediaitem-square-large + .cd-mediaitem-square-large-overlay {
pointer-events: none;
}
.cd-mediaitem-square-large:hover + .cd-mediaitem-square-large-overlay {
opacity: 1;
}
.cd-mediaitem-square-large .artwork.round {
border-radius: var(--mediaItemRadiusRound);
}
@ -940,12 +929,10 @@
margin: 10px;
margin-top: 0px;
opacity: 0;
}
.cd-mediaitem-mvview-overlay > * {
pointer-events: auto;
}
.cd-mediaitem-mvview > .cd-mediaitem-mvview-overlay {
@ -958,15 +945,12 @@
.cd-mediaitem-mvview + .cd-mediaitem-mvview-overlay {
pointer-events: none;
}
.cd-mediaitem-mvview:hover + .cd-mediaitem-mvview-overlay {
opacity: 1;
}
.cd-mediaitem-mvview .artwork.round {
border-radius: var(--mediaItemRadiusRound);
}
@ -982,10 +966,9 @@
font-size: 12px;
}
/* mediaitem-square */
.cd-mediaitem-square {
--transitionDuration: .5s;
--transitionDuration: 0.5s;
--scaleRate: 1.25;
--scaleRateArtwork: 1;
width: calc(160px * var(--windowRelativeScale));
@ -1062,7 +1045,6 @@
bottom: 14px;
left: 14px;
z-index: 2;
}
> .menu-btn {
@ -1110,7 +1092,6 @@
// }
// }
.info-rect {
width: 90%;
height: 100%;
@ -1119,7 +1100,6 @@
align-items: center;
}
.title {
width: 100%;
text-align: center;
@ -1174,19 +1154,19 @@
--scaleRateArtwork: 1.1;
width: calc(240px * var(--scaleRate));
height: calc(200px * var(--scaleRate));
.artwork-container > .artwork {
width: calc(220px * var(--scaleRateArtwork));
height: calc(123px * var(--scaleRateArtwork));
}
}
@media (min-width: 1550px) {
--scaleRate: 1.25;
--scaleRateArtwork: 1.25;
width: calc(240px * var(--scaleRate));
height: calc(200px * var(--scaleRate));
.artwork-container > .artwork {
width: calc(220px * var(--scaleRateArtwork));
height: calc(123px * var(--scaleRateArtwork));
@ -1212,19 +1192,19 @@
--scaleRateArtwork: 1.1;
width: calc(240px * var(--scaleRate));
height: calc(200px * var(--scaleRate));
.artwork-container > .artwork {
width: calc(220px * var(--scaleRateArtwork));
height: calc(123px * var(--scaleRateArtwork));
}
}
@media (min-width: 1550px) {
--scaleRate: 1.25;
--scaleRateArtwork: 1.25;
width: calc(240px * var(--scaleRate));
height: calc(200px * var(--scaleRate));
.artwork-container > .artwork {
width: calc(220px * var(--scaleRateArtwork));
height: calc(123px * var(--scaleRateArtwork));
@ -1338,7 +1318,7 @@
}
&:hover {
.artwork{
.artwork {
filter: brightness(0.8);
}
.info-rect-card::before {
@ -1430,7 +1410,6 @@
}
&:hover {
> .play-btn,
> .menu-btn {
opacity: 1;
@ -1438,7 +1417,6 @@
}
}
.title {
width: 90%;
text-align: center;
@ -1471,7 +1449,6 @@
}
}
.listitem-horizontal {
.cd-mediaitem-list-item {
width: 350px;
@ -1497,7 +1474,6 @@
&:hover::-webkit-scrollbar {
display: initial;
}
}
@ -1536,9 +1512,8 @@
}
}
/* Switch Checkbox */
input[type=checkbox][switch] {
input[type="checkbox"][switch] {
width: 38px;
appearance: none;
border-radius: 32px;
@ -1554,12 +1529,12 @@ input[type=checkbox][switch] {
margin: 0;
}
input[type=checkbox][switch]:focus,
input[type=checkbox][switch]:active {
input[type="checkbox"][switch]:focus,
input[type="checkbox"][switch]:active {
outline: none;
}
input[type=checkbox][switch]:checked {
input[type="checkbox"][switch]:checked {
background: var(--keyColor);
border: 0 solid var(--keyColor);
mix-blend-mode: unset;
@ -1573,43 +1548,41 @@ input[type=checkbox][switch]:checked {
}
}
input[type=checkbox][switch]::before {
input[type="checkbox"][switch]::before {
background: white;
width: 26px;
height: 26px;
top: -1px;
left: -1px;
position: absolute;
content: ' ';
content: " ";
border-radius: 32px;
transition: .10s left var(--appleEase);
transform: scale(.75);
transition: 0.1s left var(--appleEase);
transform: scale(0.75);
}
input[type=checkbox][switch]:checked::before {
input[type="checkbox"][switch]:checked::before {
background: white;
top: -1px;
left: 13px;
transition: .10s left var(--appleEase);
transform: scale(.75);
transition: 0.1s left var(--appleEase);
transform: scale(0.75);
}
input[type=checkbox][switch]:disabled::before {
opacity: .5;
input[type="checkbox"][switch]:disabled::before {
opacity: 0.5;
}
input[type=checkbox][switch]:active::before {
input[type="checkbox"][switch]:active::before {
left: 13px;
}
input[type=checkbox][switch]:checked:active::before {
input[type="checkbox"][switch]:checked:active::before {
left: -1px;
}
/* End Switch Checkbox */
.header-text {
margin: 0px;
}
@ -1649,7 +1622,7 @@ input[type=checkbox][switch]:checked:active::before {
.media-item--small .text {
font-weight: 600;
font-size: 0.90em;
font-size: 0.9em;
}
.media-item--small .subtext {
@ -1684,11 +1657,11 @@ input[type=checkbox][switch]:checked:active::before {
background-repeat: no-repeat;
border-radius: 8px;
box-shadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%), 0 8px 40px rgb(0 0 0 / 0.55);
transition: transform .10s var(--appleEase);
transition: transform 0.1s var(--appleEase);
}
.media-artwork.paused {
transition: transform .35s var(--appleEase);
transition: transform 0.35s var(--appleEase);
transform: scale(0.85);
}
@ -1727,7 +1700,7 @@ input[type=checkbox][switch]:checked:active::before {
background-size: 12px;
background-position: center;
background-repeat: no-repeat;
opacity: 0.70;
opacity: 0.7;
border-radius: 6px;
position: relative;
@ -1745,7 +1718,7 @@ input[type=checkbox][switch]:checked:active::before {
z-index: -1;
transform: scale(0.5);
pointer-events: none;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
}
&:hover {
@ -1768,7 +1741,7 @@ input[type=checkbox][switch]:checked:active::before {
height: 32px;
border: 0px;
box-shadow: unset;
opacity: 0.70;
opacity: 0.7;
position: relative;
&:before {
@ -1785,7 +1758,7 @@ input[type=checkbox][switch]:checked:active::before {
z-index: -1;
transform: scale(0.5);
pointer-events: none;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
}
&:hover {
@ -1845,7 +1818,7 @@ input[type=checkbox][switch]:checked:active::before {
}
.playback-button.stop {
background-image: url('./assets/cider-icons/stop.svg');
background-image: url("./assets/cider-icons/stop.svg");
background-size: 38px;
background-position: center;
}
@ -1863,25 +1836,25 @@ input[type=checkbox][switch]:checked:active::before {
}
.playback-button.pause {
background-image: url('./assets/cider-icons/pause.svg');
background-image: url("./assets/cider-icons/pause.svg");
background-size: 38px;
background-position: center;
}
.playback-button.play {
background-image: url('./assets/cider-icons/play.svg');
background-image: url("./assets/cider-icons/play.svg");
background-size: 38px;
background-position: center;
}
.playback-button.next {
background-image: url('./assets/cider-icons/forward.svg');
background-image: url("./assets/cider-icons/forward.svg");
background-size: 60%;
background-position: center;
}
.playback-button.previous {
background-image: url('./assets/cider-icons/backward.svg');
background-image: url("./assets/cider-icons/backward.svg");
background-size: 60%;
background-position: center;
}
@ -1950,7 +1923,7 @@ input[type=checkbox][switch]:checked:active::before {
}
.player-song-artist {
font-size: 1.0em;
font-size: 1em;
text-align: left;
margin: 0 auto;
color: var(--keyColor);
@ -1999,10 +1972,8 @@ input[type=checkbox][switch]:checked:active::before {
height: 40px;
display: flex;
align-items: center;
}
.list-entry-header {
display: flex;
align-items: center;
@ -2204,9 +2175,9 @@ input[type=checkbox][switch]:checked:active::before {
.fancy-pills {
.nav-pills {
position: relative;
.nav-link {
transition: transform .3s var(--appleEase);
transition: transform 0.3s var(--appleEase);
position: relative;
background-color: transparent;
border: 0;
@ -2215,8 +2186,7 @@ input[type=checkbox][switch]:checked:active::before {
-webkit-user-drag: none;
font-weight: 500;
margin: 0px 4px;
&:after {
--dist: 1px;
content: "";
@ -2231,27 +2201,23 @@ input[type=checkbox][switch]:checked:active::before {
border-radius: 50px;
z-index: -1;
opacity: 0;
transition: background-color .5s var(--appleEase), opacity 0.25s var(--appleEase), border-radius .32s var(--appleEase);
transition: background-color 0.5s var(--appleEase), opacity 0.25s var(--appleEase), border-radius 0.32s var(--appleEase);
}
&:hover {
outline: none;
transform: scale(1.1);
// background: #eee;
background: transparent;
color: #333;
&:after {
opacity: 1;
background-color: #eee;
transition: background-color .25s var(--appleEase),
border-radius .25s var(--appleEase),
color .0s var(--appleEase),
opacity 0.0s var(--appleEase);
transition: background-color 0.25s var(--appleEase), border-radius 0.25s var(--appleEase), color 0s var(--appleEase), opacity 0s var(--appleEase);
}
}
&.active {
outline: none;
transform: scale(1.1);
@ -2259,24 +2225,22 @@ input[type=checkbox][switch]:checked:active::before {
background: transparent;
color: #333;
font-weight: 600;
&:after {
opacity: 1;
background-color: #eee;
}
}
}
&:hover {
.nav-link.active {
outline: none;
transform: scale(1.0);
transform: scale(1);
background: transparent;
color: #eee;
transform: scale(1.0);
transform: scale(1);
&:after {
background: rgb(200 200 200 / 15%);
opacity: 1;
@ -2284,12 +2248,12 @@ input[type=checkbox][switch]:checked:active::before {
// border-radius: 5px;
--dist: 4px;
}
&:hover {
transform: scale(1.1);
z-index: 1;
color: #333;
&:after {
background: #eee;
border-radius: inherit;
@ -2298,9 +2262,9 @@ input[type=checkbox][switch]:checked:active::before {
}
}
}
&:after {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
@ -2387,7 +2351,7 @@ input[type=checkbox][switch]:checked:active::before {
border-radius: 50%;
div {
border-radius: 50%;
}
}
img {
border-radius: 50%;
}
@ -2399,8 +2363,7 @@ input[type=checkbox][switch]:checked:active::before {
align-items: center;
gap: 0.5rem;
margin-bottom: 16px;
.md-input-number{
.md-input-number {
min-width: 12em;
}
}
}

View file

@ -22,14 +22,13 @@
--chromeHeight1: 70px;
.app-content-container {
width:100%;
height:100%;
width: 100%;
height: 100%;
#app-content {
width:100%;
height:100%;
width: 100%;
height: 100%;
.fs-search {
.search-input--icon {
width: 4em;
background-size: 40%;
@ -38,7 +37,7 @@
input {
padding-left: 2em;
font-size: 2em;
border-radius: var(--mediaItemRadius)
border-radius: var(--mediaItemRadius);
}
}
}
@ -56,42 +55,41 @@
z-index: 9999;
.top-nav-group {
background : #1e1e1e99;
border : 1px solid lighten(@baseColor, 8);
background: #1e1e1e99;
border: 1px solid lighten(@baseColor, 8);
border-radius: 12px;
display : flex;
height : 55px;
display: flex;
height: 55px;
width: 90%;
backdrop-filter: var(--glassFilter);
.app-sidebar-item {
background-color: #1e1e1e00;
border-radius : 10px !important;
border : 0px;
min-width : 120px;
padding : 6px;
justify-content : center;
align-items : center;
margin : 0px;
height : 100%;
position : relative;
border-radius: 10px !important;
border: 0px;
min-width: 120px;
padding: 6px;
justify-content: center;
align-items: center;
margin: 0px;
height: 100%;
position: relative;
font-size: 1.1em;
font-weight: 500;
&:before {
--dist : 1px;
content : '';
position : absolute;
top : var(--dist);
left : var(--dist);
right : var(--dist);
bottom : var(--dist);
--dist: 1px;
content: "";
position: absolute;
top: var(--dist);
left: var(--dist);
right: var(--dist);
bottom: var(--dist);
background-color: #fff;
opacity : 0;
border-radius : 10px;
transform : scale(0.5);
transition : transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
opacity: 0;
border-radius: 10px;
transform: scale(0.5);
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
}
&:after {
@ -103,8 +101,8 @@
&:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1;
transform : scale(1);
opacity: 0.1;
transform: scale(1);
}
}
@ -112,15 +110,15 @@
background-color: transparent;
&:before {
opacity : .2;
opacity: 0.2;
transform: scale(1);
}
}
&.md-btn-primary {
box-shadow : 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
box-shadow: 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
background-color: lighten(@baseColor, @colorMixRate * 5);
z-index : 1;
z-index: 1;
}
}
}
@ -164,7 +162,7 @@
display: flex;
justify-content: center;
align-items: center;
width: 100%
width: 100%;
}
.volume-button--small {
@ -178,7 +176,7 @@
width: 30px;
border: 0px;
box-shadow: unset;
opacity: 0.70;
opacity: 0.7;
background-image: url("./assets/feather/volume-2.svg");
}
@ -190,7 +188,7 @@
background-image: url("./assets/feather/volume.svg");
}
input[type=range] {
input[type="range"] {
-webkit-appearance: none;
height: 4px;
background: rgba(255, 255, 255, 0.4);
@ -228,7 +226,6 @@
}
}
.background {
position: absolute;
background-size: cover;
@ -247,13 +244,10 @@
.bg-artwork-container .bg-artwork {
filter: brightness(85%) saturate(95%) blur(180px) contrast(0.9) opacity(0.9);
}
}
}
.lyrics-col {
height: 62vh;
display: flex;
justify-content: center;
@ -278,11 +272,9 @@
.lyric-line {
font-size: 35px;
}
}
.queue-col {
width: 60vh;
height: 62vh;
@ -361,7 +353,8 @@
}
.app-playback-controls {
.song-artist, .song-name {
.song-artist,
.song-name {
font-weight: 600;
text-align: center;
font-size: 0.9em;
@ -414,8 +407,6 @@
width: 100%;
text-align: center;
}
}
.app-playback-controls .song-progress {
@ -436,7 +427,7 @@
}
&:hover {
> input[type=range] {
> input[type="range"] {
&::-webkit-slider-thumb {
opacity: 1;
transform: scale(1);
@ -445,7 +436,7 @@
}
}
input[type=range] {
input[type="range"] {
appearance: none;
width: 100%;
height: 4px;
@ -462,7 +453,7 @@
border-radius: 100%;
background: var(--songProgressColor);
cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
}
&::-moz-range-thumb {
@ -482,7 +473,6 @@
justify-content: center;
align-items: center;
}
}
.cd-mediaitem-square {
@ -556,18 +546,18 @@
.playlist-page .playlist-display {
width: 100%;
max-width: 500px;
flex:1;
flex: 1;
text-align: center;
.playlistInfo {
>.row {
> .row {
justify-content: center;
}
}
.playlist-controls {
div {
width:100%;
width: 100%;
}
}
}

View file

@ -156,8 +156,7 @@
}
.close-btn {
.menu-panel.menu-header-text.close-btn
.menu-panel.menu-header-text.close-btn;
}
}
}
@ -180,7 +179,7 @@
}
.close-btn {
.menu-panel.menu-header-text.close-btn
.menu-panel.menu-header-text.close-btn;
}
}
@ -294,7 +293,6 @@
overflow: hidden;
font-size: 13px;
.menu-option {
text-align: left;
display: flex;
@ -323,7 +321,7 @@
opacity: 0;
transform: scale(0.98);
z-index: -1;
transition: transform .25s ease-out, opacity .25s ease-out;
transition: transform 0.25s ease-out, opacity 0.25s ease-out;
}
&:hover {
@ -336,7 +334,7 @@
&:active {
&::before {
transition: transform .1s ease-in-out, opacity .1s ease-in-out;
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity: 1;
transform: scale(0.98);
background: var(--selected-click);
@ -372,7 +370,7 @@
}
&:hover {
background-color: rgb(196, 43, 28)
background-color: rgb(196, 43, 28);
}
}
}
@ -430,7 +428,6 @@
}
}
.moreinfo-modal {
.modal-window {
height: 70%;
@ -496,7 +493,8 @@
font-weight: 600;
}
.song-artist, .song-album {
.song-artist,
.song-album {
opacity: 0.75;
cursor: pointer;
@ -521,4 +519,4 @@
&.svg-md {
--size: 1.2em;
}
}
}

View file

@ -1,59 +1,59 @@
// Linux
body[platform="linux"] {
#window-controls-container {
//display: none;
}
#window-controls-container {
//display: none;
}
.window-controls {
justify-content: flex-end;
align-items : center;
padding-right : 6px;
.window-controls {
justify-content: flex-end;
align-items: center;
padding-right: 6px;
>div {
--iconSize: 16px;
> div {
--iconSize: 16px;
&.close,
&.minmax,
&.minimize,
&.minmax.restore {
background-image: unset!important;
position : relative;
display : grid;
align-content : center;
text-align : center;
height : 36px!important;
width : 36px!important;
border-radius : 50px;
transition: background-color .1s ease-in-out;
&.close,
&.minmax,
&.minimize,
&.minmax.restore {
background-image: unset !important;
position: relative;
display: grid;
align-content: center;
text-align: center;
height: 36px !important;
width: 36px !important;
border-radius: 50px;
transition: background-color 0.1s ease-in-out;
&:hover {
background: rgb(200 200 200 / 10%)!important;
}
}
&.close::before {
font-family: "codicon";
font-size : var(--iconSize);
content : "";
}
&.minmax::before {
font-family: "codicon";
font-size : var(--iconSize);
content : "";
}
&.minimize::before {
font-family: "codicon";
font-size : var(--iconSize);
content : "";
}
&.restore::before {
font-family: "codicon";
font-size : var(--iconSize);
content : "";
}
&:hover {
background: rgb(200 200 200 / 10%) !important;
}
}
&.close::before {
font-family: "codicon";
font-size: var(--iconSize);
content: "";
}
&.minmax::before {
font-family: "codicon";
font-size: var(--iconSize);
content: "";
}
&.minimize::before {
font-family: "codicon";
font-size: var(--iconSize);
content: "";
}
&.restore::before {
font-family: "codicon";
font-size: var(--iconSize);
content: "";
}
}
}
}
}

View file

@ -1,79 +1,79 @@
body[platform="darwin"] {
html {
background: transparent !important;
html {
background: transparent !important;
}
&.notransparency::before {
display: none;
}
#app {
&.simplebg {
background: transparent;
}
&.notransparency::before {
display: none;
&::before {
display: none;
}
#app {
&.simplebg {
background: transparent;
}
&::before {
display: none;
}
.app-chrome {
background-color: var(--macOSChromeColor);
}
&.twopanel {
--chromeHeight1: 55px;
--chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2));
.app-chrome .app-chrome-item.search {
margin-right: 12px;
}
.app-chrome .app-mainmenu {
width: 46px;
}
.app-chrome.chrome-bottom {
background-color: var(--macOSChromeColor);
}
}
&[window-state="normal"] {
&::after {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
box-shadow: inset 0px 0px .5px 1px rgb(200 200 200 / 40%);
border-radius: 10px;
content: " ";
z-index: 999999;
pointer-events: none;
}
}
.app-chrome {
background-color: var(--macOSChromeColor);
}
#app-main {
background-color: transparent;
&.twopanel {
--chromeHeight1: 55px;
--chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2));
.app-navigation {
background: transparent;
}
.app-chrome .app-chrome-item.search {
margin-right: 12px;
}
#app-content {
background-color: #1e1e1e6b;
}
.app-chrome .app-mainmenu {
width: 46px;
}
.app-chrome.chrome-bottom {
background-color: var(--macOSChromeColor);
}
}
.settings-window.maxed {
.tabs>.col-auto {
transition: padding-top .3s linear;
padding-top: var(--chromeHeight1);
}
&[window-state="normal"] {
&::after {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
box-shadow: inset 0px 0px 0.5px 1px rgb(200 200 200 / 40%);
border-radius: 10px;
content: " ";
z-index: 999999;
pointer-events: none;
}
}
}
#app-main {
background-color: transparent;
.app-navigation {
background: transparent;
}
#apple-music-video-player-controls #player-exit {
margin-top: 18px;
left: 70px;
#app-content {
background-color: #1e1e1e6b;
}
}
}
.settings-window.maxed {
.tabs > .col-auto {
transition: padding-top 0.3s linear;
padding-top: var(--chromeHeight1);
}
}
#apple-music-video-player-controls #player-exit {
margin-top: 18px;
left: 70px;
}
}

View file

@ -1,7 +1,4 @@
#app.macosemu {
.app-chrome .app-chrome-item > .window-controls-macos {
@controlSize: 12px;
display: flex;
@ -42,8 +39,8 @@
}
}
}
.usermenu-body{
.usermenu-body {
left: calc(100vw - 260px);
position: relative;
}
}
}

View file

@ -65,7 +65,7 @@
display: flex;
justify-content: center;
align-items: center;
width: 100%
width: 100%;
}
.volume-button--small {
@ -79,7 +79,7 @@
width: 30px;
border: 0px;
box-shadow: unset;
opacity: 0.70;
opacity: 0.7;
background-image: url("./assets/feather/volume-2.svg");
}
@ -91,7 +91,7 @@
background-image: url("./assets/feather/volume.svg");
}
input[type=range] {
input[type="range"] {
-webkit-appearance: none;
height: 4px;
background: rgba(255, 255, 255, 0.4);
@ -129,7 +129,6 @@
}
}
.background {
position: absolute;
background-size: cover;
@ -157,9 +156,7 @@
}
}
.lyrics-col {
height: 62vh;
display: flex;
justify-content: center;
@ -184,11 +181,9 @@
.lyric-line {
font-size: 35px;
}
}
.queue-col {
width: 60vh;
height: 50vh;
@ -281,11 +276,11 @@
}
}
.app-playback-controls {
-webkit-app-region: no-drag;
.song-artist, .song-name {
.song-artist,
.song-name {
font-weight: 600;
text-align: center;
font-size: 0.9em;
@ -338,8 +333,6 @@
width: 100%;
text-align: center;
}
}
.app-playback-controls .song-progress {
@ -360,7 +353,7 @@
}
&:hover {
> input[type=range] {
> input[type="range"] {
&::-webkit-slider-thumb {
opacity: 1;
transform: scale(1);
@ -369,7 +362,7 @@
}
}
input[type=range] {
input[type="range"] {
appearance: none;
width: 100%;
height: 4px;
@ -386,7 +379,7 @@
border-radius: 100%;
background: var(--songProgressColor);
cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
transition: opacity 0.1s var(--appleEase), transform 0.1s var(--appleEase);
}
&::-moz-range-thumb {
@ -405,6 +398,5 @@
width: 100%;
justify-content: center;
}
}
}

View file

@ -1,370 +1,370 @@
@-webkit-keyframes notyf-fadeinup {
0% {
opacity: 0;
transform: translateY(25%)
}
0% {
opacity: 0;
transform: translateY(25%);
}
to {
opacity: 1;
transform: translateY(0)
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes notyf-fadeinup {
0% {
opacity: 0;
transform: translateY(25%)
}
0% {
opacity: 0;
transform: translateY(25%);
}
to {
opacity: 1;
transform: translateY(0)
}
to {
opacity: 1;
transform: translateY(0);
}
}
@-webkit-keyframes notyf-fadeinleft {
0% {
opacity: 0;
transform: translateX(25%)
}
0% {
opacity: 0;
transform: translateX(25%);
}
to {
opacity: 1;
transform: translateX(0)
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes notyf-fadeinleft {
0% {
opacity: 0;
transform: translateX(25%)
}
0% {
opacity: 0;
transform: translateX(25%);
}
to {
opacity: 1;
transform: translateX(0)
}
to {
opacity: 1;
transform: translateX(0);
}
}
@-webkit-keyframes notyf-fadeoutright {
0% {
opacity: 1;
transform: translateX(0)
}
0% {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(25%)
}
to {
opacity: 0;
transform: translateX(25%);
}
}
@keyframes notyf-fadeoutright {
0% {
opacity: 1;
transform: translateX(0)
}
0% {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(25%)
}
to {
opacity: 0;
transform: translateX(25%);
}
}
@-webkit-keyframes notyf-fadeoutdown {
0% {
opacity: 1;
transform: translateY(0)
}
0% {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(25%)
}
to {
opacity: 0;
transform: translateY(25%);
}
}
@keyframes notyf-fadeoutdown {
0% {
opacity: 1;
transform: translateY(0)
}
0% {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(25%)
}
to {
opacity: 0;
transform: translateY(25%);
}
}
@-webkit-keyframes ripple {
0% {
transform: scale(0) translateY(-45%) translateX(13%)
}
0% {
transform: scale(0) translateY(-45%) translateX(13%);
}
to {
transform: scale(1) translateY(-45%) translateX(13%)
}
to {
transform: scale(1) translateY(-45%) translateX(13%);
}
}
@keyframes ripple {
0% {
transform: scale(0) translateY(-45%) translateX(13%)
}
0% {
transform: scale(0) translateY(-45%) translateX(13%);
}
to {
transform: scale(1) translateY(-45%) translateX(13%)
}
to {
transform: scale(1) translateY(-45%) translateX(13%);
}
}
.notyf {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
color: #fff;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
pointer-events: none;
box-sizing: border-box;
padding: 20px
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
color: #fff;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
pointer-events: none;
box-sizing: border-box;
padding: 20px;
}
.notyf__icon--error,
.notyf__icon--success {
height: 21px;
width: 21px;
background: #fff;
border-radius: 50%;
display: block;
margin: 0 auto;
position: relative
height: 21px;
width: 21px;
background: #fff;
border-radius: 50%;
display: block;
margin: 0 auto;
position: relative;
}
.notyf__icon--error:after,
.notyf__icon--error:before {
content: "";
background: currentColor;
display: block;
position: absolute;
width: 3px;
border-radius: 3px;
left: 9px;
height: 12px;
top: 5px
content: "";
background: currentColor;
display: block;
position: absolute;
width: 3px;
border-radius: 3px;
left: 9px;
height: 12px;
top: 5px;
}
.notyf__icon--error:after {
transform: rotate(-45deg)
transform: rotate(-45deg);
}
.notyf__icon--error:before {
transform: rotate(45deg)
transform: rotate(45deg);
}
.notyf__icon--success:after,
.notyf__icon--success:before {
content: "";
background: currentColor;
display: block;
position: absolute;
width: 3px;
border-radius: 3px
content: "";
background: currentColor;
display: block;
position: absolute;
width: 3px;
border-radius: 3px;
}
.notyf__icon--success:after {
height: 6px;
transform: rotate(-45deg);
top: 9px;
left: 6px
height: 6px;
transform: rotate(-45deg);
top: 9px;
left: 6px;
}
.notyf__icon--success:before {
height: 11px;
transform: rotate(45deg);
top: 5px;
left: 10px
height: 11px;
transform: rotate(45deg);
top: 5px;
left: 10px;
}
.notyf__toast {
display: block;
overflow: hidden;
pointer-events: auto;
-webkit-animation: notyf-fadeinup .3s ease-in forwards;
animation: notyf-fadeinup .3s ease-in forwards;
box-shadow: 0 3px 7px 0 rgba(0, 0, 0, .25);
position: relative;
padding: 0 15px;
border-radius: 2px;
max-width: 300px;
transform: translateY(25%);
box-sizing: border-box;
flex-shrink: 0
display: block;
overflow: hidden;
pointer-events: auto;
-webkit-animation: notyf-fadeinup 0.3s ease-in forwards;
animation: notyf-fadeinup 0.3s ease-in forwards;
box-shadow: 0 3px 7px 0 rgba(0, 0, 0, 0.25);
position: relative;
padding: 0 15px;
border-radius: 2px;
max-width: 300px;
transform: translateY(25%);
box-sizing: border-box;
flex-shrink: 0;
}
.notyf__toast--disappear {
transform: translateY(0);
-webkit-animation: notyf-fadeoutdown .3s forwards;
animation: notyf-fadeoutdown .3s forwards;
-webkit-animation-delay: .25s;
animation-delay: .25s
transform: translateY(0);
-webkit-animation: notyf-fadeoutdown 0.3s forwards;
animation: notyf-fadeoutdown 0.3s forwards;
-webkit-animation-delay: 0.25s;
animation-delay: 0.25s;
}
.notyf__toast--disappear .notyf__icon,
.notyf__toast--disappear .notyf__message {
-webkit-animation: notyf-fadeoutdown .3s forwards;
animation: notyf-fadeoutdown .3s forwards;
opacity: 1;
transform: translateY(0)
-webkit-animation: notyf-fadeoutdown 0.3s forwards;
animation: notyf-fadeoutdown 0.3s forwards;
opacity: 1;
transform: translateY(0);
}
.notyf__toast--disappear .notyf__dismiss {
-webkit-animation: notyf-fadeoutright .3s forwards;
animation: notyf-fadeoutright .3s forwards;
opacity: 1;
transform: translateX(0)
-webkit-animation: notyf-fadeoutright 0.3s forwards;
animation: notyf-fadeoutright 0.3s forwards;
opacity: 1;
transform: translateX(0);
}
.notyf__toast--disappear .notyf__message {
-webkit-animation-delay: .05s;
animation-delay: .05s
-webkit-animation-delay: 0.05s;
animation-delay: 0.05s;
}
.notyf__toast--upper {
margin-bottom: 20px
margin-bottom: 20px;
}
.notyf__toast--lower {
margin-top: 20px
margin-top: 20px;
}
.notyf__toast--dismissible .notyf__wrapper {
padding-right: 30px
padding-right: 30px;
}
.notyf__ripple {
height: 400px;
width: 400px;
position: absolute;
transform-origin: bottom right;
right: 0;
top: 0;
border-radius: 50%;
transform: scale(0) translateY(-51%) translateX(13%);
z-index: 5;
-webkit-animation: ripple .4s ease-out forwards;
animation: ripple .4s ease-out forwards
height: 400px;
width: 400px;
position: absolute;
transform-origin: bottom right;
right: 0;
top: 0;
border-radius: 50%;
transform: scale(0) translateY(-51%) translateX(13%);
z-index: 5;
-webkit-animation: ripple 0.4s ease-out forwards;
animation: ripple 0.4s ease-out forwards;
}
.notyf__wrapper {
display: flex;
align-items: center;
padding-top: 17px;
padding-bottom: 17px;
padding-right: 15px;
border-radius: 3px;
position: relative;
z-index: 10
display: flex;
align-items: center;
padding-top: 17px;
padding-bottom: 17px;
padding-right: 15px;
border-radius: 3px;
position: relative;
z-index: 10;
}
.notyf__icon {
width: 22px;
text-align: center;
font-size: 1.3em;
opacity: 0;
-webkit-animation: notyf-fadeinup .3s forwards;
animation: notyf-fadeinup .3s forwards;
-webkit-animation-delay: .3s;
animation-delay: .3s;
margin-right: 13px
width: 22px;
text-align: center;
font-size: 1.3em;
opacity: 0;
-webkit-animation: notyf-fadeinup 0.3s forwards;
animation: notyf-fadeinup 0.3s forwards;
-webkit-animation-delay: 0.3s;
animation-delay: 0.3s;
margin-right: 13px;
}
.notyf__dismiss {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 26px;
margin-right: -15px;
-webkit-animation: notyf-fadeinleft .3s forwards;
animation: notyf-fadeinleft .3s forwards;
-webkit-animation-delay: .35s;
animation-delay: .35s;
opacity: 0
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 26px;
margin-right: -15px;
-webkit-animation: notyf-fadeinleft 0.3s forwards;
animation: notyf-fadeinleft 0.3s forwards;
-webkit-animation-delay: 0.35s;
animation-delay: 0.35s;
opacity: 0;
}
.notyf__dismiss-btn {
background-color: rgba(0, 0, 0, .25);
border: none;
cursor: pointer;
transition: opacity .2s ease, background-color .2s ease;
outline: none;
opacity: .35;
height: 100%;
width: 100%
background-color: rgba(0, 0, 0, 0.25);
border: none;
cursor: pointer;
transition: opacity 0.2s ease, background-color 0.2s ease;
outline: none;
opacity: 0.35;
height: 100%;
width: 100%;
}
.notyf__dismiss-btn:after,
.notyf__dismiss-btn:before {
content: "";
background: #fff;
height: 12px;
width: 2px;
border-radius: 3px;
position: absolute;
left: calc(50% - 1px);
top: calc(50% - 5px)
content: "";
background: #fff;
height: 12px;
width: 2px;
border-radius: 3px;
position: absolute;
left: calc(50% - 1px);
top: calc(50% - 5px);
}
.notyf__dismiss-btn:after {
transform: rotate(-45deg)
transform: rotate(-45deg);
}
.notyf__dismiss-btn:before {
transform: rotate(45deg)
transform: rotate(45deg);
}
.notyf__dismiss-btn:hover {
opacity: .7;
background-color: rgba(0, 0, 0, .15)
opacity: 0.7;
background-color: rgba(0, 0, 0, 0.15);
}
.notyf__dismiss-btn:active {
opacity: .8
opacity: 0.8;
}
.notyf__message {
vertical-align: middle;
position: relative;
opacity: 0;
-webkit-animation: notyf-fadeinup .3s forwards;
animation: notyf-fadeinup .3s forwards;
-webkit-animation-delay: .25s;
animation-delay: .25s;
line-height: 1.5em
vertical-align: middle;
position: relative;
opacity: 0;
-webkit-animation: notyf-fadeinup 0.3s forwards;
animation: notyf-fadeinup 0.3s forwards;
-webkit-animation-delay: 0.25s;
animation-delay: 0.25s;
line-height: 1.5em;
}
@media only screen and (max-width:480px) {
.notyf {
padding: 0
}
@media only screen and (max-width: 480px) {
.notyf {
padding: 0;
}
.notyf__ripple {
height: 600px;
width: 600px;
-webkit-animation-duration: .5s;
animation-duration: .5s
}
.notyf__ripple {
height: 600px;
width: 600px;
-webkit-animation-duration: 0.5s;
animation-duration: 0.5s;
}
.notyf__toast {
max-width: none;
border-radius: 0;
box-shadow: 0 -2px 7px 0 rgba(0, 0, 0, .13);
width: 100%
}
.notyf__toast {
max-width: none;
border-radius: 0;
box-shadow: 0 -2px 7px 0 rgba(0, 0, 0, 0.13);
width: 100%;
}
.notyf__dismiss {
width: 56px
}
}
.notyf__dismiss {
width: 56px;
}
}

View file

@ -1277,7 +1277,7 @@
}
.audiolabs-page .spprofile-line .spprofile-viewport .spprev:before,
.audiolabs-page .spprofile-line .spprofile-viewport .nextprev:before {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
@ -1309,7 +1309,7 @@
background: black;
}
.audiolabs-page .spprofile-line .spprofile-viewport .spslide > img {
WIDTH: 100%;
width: 100%;
height: 100%;
object-fit: cover;
}

View file

@ -242,7 +242,6 @@
}
.list-group-item {
&:hover {
cursor: grab;
}
@ -294,7 +293,6 @@
// Search Page
&.search-page {
.searchToggle {
float: right;
@ -302,7 +300,7 @@
min-width: 120px;
}
}
.categories{
.categories {
display: grid;
grid-template-columns: repeat(3, minmax(200px, 1fr));
gap: 1rem;
@ -316,7 +314,7 @@
width: 100% !important;
z-index: 1;
}
.info-rect{
.info-rect {
height: max-content;
}
.title {
@ -452,7 +450,7 @@
right: 0;
&:hover {
background-color: rgb(196, 43, 28)
background-color: rgb(196, 43, 28);
}
}
@ -488,10 +486,7 @@
display: block;
line-break: anywhere;
}
}
}
// Podcast Page
@ -622,7 +617,7 @@
right: 0;
&:hover {
background-color: rgb(196, 43, 28)
background-color: rgb(196, 43, 28);
}
}
@ -657,10 +652,7 @@
display: block;
line-break: anywhere;
}
}
}
@media only screen and (max-width: 1230px) {
@ -828,7 +820,7 @@
margin-bottom: -10px;
padding: 0;
-webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%);
opacity: .7;
opacity: 0.7;
animation: playlistArtworkFadeIn 1s var(--appleEase);
.artworkMaterial img {
@ -898,7 +890,7 @@
}
.search-input::placeholder {
color: var(--heroplaceholdercolor)
color: var(--heroplaceholdercolor);
}
.nameEdit {
@ -939,7 +931,7 @@
}
.playlist-desc {
transition: height .2s ease-in-out, opacity .2s ease-in-out;
transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
box-sizing: border-box;
font-size: 14px;
flex-shrink: unset;
@ -1043,8 +1035,6 @@
}
}
}
}
.friends-info {
@ -1061,7 +1051,7 @@
border-radius: 100%;
overflow: hidden;
box-shadow: var(--mediaItemShadow-ShadowSubtle);
transition: transform .2s var(--appleEase);
transition: transform 0.2s var(--appleEase);
margin: 6px;
&:hover {
@ -1081,7 +1071,7 @@
font-size: 0.9em;
margin: 6px;
opacity: 0.7;
transition: height .2s ease-in-out, opacity .2s ease-in-out;
transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
height: 0.9em;
}
@ -1151,13 +1141,13 @@
}
.playlist-time {
transition: height .2s ease-in-out, opacity .2s ease-in-out;
transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
height: 0px;
opacity: 0;
}
.playlist-desc {
transition: height .2s ease-in-out, opacity .2s ease-in-out;
transition: height 0.2s ease-in-out, opacity 0.2s ease-in-out;
height: 0px !important;
opacity: 0;
}
@ -1272,7 +1262,6 @@
}
}
.artworkContainer {
position: absolute;
top: 0;
@ -1282,7 +1271,7 @@
margin: 0;
padding: 0;
-webkit-mask-image: radial-gradient(at top left, black, transparent 70%), radial-gradient(at top right, black, transparent 70%), linear-gradient(180deg, rgb(200 200 200), transparent 98%);
opacity: .7;
opacity: 0.7;
animation: playlistArtworkFadeIn 1s var(--appleEase);
.artworkMaterial img {
@ -1412,7 +1401,6 @@
}
.artist-title {
.artist-play {
transform: translateY(3px);
margin: 14px;
@ -1490,7 +1478,6 @@
width: 90%;
margin: 16px auto 0px;
}
}
// AudioLabs page
@ -1502,7 +1489,7 @@
border-bottom: unset;
border-top: unset;
font-weight: 600;
font-size: 1.0em;
font-size: 1em;
background: rgb(255 255 255 / 3%);
}
@ -1548,7 +1535,7 @@
}
&:before {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
@ -1568,7 +1555,6 @@
&:before {
-webkit-mask-image: url("./views/svg/chevron-left.svg");
}
}
.nextprev {
@ -1577,7 +1563,6 @@
&:before {
-webkit-mask-image: url("./views/svg/chevron-right.svg");
}
}
.spslide {
@ -1588,7 +1573,7 @@
background: black;
> img {
WIDTH: 100%;
width: 100%;
height: 100%;
object-fit: cover;
}
@ -1646,7 +1631,6 @@
//Home
.home-page {
.md-btn-replay {
background-image: linear-gradient(-45deg, #2e2173, #925042);
animation: gradient-animation 5s ease-in-out infinite;
@ -1738,8 +1722,8 @@
border-radius: var(--mediaItemRadius);
overflow: hidden;
cursor: pointer;
transition: transform .2s var(--appleEase);
transition-delay: .1s;
transition: transform 0.2s var(--appleEase);
transition-delay: 0.1s;
align-self: center;
&:hover {
@ -1780,7 +1764,6 @@
}
.top-genres-container {
.genre-name {
font-size: 0.9em;
margin: 6px 0px;
@ -1810,11 +1793,11 @@
.cd-mediaitem-square {
.mediaitem-artwork {
animation: replayFadeIn .5s var(--appleEase);
animation: replayFadeIn 0.5s var(--appleEase);
}
transition: transform .2s var(--appleEase);
transition-delay: .1s;
transition: transform 0.2s var(--appleEase);
transition-delay: 0.1s;
&:hover {
transform: scale(1.1);
@ -1883,7 +1866,6 @@
height: 100%;
width: 100%;
.oobe-header {
font-size: 3em;
text-shadow: var(--replayTextShadow);
@ -1927,7 +1909,7 @@
.visualPreview {
pointer-events: none;
transition: .25s all;
transition: 0.25s all;
width: 100%;
}
@ -1954,9 +1936,8 @@
outline: 4px solid var(--keyColor);
}
&:hover {
transform: scale(1.10) translateZ(-1px) translateY(10px);
transform: scale(1.1) translateZ(-1px) translateY(10px);
z-index: 1;
box-shadow: 0px 12px 16px rgb(0 0 0 / 25%);
}
@ -1981,10 +1962,8 @@
text-align: center;
}
}
}
.oobe-titlebar {
position: absolute;
top: 0;
@ -2125,7 +2104,6 @@
.nav-pills {
gap: 6px;
}
.nav-pills .nav-link {
@ -2139,7 +2117,6 @@
}
}
.md-option-header {
padding: 0px 26px;
border-bottom: unset;
@ -2192,7 +2169,7 @@
}
&:hover {
background-color: rgb(196, 43, 28)
background-color: rgb(196, 43, 28);
}
&.back-btn {
@ -2241,35 +2218,36 @@
overflow-y: overlay;
height: 100%;
background-color: var(--panelColor2);
padding:0px;
padding: 0px;
padding-top: 48px;
border-left: 1px solid var(--borderColor);
}
.github-themes-page, .installed-themes-page {
.github-themes-page,
.installed-themes-page {
.header-text {
font-size: 1.25em;
}
}
.tab-pane {
height:100%;
height: 100%;
}
.settings-tab-content {
height:100%;
height: 100%;
}
&.no-sidebar {
.gh-header {
>.row {
> .row {
&:last-child {
padding-right: 90px;
}
}
}
.tab-content {
padding-top:0px;
padding-top: 0px;
}
.tabs {
@ -2277,10 +2255,10 @@
width: 50px;
:nth-child(2) {
// font-size: 0px;
opacity:0;
opacity: 0;
}
}
>.col-auto {
> .col-auto {
width: 80px;
}
}
@ -2290,4 +2268,4 @@
#hid___BV_tab_button__ {
display: none;
}
}

View file

@ -1,48 +1,46 @@
import { app } from "./vueapp.js"
import {CiderCache} from './cidercache.js'
import {CiderFrontAPI} from './ciderfrontapi.js'
import {simulateGamepad} from './gamepad.js'
import {CiderAudio} from '../audio/cideraudio.js'
import {Events} from './events.js'
import { wsapi } from "./wsapi_interop.js"
import { MusicKitTools } from "./musickittools.js"
import { spawnMica } from "./mica.js"
import { svgIcon } from './components/svg-icon.js'
import { sidebarLibraryItem } from './components/sidebar-library-item.js'
import { app } from "./vueapp.js";
import { CiderCache } from "./cidercache.js";
import { CiderFrontAPI } from "./ciderfrontapi.js";
import { simulateGamepad } from "./gamepad.js";
import { CiderAudio } from "../audio/cideraudio.js";
import { Events } from "./events.js";
import { wsapi } from "./wsapi_interop.js";
import { MusicKitTools } from "./musickittools.js";
import { spawnMica } from "./mica.js";
import { svgIcon } from "./components/svg-icon.js";
import { sidebarLibraryItem } from "./components/sidebar-library-item.js";
// Define window objects
window.app = app
window.MusicKitTools = MusicKitTools
window.CiderAudio = CiderAudio
window.CiderCache = CiderCache
window.CiderFrontAPI = CiderFrontAPI
window.wsapi = wsapi
window.app = app;
window.MusicKitTools = MusicKitTools;
window.CiderAudio = CiderAudio;
window.CiderCache = CiderCache;
window.CiderFrontAPI = CiderFrontAPI;
window.wsapi = wsapi;
if (app.cfg.advanced.disableLogging === true) {
window.console = {
log: function() {},
error: function() {},
warn: function() {},
assert: function() {},
debug: function() {}
}
window.console = {
log: function () {},
error: function () {},
warn: function () {},
assert: function () {},
debug: function () {},
};
}
// Mount Vue to #app
app.$mount("#app")
app.$mount("#app");
// Init CiderAudio and force audiocontext
if (app.cfg.advanced.AudioContext != true) {
app.cfg.advanced.AudioContext = true;
window.location.reload();
app.cfg.advanced.AudioContext = true;
window.location.reload();
}
CiderAudio.init()
CiderAudio.init();
// Import gamepad support
app.simulateGamepad = simulateGamepad
app.spawnMica = spawnMica
app.simulateGamepad = simulateGamepad;
app.spawnMica = spawnMica;
Events.InitEvents()
Events.InitEvents();

View file

@ -1,24 +1,24 @@
const CiderCache = {
async getCache(file) {
let cache = await ipcRenderer.sendSync("get-cache", file)
if (isJson(cache)) {
cache = JSON.parse(cache)
if (Object.keys(cache).length === 0) {
cache = false
}
} else {
cache = false
}
return cache
},
async putCache(file, data) {
console.log(`Caching ${file}`)
ipcRenderer.invoke("put-cache", {
file: file,
data: JSON.stringify(data)
})
return true
async getCache(file) {
let cache = await ipcRenderer.sendSync("get-cache", file);
if (isJson(cache)) {
cache = JSON.parse(cache);
if (Object.keys(cache).length === 0) {
cache = false;
}
} else {
cache = false;
}
}
return cache;
},
async putCache(file, data) {
console.log(`Caching ${file}`);
ipcRenderer.invoke("put-cache", {
file: file,
data: JSON.stringify(data),
});
return true;
},
};
export {CiderCache}
export { CiderCache };

View file

@ -1,32 +1,31 @@
const CiderFrontAPI = {
Objects: {
MenuEntry: function () {
this.id = ""
this.name = ""
this.onClick = () => {
}
}
Objects: {
MenuEntry: function () {
this.id = "";
this.name = "";
this.onClick = () => {};
},
AddMenuEntry(entry) {
app.pluginMenuEntries.push(entry)
app.pluginInstalled = true
},
AddMenuEntry(entry) {
app.pluginMenuEntries.push(entry);
app.pluginInstalled = true;
},
StyleSheets: {
Add(href) {
console.log("Adding stylesheet: " + href);
let id = uuidv4();
let link = document.createElement("link");
link.rel = "stylesheet/less";
link.type = "text/css";
link.href = href;
link.setAttribute("css-id", id);
// insert the link before document.querySelector("#userTheme") in head
document.querySelector("head").insertBefore(link, document.querySelector("#userTheme"));
less.registerStylesheetsImmediately();
less.refresh(true, true, true);
return link;
},
StyleSheets: {
Add(href) {
console.log("Adding stylesheet: " + href)
let id = uuidv4()
let link = document.createElement("link")
link.rel = "stylesheet/less"
link.type = "text/css"
link.href = href
link.setAttribute("css-id", id)
// insert the link before document.querySelector("#userTheme") in head
document.querySelector("head").insertBefore(link, document.querySelector("#userTheme"))
less.registerStylesheetsImmediately()
less.refresh(true, true, true)
return link
}
}
}
},
};
export {CiderFrontAPI}
export { CiderFrontAPI };

View file

@ -1,46 +1,45 @@
import {html} from "../html.js"
import { html } from "../html.js";
export const sidebarLibraryItem = Vue.component("sidebar-library-item", {
template: html`
<button class="app-sidebar-item"
:class="$root.getSidebarItemClass(page)" @click="$root.setWindowHash(page)">
<svg-icon :url="svgIconData" :name="'sidebar-' + svgIconName" v-if="svgIconData != ''"/>
<span class="sidebar-item-text">{{ name }}</span>
</button>
`,
props: {
name: {
type: String,
required: true,
},
page: {
type: String,
required: true,
},
svgIcon: {
type: String,
required: false,
default: "",
},
svgIconName: {
type: String,
required: false
},
cdClick: {
type: Function,
required: false,
},
template: html`
<button class="app-sidebar-item" :class="$root.getSidebarItemClass(page)" @click="$root.setWindowHash(page)">
<svg-icon :url="svgIconData" :name="'sidebar-' + svgIconName" v-if="svgIconData != ''" />
<span class="sidebar-item-text">{{ name }}</span>
</button>
`,
props: {
name: {
type: String,
required: true,
},
data: function () {
return {
app: app,
svgIconData: "",
};
page: {
type: String,
required: true,
},
async mounted() {
if (this.svgIcon) {
this.svgIconData = this.svgIcon;
}
svgIcon: {
type: String,
required: false,
default: "",
},
methods: {},
})
svgIconName: {
type: String,
required: false,
},
cdClick: {
type: Function,
required: false,
},
},
data: function () {
return {
app: app,
svgIconData: "",
};
},
async mounted() {
if (this.svgIcon) {
this.svgIconData = this.svgIcon;
}
},
methods: {},
});

View file

@ -1,22 +1,20 @@
import {html} from "../html.js"
import { html } from "../html.js";
export const svgIcon = Vue.component("svg-icon", {
template: html`
<div class="_svg-icon" :class="classes" :svg-name="name" :style="{'--icon': 'url(' + url + ')'}"></div>
`,
props: {
name: {
type: String,
required: false
},
classes: {
type: String,
required: false
},
url: {
type: String,
required: true,
default: "./assets/repeat.svg"
}
}
})
template: html` <div class="_svg-icon" :class="classes" :svg-name="name" :style="{'--icon': 'url(' + url + ')'}"></div> `,
props: {
name: {
type: String,
required: false,
},
classes: {
type: String,
required: false,
},
url: {
type: String,
required: true,
default: "./assets/repeat.svg",
},
},
});

View file

@ -1,98 +1,94 @@
const Events = {
InitEvents() {
const app = window.app
InitEvents() {
const app = window.app;
// add event listener for when window.location.hash changes
window.addEventListener("hashchange", function () {
app.page = "blank"
setTimeout(()=>{
app.appRoute(window.location.hash)
}, 100)
// add event listener for when window.location.hash changes
window.addEventListener("hashchange", function () {
app.page = "blank";
setTimeout(() => {
app.appRoute(window.location.hash);
}, 100);
});
window.addEventListener("mouseup", (e) => {
if (e.button === 3) {
e.preventDefault();
app.navigateBack();
} else if (e.button === 4) {
e.preventDefault();
app.navigateForward();
}
});
document.addEventListener("keydown", async function (event) {
// CTRL + R
if (event.keyCode === 82 && event.ctrlKey) {
event.preventDefault();
app.confirm(app.getLz("term.reload"), (res) => {
if (res) {
window.location.reload();
}
});
window.addEventListener("mouseup", (e) => {
if (e.button === 3) {
e.preventDefault()
app.navigateBack()
} else if (e.button === 4) {
e.preventDefault()
app.navigateForward()
}
}
// CTRL + SHIFT + R
if (event.keyCode === 82 && event.ctrlKey && event.shiftKey) {
event.preventDefault();
window.location.reload();
}
// CTRL + E
if (event.keyCode === 69 && event.ctrlKey) {
app.invokeDrawer("queue");
}
// CTRL+H
if (event.keyCode === 72 && event.ctrlKey) {
app.appRoute("home");
}
// CTRL+SHIFT+H
if (event.ctrlKey && event.shiftKey && event.keyCode == 72) {
let hist = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, {
l: app.mklang,
});
app.showCollection(hist.data, app.getLz("term.history"));
}
if (event.ctrlKey && event.keyCode == 121) {
try {
app.mk._services.mediaItemPlayback._currentPlayer.stop();
} catch (e) {}
try {
app.mk._services.mediaItemPlayback._currentPlayer.destroy();
} catch (e) {}
}
if (event.ctrlKey && event.keyCode == 122) {
try {
ipcRenderer.send("detachDT", "");
} catch (e) {}
}
// Prevent Scrolling on spacebar
if (event.keyCode === 32 && event.target === document.body) {
event.preventDefault();
app.SpacePause();
}
});
document.addEventListener('keydown', async function (event) {
// CTRL + R
if (event.keyCode === 82 && event.ctrlKey) {
event.preventDefault()
app.confirm(app.getLz('term.reload'), (res)=>{
if (res) {
window.location.reload()
}
})
}
// CTRL + SHIFT + R
if (event.keyCode === 82 && event.ctrlKey && event.shiftKey) {
event.preventDefault()
window.location.reload()
}
// CTRL + E
if (event.keyCode === 69 && event.ctrlKey) {
app.invokeDrawer('queue')
}
// CTRL+H
if (event.keyCode === 72 && event.ctrlKey) {
app.appRoute("home")
}
// CTRL+SHIFT+H
if (event.ctrlKey && event.shiftKey && event.keyCode == 72) {
let hist = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, {
l: app.mklang
})
app.showCollection(hist.data, app.getLz('term.history'))
}
if (event.ctrlKey && event.keyCode == 121) {
try {
app.mk._services.mediaItemPlayback._currentPlayer.stop()
} catch (e) {
}
try {
app.mk._services.mediaItemPlayback._currentPlayer.destroy()
} catch (e) {
}
}
if (event.ctrlKey && event.keyCode == 122) {
try {
ipcRenderer.send('detachDT', '')
} catch (e) {
}
}
// Prevent Scrolling on spacebar
if (event.keyCode === 32 && event.target === document.body) {
event.preventDefault()
app.SpacePause()
// Hang Timer
app.hangtimer = setTimeout(() => {
if (confirm("Cider is not responding. Reload the app?")) {
window.location.reload();
}
}, 10000);
}
});
// Hang Timer
app.hangtimer = setTimeout(() => {
if (confirm("Cider is not responding. Reload the app?")) {
window.location.reload()
}
}, 10000)
// Refresh Focus
function refreshFocus() {
if (document.hasFocus() == false) {
app.windowFocus(false)
} else {
app.windowFocus(true)
}
setTimeout(refreshFocus, 200);
}
refreshFocus();
// Refresh Focus
function refreshFocus() {
if (document.hasFocus() == false) {
app.windowFocus(false);
} else {
app.windowFocus(true);
}
setTimeout(refreshFocus, 200);
}
}
export {Events}
refreshFocus();
},
};
export { Events };

View file

@ -1,327 +1,313 @@
function simulateGamepad () {
const app = window.app
app.chrome.showCursor = true
let cursorPos = [0, 0];
let intTabIndex = 0
const cursorSpeedPvt = 8
const cursorSize = 16
let scrollSpeed = 8
let buttonPressDelay = 500
let stickDeadZone = 0.2
let scrollGroup = null
let scrollGroupY = null
let elementFocusEnabled = true
let start;
function simulateGamepad() {
const app = window.app;
app.chrome.showCursor = true;
let cursorPos = [0, 0];
let intTabIndex = 0;
const cursorSpeedPvt = 8;
const cursorSize = 16;
let scrollSpeed = 8;
let buttonPressDelay = 500;
let stickDeadZone = 0.2;
let scrollGroup = null;
let scrollGroupY = null;
let elementFocusEnabled = true;
let start;
let cursorSpeed = cursorSpeedPvt
let cursorSpeed = cursorSpeedPvt;
let lastButtonPress = {
let lastButtonPress = {};
var sounds = {
Confirm: new Audio("./sounds/confirm.ogg"),
Menu: new Audio("./sounds/btn1.ogg"),
Hover: new Audio("./sounds/hover.ogg"),
};
let element = document.elementFromPoint(0, 0);
let elementType = 0;
function appLoop() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : [];
if (!gamepads) {
return;
}
var sounds = {
Confirm: new Audio("./sounds/confirm.ogg"),
Menu: new Audio("./sounds/btn1.ogg"),
Hover: new Audio("./sounds/hover.ogg")
var gp = gamepads[0];
// LEFT STICK
if (gp.axes[0] > stickDeadZone) {
cursorPos[0] += gp.axes[0] * cursorSpeed;
} else if (gp.axes[0] < -stickDeadZone) {
cursorPos[0] += gp.axes[0] * cursorSpeed;
}
let element = document.elementFromPoint(0, 0)
let elementType = 0
function appLoop() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
if (!gamepads) {
return;
}
var gp = gamepads[0];
// LEFT STICK
if (gp.axes[0] > stickDeadZone) {
cursorPos[0] += (gp.axes[0] * cursorSpeed)
} else if (gp.axes[0] < -stickDeadZone) {
cursorPos[0] += (gp.axes[0] * cursorSpeed)
}
if (gp.axes[1] > stickDeadZone) {
cursorPos[1] += (gp.axes[1] * cursorSpeed)
} else if (gp.axes[1] < -stickDeadZone) {
cursorPos[1] += (gp.axes[1] * cursorSpeed)
}
if (cursorPos[0] < cursorSize) {
cursorPos[0] = cursorSize
}
if (cursorPos[1] < cursorSize) {
cursorPos[1] = cursorSize
}
if (cursorPos[0] > window.innerWidth - cursorSize) {
cursorPos[0] = window.innerWidth - cursorSize
}
if (cursorPos[1] > window.innerHeight - cursorSize) {
cursorPos[1] = window.innerHeight - cursorSize
}
// RIGHT STICK.
if (scrollGroupY) {
if (gp.axes[3] > stickDeadZone) {
$(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed))
elementFocusEnabled = false
} else if (gp.axes[3] < -stickDeadZone) {
$(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + (gp.axes[3] * scrollSpeed))
elementFocusEnabled = false
} else {
elementFocusEnabled = true
}
}
if (scrollGroup) {
if (gp.axes[2] > stickDeadZone) {
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
elementFocusEnabled = false
} else if (gp.axes[2] < -stickDeadZone) {
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + (gp.axes[2] * scrollSpeed))
elementFocusEnabled = false
} else {
elementFocusEnabled = true
}
}
$(".cursor").css({
top: cursorPos[1] + "px",
left: cursorPos[0] + "px",
display: "block"
})
// A BUTTON
if (gp.buttons[0].pressed) {
if (!lastButtonPress["A"]) {
lastButtonPress["A"] = 0
}
if (Date.now() - lastButtonPress["A"] > buttonPressDelay) {
lastButtonPress["A"] = Date.now()
sounds.Confirm.play()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click"))
document.activeElement.dispatchEvent(new Event("controller-click"))
} else {
element.dispatchEvent(new Event("click"))
element.dispatchEvent(new Event("controller-click"))
}
}
}
// B BUTTON
if (gp.buttons[1].pressed) {
if (!lastButtonPress["B"]) {
lastButtonPress["B"] = 0
}
if (Date.now() - lastButtonPress["B"] > buttonPressDelay) {
lastButtonPress["B"] = Date.now()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu"))
setTimeout(() => {
if ($(".menu-option").length > 0) {
let bounds = $(".menu-option")[0].getBoundingClientRect()
cursorPos[0] = bounds.left + (bounds.width / 2)
cursorPos[1] = bounds.top + (bounds.height / 2)
}
}, 100)
} else {
element.dispatchEvent(new Event("contextmenu"))
}
}
}
// right bumper
if (gp.buttons[5].pressed) {
if (!lastButtonPress["RB"]) {
lastButtonPress["RB"] = 0
}
if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) {
lastButtonPress["RB"] = Date.now()
app.navigateForward()
}
}
// left bumper
if (gp.buttons[4].pressed) {
if (!lastButtonPress["LB"]) {
lastButtonPress["LB"] = 0
}
if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) {
lastButtonPress["LB"] = Date.now()
app.navigateBack()
}
}
// cursor hover
if (elementFocusEnabled) {
element = document.elementFromPoint(cursorPos[0], cursorPos[1])
}
if (element) {
let closest = element.closest("[tabindex], input, button, a")
// VERT SCROLL
let scrollGroupCloY = element.closest(`[scrollaxis="y"]`)
if (scrollGroupCloY) {
scrollGroupY = scrollGroupCloY
}
// HOZ SCROLL
let scrollGroupClo = element.closest(".v-hl-container")
if (scrollGroupClo) {
if (scrollGroupClo.classList.contains("v-hl-container")) {
scrollGroup = scrollGroupClo
scrollGroup.style["scroll-snap-type"] = "unset"
} else {
scrollGroup.style["scroll-snap-type"] = ""
scrollGroup = null
}
}
if (closest) {
elementType = 0
closest.focus()
} else {
if (closest) {
closest.blur()
}
elementType = 1
element.focus()
}
cursorSpeed = cursorSpeedPvt
if (!element.classList.contains("app-chrome")
&& !element.classList.contains("app-content")) {
cursorSpeed = cursorSpeedPvt
}
// console.log($._data($(element), "events"))
} else {
cursorSpeed = 12
}
// console.log(gp.axes[0], gp.axes[1])
start = requestAnimationFrame(appLoop);
if (gp.axes[1] > stickDeadZone) {
cursorPos[1] += gp.axes[1] * cursorSpeed;
} else if (gp.axes[1] < -stickDeadZone) {
cursorPos[1] += gp.axes[1] * cursorSpeed;
}
// controller pairing
notyf.error("Press the button on your controller to pair it to Cider.")
window.addEventListener("gamepadconnected", function (e) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.",
e.gamepad.index, e.gamepad.id,
e.gamepad.buttons.length, e.gamepad.axes.length);
notyf.success("Pairing successful!")
appLoop()
}, { once: true });
if (cursorPos[0] < cursorSize) {
cursorPos[0] = cursorSize;
}
if (cursorPos[1] < cursorSize) {
cursorPos[1] = cursorSize;
}
if (cursorPos[0] > window.innerWidth - cursorSize) {
cursorPos[0] = window.innerWidth - cursorSize;
}
if (cursorPos[1] > window.innerHeight - cursorSize) {
cursorPos[1] = window.innerHeight - cursorSize;
}
document.addEventListener("keydown", (e) => {
sounds.Confirm.currentTime = 0
sounds.Menu.currentTime = 0
sounds.Hover.currentTime = 0
let tabbable = $("[tabindex]")
console.log(e.key)
switch (e.key) {
default:
break;
case "ArrowLeft":
e.preventDefault()
// RIGHT STICK.
if (scrollGroupY) {
if (gp.axes[3] > stickDeadZone) {
$(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + gp.axes[3] * scrollSpeed);
elementFocusEnabled = false;
} else if (gp.axes[3] < -stickDeadZone) {
$(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + gp.axes[3] * scrollSpeed);
elementFocusEnabled = false;
} else {
elementFocusEnabled = true;
}
}
cursorPos[0] -= cursorSpeed
break;
case "ArrowRight":
e.preventDefault()
if (scrollGroup) {
if (gp.axes[2] > stickDeadZone) {
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + gp.axes[2] * scrollSpeed);
elementFocusEnabled = false;
} else if (gp.axes[2] < -stickDeadZone) {
$(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + gp.axes[2] * scrollSpeed);
elementFocusEnabled = false;
} else {
elementFocusEnabled = true;
}
}
cursorPos[0] += cursorSpeed
break;
case "ArrowUp":
e.preventDefault()
cursorPos[1] -= cursorSpeed
// sounds.Hover.play()
// if (intTabIndex <= 0) {
// intTabIndex = 0
// } else {
// intTabIndex--
// }
// $(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
break;
case "ArrowDown":
e.preventDefault()
cursorPos[1] += cursorSpeed
// if (intTabIndex < tabbable.length) {
// intTabIndex++
// } else {
// intTabIndex = tabbable.length
// }
// $(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
break;
case "c":
app.resetState()
break;
case "x":
// set cursorPos to the top right of the screen
// sounds.Menu.play()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu"))
} else {
element.dispatchEvent(new Event("contextmenu"))
}
e.preventDefault()
break;
case "z":
sounds.Confirm.play()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click"))
document.activeElement.dispatchEvent(new Event("controller-click"))
} else {
element.dispatchEvent(new Event("click"))
element.dispatchEvent(new Event("controller-click"))
}
e.preventDefault()
break;
}
$(".cursor").css({
top: cursorPos[1] + "px",
left: cursorPos[0] + "px"
})
function lerp(a, b, n) {
return (1 - n) * a + n * b
}
element = document.elementFromPoint(cursorPos[0], cursorPos[1])
if (element) {
let closest = element.closest("[tabindex], input, button, a")
if (closest) {
elementType = 0
closest.focus()
} else {
elementType = 1
element.focus()
}
}
console.log(element)
$(".cursor").css({
top: cursorPos[1] + "px",
left: cursorPos[0] + "px",
display: "block",
});
// A BUTTON
if (gp.buttons[0].pressed) {
if (!lastButtonPress["A"]) {
lastButtonPress["A"] = 0;
}
if (Date.now() - lastButtonPress["A"] > buttonPressDelay) {
lastButtonPress["A"] = Date.now();
sounds.Confirm.play();
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click"));
document.activeElement.dispatchEvent(new Event("controller-click"));
} else {
element.dispatchEvent(new Event("click"));
element.dispatchEvent(new Event("controller-click"));
}
}
}
// B BUTTON
if (gp.buttons[1].pressed) {
if (!lastButtonPress["B"]) {
lastButtonPress["B"] = 0;
}
if (Date.now() - lastButtonPress["B"] > buttonPressDelay) {
lastButtonPress["B"] = Date.now();
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu"));
setTimeout(() => {
if ($(".menu-option").length > 0) {
let bounds = $(".menu-option")[0].getBoundingClientRect();
cursorPos[0] = bounds.left + bounds.width / 2;
cursorPos[1] = bounds.top + bounds.height / 2;
}
}, 100);
} else {
element.dispatchEvent(new Event("contextmenu"));
}
}
}
// right bumper
if (gp.buttons[5].pressed) {
if (!lastButtonPress["RB"]) {
lastButtonPress["RB"] = 0;
}
if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) {
lastButtonPress["RB"] = Date.now();
app.navigateForward();
}
}
// left bumper
if (gp.buttons[4].pressed) {
if (!lastButtonPress["LB"]) {
lastButtonPress["LB"] = 0;
}
if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) {
lastButtonPress["LB"] = Date.now();
app.navigateBack();
}
}
// cursor hover
if (elementFocusEnabled) {
element = document.elementFromPoint(cursorPos[0], cursorPos[1]);
}
if (element) {
let closest = element.closest("[tabindex], input, button, a");
// VERT SCROLL
let scrollGroupCloY = element.closest(`[scrollaxis="y"]`);
if (scrollGroupCloY) {
scrollGroupY = scrollGroupCloY;
}
// HOZ SCROLL
let scrollGroupClo = element.closest(".v-hl-container");
if (scrollGroupClo) {
if (scrollGroupClo.classList.contains("v-hl-container")) {
scrollGroup = scrollGroupClo;
scrollGroup.style["scroll-snap-type"] = "unset";
} else {
scrollGroup.style["scroll-snap-type"] = "";
scrollGroup = null;
}
}
if (closest) {
elementType = 0;
closest.focus();
} else {
if (closest) {
closest.blur();
}
elementType = 1;
element.focus();
}
cursorSpeed = cursorSpeedPvt;
if (!element.classList.contains("app-chrome") && !element.classList.contains("app-content")) {
cursorSpeed = cursorSpeedPvt;
}
// console.log($._data($(element), "events"))
} else {
cursorSpeed = 12;
}
// console.log(gp.axes[0], gp.axes[1])
start = requestAnimationFrame(appLoop);
}
// controller pairing
notyf.error("Press the button on your controller to pair it to Cider.");
window.addEventListener(
"gamepadconnected",
function (e) {
console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", e.gamepad.index, e.gamepad.id, e.gamepad.buttons.length, e.gamepad.axes.length);
notyf.success("Pairing successful!");
appLoop();
},
{ once: true }
);
document.addEventListener("keydown", (e) => {
sounds.Confirm.currentTime = 0;
sounds.Menu.currentTime = 0;
sounds.Hover.currentTime = 0;
let tabbable = $("[tabindex]");
console.log(e.key);
switch (e.key) {
default:
break;
case "ArrowLeft":
e.preventDefault();
cursorPos[0] -= cursorSpeed;
break;
case "ArrowRight":
e.preventDefault();
cursorPos[0] += cursorSpeed;
break;
case "ArrowUp":
e.preventDefault();
cursorPos[1] -= cursorSpeed;
// sounds.Hover.play()
// if (intTabIndex <= 0) {
// intTabIndex = 0
// } else {
// intTabIndex--
// }
// $(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
break;
case "ArrowDown":
e.preventDefault();
cursorPos[1] += cursorSpeed;
// if (intTabIndex < tabbable.length) {
// intTabIndex++
// } else {
// intTabIndex = tabbable.length
// }
// $(tabbable[intTabIndex]).focus()
// $("#app-content").scrollTop($(document.activeElement).offset().top)
break;
case "c":
app.resetState();
break;
case "x":
// set cursorPos to the top right of the screen
// sounds.Menu.play()
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("contextmenu"));
} else {
element.dispatchEvent(new Event("contextmenu"));
}
e.preventDefault();
break;
case "z":
sounds.Confirm.play();
if (elementType == 0) {
document.activeElement.dispatchEvent(new Event("click"));
document.activeElement.dispatchEvent(new Event("controller-click"));
} else {
element.dispatchEvent(new Event("click"));
element.dispatchEvent(new Event("controller-click"));
}
e.preventDefault();
break;
}
$(".cursor").css({
top: cursorPos[1] + "px",
left: cursorPos[0] + "px",
});
function lerp(a, b, n) {
return (1 - n) * a + n * b;
}
element = document.elementFromPoint(cursorPos[0], cursorPos[1]);
if (element) {
let closest = element.closest("[tabindex], input, button, a");
if (closest) {
elementType = 0;
closest.focus();
} else {
elementType = 1;
element.focus();
}
}
console.log(element);
});
}
export {simulateGamepad}
export { simulateGamepad };

View file

@ -1,3 +1,3 @@
export function html (str) {
return str[0]
}
export function html(str) {
return str[0];
}

View file

@ -31,9 +31,9 @@ async function spawnMica() {
}
if (micaCache.path == imgSrc.path) {
imgSrc = micaCache;
}else{
} else {
imgSrc = await ipcRenderer.sendSync("get-wallpaper", {
blurAmount: 256
blurAmount: 256,
});
CiderCache.putCache("mica-cache", imgSrc);
}
@ -51,10 +51,7 @@ async function spawnMica() {
cb();
}
// window size change
if (
lastScreenWidth !== window.innerWidth ||
lastScreenHeight !== window.innerHeight
) {
if (lastScreenWidth !== window.innerWidth || lastScreenHeight !== window.innerHeight) {
lastScreenWidth = window.innerWidth;
lastScreenHeight = window.innerHeight;
cb();

View file

@ -1,56 +1,44 @@
const MusicKitTools = {
async v3Backend({
route = "", getBody = {}, options = {}
}) {
return await (await ipcRenderer.invoke("mkv3", {
token: MusicKit.getInstance().developerToken,
route: route,
mediaToken: MusicKit.getInstance().musicUserToken,
GETBody: getBody
}))
},
async v3Continuous({
href,
options = {},
reqOptions = {},
onProgress = () => {
},
onError = () => {
},
onSuccess = () => {
}
} = {}) {
let returnData = []
async v3Backend({ route = "", getBody = {}, options = {} }) {
return await await ipcRenderer.invoke("mkv3", {
token: MusicKit.getInstance().developerToken,
route: route,
mediaToken: MusicKit.getInstance().musicUserToken,
GETBody: getBody,
});
},
async v3Continuous({ href, options = {}, reqOptions = {}, onProgress = () => {}, onError = () => {}, onSuccess = () => {} } = {}) {
let returnData = [];
async function sendReq(href, options) {
const response = await app.mk.api.v3.music(href, options).catch(error => onError)
async function sendReq(href, options) {
const response = await app.mk.api.v3.music(href, options).catch((error) => onError);
returnData = returnData.concat(response.data.data)
if (response.data.next) {
onProgress({
response: response,
total: returnData.length
})
try {
await sendReq(response.data.next, options)
} catch (e) {
await sendReq(response.data.next, options)
}
}
}
await sendReq(href, options)
onSuccess(returnData)
return returnData
},
getHeader() {
return new Headers({
Authorization: 'Bearer ' + MusicKit.getInstance().developerToken,
Accept: 'application/json',
'Content-Type': 'application/json',
'Music-User-Token': '' + MusicKit.getInstance().musicUserToken
returnData = returnData.concat(response.data.data);
if (response.data.next) {
onProgress({
response: response,
total: returnData.length,
});
try {
await sendReq(response.data.next, options);
} catch (e) {
await sendReq(response.data.next, options);
}
}
}
}
export {MusicKitTools}
await sendReq(href, options);
onSuccess(returnData);
return returnData;
},
getHeader() {
return new Headers({
Authorization: "Bearer " + MusicKit.getInstance().developerToken,
Accept: "application/json",
"Content-Type": "application/json",
"Music-User-Token": "" + MusicKit.getInstance().musicUserToken,
});
},
};
export { MusicKitTools };

File diff suppressed because it is too large Load diff

View file

@ -1,38 +1,38 @@
const store = new Vuex.Store({
state: {
windowRelativeScale: 1,
library: {
// songs: ipcRenderer.sendSync("get-library-songs"),
// albums: ipcRenderer.sendSync("get-library-albums"),
// recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"),
// playlists: ipcRenderer.sendSync("get-library-playlists")
},
pageState: {
recentlyAdded: {
loaded: false,
nextUrl: null,
items: [],
size: "normal"
},
settings: {
currentTabIndex: 0,
fullscreen: false
}
},
artwork: {
playerLCD: ""
}
state: {
windowRelativeScale: 1,
library: {
// songs: ipcRenderer.sendSync("get-library-songs"),
// albums: ipcRenderer.sendSync("get-library-albums"),
// recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"),
// playlists: ipcRenderer.sendSync("get-library-playlists")
},
mutations: {
resetRecentlyAdded(state) {
state.pageState.recentlyAdded.loaded = false;
state.pageState.recentlyAdded.nextUrl = null;
state.pageState.recentlyAdded.items = [];
},
setLCDArtwork(state, artwork) {
state.artwork.playerLCD = artwork
}
}
})
pageState: {
recentlyAdded: {
loaded: false,
nextUrl: null,
items: [],
size: "normal",
},
settings: {
currentTabIndex: 0,
fullscreen: false,
},
},
artwork: {
playerLCD: "",
},
},
mutations: {
resetRecentlyAdded(state) {
state.pageState.recentlyAdded.loaded = false;
state.pageState.recentlyAdded.nextUrl = null;
state.pageState.recentlyAdded.items = [];
},
setLCDArtwork(state, artwork) {
state.artwork.playerLCD = artwork;
},
},
});
export {store}
export { store };

View file

@ -1,194 +1,230 @@
const wsapi = {
cache: {playParams: {id: 0}, status: null, remainingTime: 0},
playbackCache: {status: null, time: Date.now()},
async v3(encoded = "") {
let decoded = atob(encoded);
let json = JSON.parse(decoded);
console.log(json)
let response = await (await MusicKit.getInstance().api.v3.music(json.route, json.body, json.options))
let ret = response.data
return JSON.stringify(ret)
},
search(term, limit) {
MusicKit.getInstance().api.search(term, {limit: limit, types: 'songs,artists,albums,playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearch', JSON.stringify(results))
})
},
searchLibrary(term, limit) {
MusicKit.getInstance().api.library.search(term, {limit: limit, types: 'library-songs,library-artists,library-albums,library-playlists'}).then((results)=>{
ipcRenderer.send('wsapi-returnSearchLibrary', JSON.stringify(results))
})
},
getAttributes: function () {
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = (nowPlayingItem != null ? nowPlayingItem.attributes : {});
cache: { playParams: { id: 0 }, status: null, remainingTime: 0 },
playbackCache: { status: null, time: Date.now() },
async v3(encoded = "") {
let decoded = atob(encoded);
let json = JSON.parse(decoded);
console.log(json);
let response = await await MusicKit.getInstance().api.v3.music(json.route, json.body, json.options);
let ret = response.data;
return JSON.stringify(ret);
},
search(term, limit) {
MusicKit.getInstance()
.api.search(term, {
limit: limit,
types: "songs,artists,albums,playlists",
})
.then((results) => {
ipcRenderer.send("wsapi-returnSearch", JSON.stringify(results));
});
},
searchLibrary(term, limit) {
MusicKit.getInstance()
.api.library.search(term, {
limit: limit,
types: "library-songs,library-artists,library-albums,library-playlists",
})
.then((results) => {
ipcRenderer.send("wsapi-returnSearchLibrary", JSON.stringify(results));
});
},
getAttributes: function () {
const mk = MusicKit.getInstance();
const nowPlayingItem = mk.nowPlayingItem;
const isPlayingExport = mk.isPlaying;
const remainingTimeExport = mk.currentPlaybackTimeRemaining;
const attributes = nowPlayingItem != null ? nowPlayingItem.attributes : {};
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : 'No Title Found';
attributes.artwork = attributes.artwork ? attributes.artwork : {url: ''};
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : '';
attributes.playParams = attributes.playParams ? attributes.playParams : {id: 'no-id-found'};
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : 'no-id-found';
attributes.albumName = attributes.albumName ? attributes.albumName : '';
attributes.artistName = attributes.artistName ? attributes.artistName : '';
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? (remainingTimeExport * 1000) : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.startTime = Date.now();
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
attributes.volume = mk.volume;
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes
},
moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition,0,MusicKit.getInstance().queue._queueItems.splice(oldPosition,1)[0])
MusicKit.getInstance().queue._reindex()
},
setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value
},
returnDynamic(data, type) {
ipcRenderer.send('wsapi-returnDynamic', JSON.stringify(data), type)
},
musickitApi(method, id, params, library = false) {
if (library) {
MusicKit.getInstance().api.library[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
} else {
MusicKit.getInstance().api[method](id, params).then((results)=>{
ipcRenderer.send('wsapi-returnMusicKitApi', JSON.stringify(results), method)
})
}
},
getPlaybackState () {
ipcRenderer.send('wsapi-updatePlaybackState', MusicKitInterop.getAttributes());
},
getLyrics() {
ipcRenderer.send('wsapi-returnLyrics',JSON.stringify(app.lyrics));
},
getQueue() {
ipcRenderer.send('wsapi-returnQueue', JSON.stringify(MusicKit.getInstance().queue))
},
playNext(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playNext(request)
},
playLater(type, id) {
var request = {}
request[type] = id
MusicKit.getInstance().playLater(request)
},
love() {
},
playTrackById(id, kind = "song") {
MusicKit.getInstance().setQueue({ [kind]: id , parameters : {l : app.mklang}}).then(function (queue) {
MusicKit.getInstance().play()
})
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance().api.search(term, { limit: 2, types: 'songs' }).then(function (data) {
MusicKit.getInstance().setQueue({ song: data["songs"][0]["id"],parameters : {l : app.mklang} }).then(function (queue) {
MusicKit.getInstance().play()
})
})
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0
},
togglePlayPause() {
app.mk.isPlaying ? app.mk.pause() : app.mk.play()
},
toggleRepeat() {
if (MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 1
} else if (MusicKit.getInstance().repeatMode == 1){
MusicKit.getInstance().repeatMode = 2
} else {
MusicKit.getInstance().repeatMode = 0
}
},
getmaxVolume() {
ipcRenderer.send('wsapi-returnvolumeMax',JSON.stringify(app.cfg.audio.maxVolume));
},
getLibraryStatus(kind, id) {
if (kind === undefined || id === "no-id-found") return;
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library",
fields: "inLibrary"
}).then(data => {
const res = data.data.data[0];
const inLibrary = res && res.attributes && res.attributes.inLibrary;
app.getRating({ type: truekind, id: id }).then(rating => {
ipcRenderer.send('wsapi-libraryStatus', inLibrary, rating);
})
})
},
rate(kind, id, rating) {
if (kind === undefined || id === "no-id-found") return;
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
if (rating === 0) {
app.mk.api.v3.music(`/v1/me/ratings/${truekind}/${id}`, {}, {
fetchOptions: {
method: "DELETE",
}
}).then(function () {
ipcRenderer.send('wsapi-rate', kind, id, rating);
})
} else {
app.mk.api.v3.music(`/v1/me/ratings/${truekind}/${id}`, {}, {
fetchOptions: {
method: "PUT",
body: JSON.stringify({
"type": "rating",
"attributes": {
"value": rating
}
})
}
}).then(function () {
ipcRenderer.send('wsapi-rate', kind, id, rating);
})
}
},
changeLibrary(kind, id, shouldAdd) {
if (shouldAdd) {
app.addToLibrary(id);
ipcRenderer.send('wsapi-change-library', kind, id, shouldAdd);
} else {
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library",
fields: "inLibrary"
})
.then(res => {
res = res.data.data[0]
if (res && res.relationships && res.relationships.library && res.relationships.library.data) {
const item = res.relationships.library.data[0];
if (item) {
app.removeFromLibrary(kind, item.id)
}
ipcRenderer.send('wsapi-change-library', kind, id, shouldAdd);
}
});
}
attributes.status = isPlayingExport ? isPlayingExport : false;
attributes.name = attributes.name ? attributes.name : "No Title Found";
attributes.artwork = attributes.artwork ? attributes.artwork : { url: "" };
attributes.artwork.url = attributes.artwork.url ? attributes.artwork.url : "";
attributes.playParams = attributes.playParams ? attributes.playParams : { id: "no-id-found" };
attributes.playParams.id = attributes.playParams.id ? attributes.playParams.id : "no-id-found";
attributes.albumName = attributes.albumName ? attributes.albumName : "";
attributes.artistName = attributes.artistName ? attributes.artistName : "";
attributes.genreNames = attributes.genreNames ? attributes.genreNames : [];
attributes.remainingTime = remainingTimeExport ? remainingTimeExport * 1000 : 0;
attributes.durationInMillis = attributes.durationInMillis ? attributes.durationInMillis : 0;
attributes.startTime = Date.now();
attributes.endTime = attributes.endTime ? attributes.endTime : Date.now();
attributes.volume = mk.volume;
attributes.shuffleMode = mk.shuffleMode;
attributes.repeatMode = mk.repeatMode;
attributes.autoplayEnabled = mk.autoplayEnabled;
return attributes;
},
moveQueueItem(oldPosition, newPosition) {
MusicKit.getInstance().queue._queueItems.splice(newPosition, 0, MusicKit.getInstance().queue._queueItems.splice(oldPosition, 1)[0]);
MusicKit.getInstance().queue._reindex();
},
setAutoplay(value) {
MusicKit.getInstance().autoplayEnabled = value;
},
returnDynamic(data, type) {
ipcRenderer.send("wsapi-returnDynamic", JSON.stringify(data), type);
},
musickitApi(method, id, params, library = false) {
if (library) {
MusicKit.getInstance()
.api.library[method](id, params)
.then((results) => {
ipcRenderer.send("wsapi-returnMusicKitApi", JSON.stringify(results), method);
});
} else {
MusicKit.getInstance()
.api[method](id, params)
.then((results) => {
ipcRenderer.send("wsapi-returnMusicKitApi", JSON.stringify(results), method);
});
}
}
},
getPlaybackState() {
ipcRenderer.send("wsapi-updatePlaybackState", MusicKitInterop.getAttributes());
},
getLyrics() {
ipcRenderer.send("wsapi-returnLyrics", JSON.stringify(app.lyrics));
},
getQueue() {
ipcRenderer.send("wsapi-returnQueue", JSON.stringify(MusicKit.getInstance().queue));
},
playNext(type, id) {
var request = {};
request[type] = id;
MusicKit.getInstance().playNext(request);
},
playLater(type, id) {
var request = {};
request[type] = id;
MusicKit.getInstance().playLater(request);
},
love() {},
playTrackById(id, kind = "song") {
MusicKit.getInstance()
.setQueue({ [kind]: id, parameters: { l: app.mklang } })
.then(function (queue) {
MusicKit.getInstance().play();
});
},
quickPlay(term) {
// Quick play by song name
MusicKit.getInstance()
.api.search(term, { limit: 2, types: "songs" })
.then(function (data) {
MusicKit.getInstance()
.setQueue({
song: data["songs"][0]["id"],
parameters: { l: app.mklang },
})
.then(function (queue) {
MusicKit.getInstance().play();
});
});
},
toggleShuffle() {
MusicKit.getInstance().shuffleMode = MusicKit.getInstance().shuffleMode === 0 ? 1 : 0;
},
togglePlayPause() {
app.mk.isPlaying ? app.mk.pause() : app.mk.play();
},
toggleRepeat() {
if (MusicKit.getInstance().repeatMode == 0) {
MusicKit.getInstance().repeatMode = 1;
} else if (MusicKit.getInstance().repeatMode == 1) {
MusicKit.getInstance().repeatMode = 2;
} else {
MusicKit.getInstance().repeatMode = 0;
}
},
getmaxVolume() {
ipcRenderer.send("wsapi-returnvolumeMax", JSON.stringify(app.cfg.audio.maxVolume));
},
getLibraryStatus(kind, id) {
if (kind === undefined || id === "no-id-found") return;
export {wsapi}
let truekind = !kind.endsWith("s") ? kind + "s" : kind;
app.mk.api.v3
.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library",
fields: "inLibrary",
})
.then((data) => {
const res = data.data.data[0];
const inLibrary = res && res.attributes && res.attributes.inLibrary;
app.getRating({ type: truekind, id: id }).then((rating) => {
ipcRenderer.send("wsapi-libraryStatus", inLibrary, rating);
});
});
},
rate(kind, id, rating) {
if (kind === undefined || id === "no-id-found") return;
let truekind = !kind.endsWith("s") ? kind + "s" : kind;
if (rating === 0) {
app.mk.api.v3
.music(
`/v1/me/ratings/${truekind}/${id}`,
{},
{
fetchOptions: {
method: "DELETE",
},
}
)
.then(function () {
ipcRenderer.send("wsapi-rate", kind, id, rating);
});
} else {
app.mk.api.v3
.music(
`/v1/me/ratings/${truekind}/${id}`,
{},
{
fetchOptions: {
method: "PUT",
body: JSON.stringify({
type: "rating",
attributes: {
value: rating,
},
}),
},
}
)
.then(function () {
ipcRenderer.send("wsapi-rate", kind, id, rating);
});
}
},
changeLibrary(kind, id, shouldAdd) {
if (shouldAdd) {
app.addToLibrary(id);
ipcRenderer.send("wsapi-change-library", kind, id, shouldAdd);
} else {
let truekind = !kind.endsWith("s") ? kind + "s" : kind;
app.mk.api.v3
.music(`/v1/catalog/${app.mk.storefrontId}/?ids[${truekind}]=${id}`, {
relate: "library",
fields: "inLibrary",
})
.then((res) => {
res = res.data.data[0];
if (res && res.relationships && res.relationships.library && res.relationships.library.data) {
const item = res.relationships.library.data[0];
if (item) {
app.removeFromLibrary(kind, item.id);
}
ipcRenderer.send("wsapi-change-library", kind, id, shouldAdd);
}
});
}
},
};
export { wsapi };

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,68 @@
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")}));
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

View file

@ -1,3 +1,3 @@
#app {
--color1: #111;
}
}

View file

@ -1 +1 @@
// Default theme
// Default theme

View file

@ -1,3 +1,3 @@
body.notransparency::before {
display: block;
}
display: block;
}

View file

@ -1,41 +1,40 @@
&:not(.modular-fs) {
.app-drawer {
border-radius: 0px;
top: 0;
right: 0;
height: 100%;
box-shadow: unset;
border-left: 1px solid var(--color2);
background: var(--color1);
margin-right: 0px;
position: relative;
}
.drawertransition-enter-active,
.drawertransition-leave-active {
transition: margin 0.25s var(--appleEase), opacity 0.25s var(--appleEase);
}
.drawertransition-enter,
.drawertransition-leave-to {
margin-right: -300px;
}
@media screen and (max-width: 1120px) {
.app-drawer {
border-radius: 0px;
top : 0;
right : 0;
height : 100%;
box-shadow : unset;
border-left : 1px solid var(--color2);
background : var(--color1);
margin-right : 0px;
position : relative;
margin-right: 0px;
position: absolute;
}
.drawertransition-enter-active,
.drawertransition-leave-active {
transition: margin .25s var(--appleEase), opacity .25s var(--appleEase);
transition: right 0.25s var(--appleEase), opacity 0.25s var(--appleEase);
}
.drawertransition-enter,
.drawertransition-leave-to {
margin-right: -300px;
right: -300px;
}
@media screen and (max-width: 1120px) {
.app-drawer {
margin-right: 0px;
position : absolute;
}
.drawertransition-enter-active,
.drawertransition-leave-active {
transition: right .25s var(--appleEase), opacity .25s var(--appleEase);
}
.drawertransition-enter,
.drawertransition-leave-to {
right: -300px;
}
}
}
}
}

View file

@ -1,141 +1,139 @@
body {
--ciderShadow-Generic : var(--mediaItemShadow);
--mediaItemShadow-Shadow : var(--mediaItemShadow);
--mediaItemShadow-ShadowSubtle: var(--mediaItemShadow);
--ciderShadow-Generic: var(--mediaItemShadow);
--mediaItemShadow-Shadow: var(--mediaItemShadow);
--mediaItemShadow-ShadowSubtle: var(--mediaItemShadow);
}
.bg-artwork-container {
display : none;
animation: none !important;
display: none;
animation: none !important;
.bg-artwork {
animation: none !important;
}
.bg-artwork {
animation: none !important;
}
}
.app-chrome:not(.chrome-bottom) {
backdrop-filter: unset;
background-color: var(--baseColor);
backdrop-filter: unset;
background-color: var(--baseColor);
}
.menu-panel .menu-panel-body {
background: rgb(30 30 30);
background: rgb(30 30 30);
}
.menu-panel .menu-panel-body .menu-option::before {
transition: unset!important;
transition: unset !important;
}
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center .top-nav-group .app-sidebar-item:before {
transition: unset!important;
transition: unset !important;
}
.playback-button:before, .playback-button--small:before {
transition: unset!important;
.playback-button:before,
.playback-button--small:before {
transition: unset !important;
}
.floating-header {
backdrop-filter: unset!important;
background: rgb(0 0 0 / 80%)!important;
backdrop-filter: unset !important;
background: rgb(0 0 0 / 80%) !important;
}
.replaycard-enter-active,
.replaycard-leave-active {
transition: unset;
transition: unset;
}
.replaycard-enter,
.replaycard-leave-to {
opacity : 0;
transform: translateY(20px);
opacity: 0;
transform: translateY(20px);
}
.modal-enter-active,
.modal-leave-active {
transition: unset;
transition: unset;
}
.modal-enter,
.modal-leave-to {
opacity : 0;
transform: scale(1.10);
opacity: 0;
transform: scale(1.1);
}
.wpfade-enter-active,
.wpfade-leave-active {
transition: opacity .1s var(--appleEase);
transition: opacity 0.1s var(--appleEase);
}
.wpfade-enter,
.wpfade-leave-to {
opacity: 0;
opacity: 0;
}
.wpfade_transform-enter-active,
.wpfade_transform-leave-active {
transition : unset;
will-change: unset;
transition: unset;
will-change: unset;
}
.wpfade_transform-enter {
opacity : 0;
transform : unset;
will-change: unset;
opacity: 0;
transform: unset;
will-change: unset;
}
.wpfade_transform-leave-to {
opacity : 0;
transform : unset;
will-change: unset;
opacity: 0;
transform: unset;
will-change: unset;
}
.wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active {
transition: unset;
transition: unset;
}
.wpfade_transform_backwards-enter {
opacity : 0;
transform : unset;
will-change: unset;
opacity: 0;
transform: unset;
will-change: unset;
}
.wpfade_transform_backwards-leave-to {
opacity : 0;
transform : unset;
will-change: unset;
opacity: 0;
transform: unset;
will-change: unset;
}
.fabfade-enter-active,
.fabfade-leave-active {
transition: unset;
transition: unset;
}
.fabfade-enter,
.fabfade-leave-to {
opacity : 0;
transform: scale(0.5);
opacity: 0;
transform: scale(0.5);
}
.fsModeSwitch-enter-active,
.fsModeSwitch-leave-active {
transition: unset;
transition: unset;
}
.fsModeSwitch-enter,
.fsModeSwitch-leave-to {
transform: scale(1.10);
opacity : 0;
transform: scale(1.1);
opacity: 0;
}
.drawertransition-enter-active,
.drawertransition-leave-active {
transition: unset;
transition: unset;
}
.drawertransition-enter,
.drawertransition-leave-to {
right: -300px;
}
right: -300px;
}

View file

@ -1,49 +1,47 @@
@panelColorsFallback: rgb(30 30 30);
@panelColors : rgb(30 30 30 / 45%);
@panelColors: rgb(30 30 30 / 45%);
.menu-panel {
.menu-panel-body {
background-color: @panelColors;
backdrop-filter : blur(32px) saturate(180%);
.menu-panel-body {
background-color: @panelColors;
backdrop-filter: blur(32px) saturate(180%);
&.menu-panel-body-down {
animation: menuInDown .10s var(--appleEase);
}
&.menu-panel-body-up {
animation: menuInUp .10s var(--appleEase);
}
&.menu-panel-body-down {
animation: menuInDown 0.1s var(--appleEase);
}
@keyframes menuInUp {
0% {
opacity : 0;
transform : translateY(-10px) translate3d(0,0,0);
background: @panelColorsFallback;
}
&.menu-panel-body-up {
animation: menuInUp 0.1s var(--appleEase);
}
}
100% {
opacity : 1;
transform : translateY(0);
background: @panelColors;
}
@keyframes menuInUp {
0% {
opacity: 0;
transform: translateY(-10px) translate3d(0, 0, 0);
background: @panelColorsFallback;
}
@keyframes menuInDown {
0% {
opacity : 0;
transform : translateY(10px) translate3d(0,0,0);
background: @panelColorsFallback;
}
100% {
opacity: 1;
transform: translateY(0);
background: @panelColors;
}
}
100% {
opacity : 1;
transform : translateY(0);
background: @panelColors;
}
@keyframes menuInDown {
0% {
opacity: 0;
transform: translateY(10px) translate3d(0, 0, 0);
background: @panelColorsFallback;
}
100% {
opacity: 1;
transform: translateY(0);
background: @panelColors;
}
}
}
.cd-mediaitem-square {
@ -55,47 +53,46 @@
}
.cd-mediaitem-square:not(.mediaitem-card) {
transition : transform .2s var(--appleEase);
transition-delay: .1s;
transition: transform 0.2s var(--appleEase);
transition-delay: 0.1s;
.artwork-container {
}
.artwork-container {}
.info-rect {
}
.info-rect {}
.artwork-container,
.info-rect {
transition: transform 0.22s var(--appleEase);
transition-delay: 0.05s;
}
.artwork-container,
.info-rect {
transition : transform .22s var(--appleEase);
transition-delay: .05s;
}
.artwork-container {
transform: scale(0.962) translateZ(0);
transition: transform 0.1s var(--appleEase);
transition-delay: 0s;
transform-origin: center;
}
&:hover {
.artwork-container {
transform : scale(0.962) translateZ(0);
transition : transform .1s var(--appleEase);
transform: scale(1);
transition: transform 0.1s var(--appleEase);
transition-delay: 0s;
transform-origin: center;
}
&:hover {
.artwork-container {
transform : scale(1.0);
transition : transform .1s var(--appleEase);
transition-delay: 0s;
transform-origin: center;
}
.info-rect {
z-index : 1;
transition : transform .1s var(--appleEase);
transition-delay: 0s;
transform : translateY(8px) translate3d(0,0,0);
}
.info-rect {
z-index: 1;
transition: transform 0.1s var(--appleEase);
transition-delay: 0s;
transform: translateY(8px) translate3d(0, 0, 0);
}
}
&:active {
}
&:active {
}
}
.wpfade_transform-enter-active,
@ -107,16 +104,15 @@
.wpfade_transform-enter {
opacity: 0;
transform: translateX(50%) translate3d(0,0,0);
transform: translateX(50%) translate3d(0, 0, 0);
will-change: opacity, transform;
}
.wpfade_transform-leave-to {
opacity: 0;
transform: translateX(-50%) translate3d(0,0,0);
transform: translateX(-50%) translate3d(0, 0, 0);
will-change: opacity, transform;
}
.wpfade_transform_backwards-enter-active,
.wpfade_transform_backwards-leave-active {
--transitionTime: 0.2s;
@ -125,11 +121,11 @@
.wpfade_transform_backwards-enter {
opacity: 0;
transform: translateX(-50%) translate3d(0,0,0);
transform: translateX(-50%) translate3d(0, 0, 0);
will-change: opacity, transform;
}
.wpfade_transform_backwards-leave-to {
opacity: 0;
transform: translateX(50%) translate3d(0,0,0);
transform: translateX(50%) translate3d(0, 0, 0);
will-change: opacity, transform;
}
}

View file

@ -1,180 +1,193 @@
// 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
// &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
});
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"
});
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"});
await app.mk.api.recentRadioStations("", {
l: "en-gb",
platform: "web",
"art[url]": "f",
});
// Recently Added
await app.mk.api.library.recentlyAdded({
"platform": "web",
await app.mk.api.library.recentlyAdded(
{
platform: "web",
include: {
"library-albums": ["artists"],
"library-artists": ["catalog"]
"library-albums": ["artists"],
"library-artists": ["catalog"],
},
fields: {
artists: ["url"],
albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url"
artists: ["url"],
albums: "artistName,artistUrl,artwork,contentRating,editorialArtwork,name,playParams,releaseDate,url",
},
includeOnly: ["catalog", "artists"],
limit: 25
}, {
limit: 25,
},
{
reload: !0,
includePagination: !0
})
includePagination: !0,
}
);
// Songs
await app.mk.api.library.songs({limit: 100}).then((data)=>{
console.log(data)
})
// 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.artists({ limit: 100 }).then((data) => {
console.log(data);
});
// Artists
await app.mk.api.library.albums({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)
})
await app.mk.api.library.albums({ limit: 50 }).then((data) => {
console.log(data);
});
// Made For You
app.mk.api.recommendations("",{extend: "editorialArtwork,artistUrl"})
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)
})
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})
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)
})
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 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 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)
}
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:
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:
// Sorting Playlists, send an array of tracks in the format below
// playlist must be fully recursively downloaded first before sorting
app.mk.api.library.putPlaylistTracklisting(app.showingPlaylist.attributes.playParams.id, [
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
},
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
},
{
"id": relationships.tracks.data[X].id,
"type": relationships.tracks.data[X].type
},
])
{
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type,
},
{
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type,
},
{
id: relationships.tracks.data[X].id,
type: relationships.tracks.data[X].type,
},
]);

View file

@ -1,136 +1,136 @@
<div class="app-navigation" v-cloak>
<transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened">
<div class="usermenu-body">
<button
class="app-sidebar-button"
style="width: 100%"
@click="appRoute('apple-account-settings')"
>
<img
class="sidebar-user-icon"
loading="lazy"
:src="getMediaItemArtwork(chrome.hideUserInfo ? './assets/logocut.png' : (chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : ''), 26)"
/>
<div class="usermenu-container" v-if="chrome.menuOpened">
<div class="usermenu-body">
<button
class="app-sidebar-button"
style="width: 100%"
@click="appRoute('apple-account-settings')"
>
<img
class="sidebar-user-icon"
loading="lazy"
:src="getMediaItemArtwork(chrome.hideUserInfo ? './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 || mk.isAuthorized">
<div class="fullname text-overflow-elipsis">
{{
chrome.userinfo != null &&
chrome.userinfo.attributes != null
? chrome.userinfo.attributes.name ?? ""
: ""
}}
</div>
<div class="handle-text text-overflow-elipsis">
{{
chrome.userinfo != null &&
chrome.userinfo.attributes != null
? chrome.userinfo.attributes.handle ?? ""
: ""
}}
</div>
</template>
<template v-else>
<div @click="mk.authorize()">
{{ $root.getLz("term.login") }}
</div>
</template>
</div>
<div class="sidebar-user-text" v-else>
{{ $root.getLz("app.name") }}
</div>
</button>
<!-- Use 20px SVG for usermenu icon -->
<button
class="usermenu-item"
v-if="cfg.general.privateEnabled"
@click="cfg.general.privateEnabled = false"
>
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.id || mk.isAuthorized">
<div class="fullname text-overflow-elipsis">
{{
chrome.userinfo != null &&
chrome.userinfo.attributes != null
? chrome.userinfo.attributes.name ?? ""
: ""
}}
</div>
<div class="handle-text text-overflow-elipsis">
{{
chrome.userinfo != null &&
chrome.userinfo.attributes != null
? chrome.userinfo.attributes.handle ?? ""
: ""
}}
</div>
</template>
<template v-else>
<div @click="mk.authorize()">
{{ $root.getLz("term.login") }}
</div>
</template>
</div>
<div class="sidebar-user-text" v-else>
{{ $root.getLz("app.name") }}
</div>
</button>
<!-- Use 20px SVG for usermenu icon -->
<button
class="usermenu-item"
v-if="cfg.general.privateEnabled"
@click="cfg.general.privateEnabled = false"
>
<span class="usermenu-item-icon">
<%- include("../svg/x.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.disablePrivateSession")
}}</span>
</button>
<button class="usermenu-item" @click="appRoute('remote-pair')">
</button>
<button class="usermenu-item" @click="appRoute('remote-pair')">
<span class="usermenu-item-icon">
<%- include("../svg/smartphone.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("action.showWebRemoteQR")
}}</span>
</button>
<button
class="usermenu-item"
@click="modals.castMenu = true"
>
</button>
<button
class="usermenu-item"
@click="modals.castMenu = true"
>
<span class="usermenu-item-icon">
<%- include("../svg/cast.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.cast")
}}</span>
</button>
<button
class="usermenu-item"
@click="modals.audioSettings = true"
>
</button>
<button
class="usermenu-item"
@click="modals.audioSettings = true"
>
<span class="usermenu-item-icon">
<%- include("../svg/headphones.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.audioSettings")
}}</span>
</button>
<button
class="usermenu-item"
v-if="pluginInstalled"
@click="modals.pluginMenu = true"
>
</button>
<button
class="usermenu-item"
v-if="pluginInstalled"
@click="modals.pluginMenu = true"
>
<span class="usermenu-item-icon">
<%- include("../svg/grid.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.plugin")
}}</span>
</button>
<button class="usermenu-item" @click="appRoute('about')">
</button>
<button class="usermenu-item" @click="appRoute('about')">
<span class="usermenu-item-icon">
<%- include("../svg/info.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.about")
}}</span>
</button>
<button class="usermenu-item" @click="modals.settings = true">
</button>
<button class="usermenu-item" @click="modals.settings = true">
<span class="usermenu-item-icon">
<%- include("../svg/settings.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.settings")
}}</span>
</button>
<button class="usermenu-item" @click="unauthorize()">
</button>
<button class="usermenu-item" @click="unauthorize()">
<span class="usermenu-item-icon" style="right: 2.5px">
<%- include("../svg/log-out.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.logout")
}}</span>
</button>
<button class="usermenu-item" @click="quit()">
</button>
<button class="usermenu-item" @click="quit()">
<span class="usermenu-item-icon" style="right: 2.5px">
<%- include("../svg/x.svg") %>
</span>
<span class="usermenu-item-name">{{
<span class="usermenu-item-name">{{
$root.getLz("term.quit")
}}</span>
</button>
</button>
</div>
</div>
</div>
</transition>
<transition name="sidebartransition">
<cider-app-sidebar v-if="!chrome.sidebarCollapsed"></cider-app-sidebar>
@ -141,9 +141,12 @@
v-if="drawer.open && drawer.panel == 'lyrics' && lyrics && lyrics != [] && lyrics.length > 0">
<div class="bgArtworkMaterial">
<div class="bg-artwork-container">
<img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork a" :src="$store.state.artwork.playerLCD">
<img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork b" :src="$store.state.artwork.playerLCD">
<img v-if="!(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork no-animation" :src="$store.state.artwork.playerLCD">
<img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork a"
:src="$store.state.artwork.playerLCD">
<img v-if="(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork b"
:src="$store.state.artwork.playerLCD">
<img v-if="!(cfg.visual.bg_artwork_rotation && animateBackground)" class="bg-artwork no-animation"
:src="$store.state.artwork.playerLCD">
</div>
</div>
<lyrics-view v-if="drawer.panel == 'lyrics'" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics"

View file

@ -1,4 +1,5 @@
<div class="app-chrome chrome-bottom" v-if="getThemeDirective('windowLayout') == 'twopanel'" :style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<div class="app-chrome chrome-bottom" v-if="getThemeDirective('windowLayout') == 'twopanel'"
:style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<div class="app-chrome--left">
<div class="app-chrome-item playback-controls">
<template v-if="mkReady()">
@ -16,15 +17,23 @@
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="song-name">{{ mk.nowPlayingItem["attributes"]["name"] }}</div>
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">{{ mk.nowPlayingItem["attributes"]["artistName"] }}</div>
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">{{
mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-album" @click="getNowPlayingItemDetailed(`album`)">
{{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
<hr>
<div class="btn-group" style="width:100%;">
<button class="md-btn md-btn-small" style="width: 100%;" @click="drawer.open = false; miniPlayer(true)">{{ $root.getLz("term.miniplayer") }}</button>
<button class="md-btn md-btn-small" style="width: 100%;" @click="drawer.open = false; fullscreen(true)">{{ $root.getLz("term.fullscreenView") }}</button>
<button class="md-btn md-btn-small" style="width: 100%;"
@click="drawer.open = false; miniPlayer(true)">{{ $root.getLz("term.miniplayer")
}}
</button>
<button class="md-btn md-btn-small" style="width: 100%;"
@click="drawer.open = false; fullscreen(true)">{{
$root.getLz("term.fullscreenView") }}
</button>
</div>
</div>
</b-popover>
@ -39,20 +48,26 @@
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-album" @click="getNowPlayingItemDetailed('album')" v-if='mk.nowPlayingItem["attributes"]["albumName"]'>
<div class="song-album" @click="getNowPlayingItemDetailed('album')"
v-if='mk.nowPlayingItem["attributes"]["albumName"]'>
{{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
<div class="chrome-icon-container">
<div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div>
<div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'" v-b-tooltip.hover
<div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'"
v-b-tooltip.hover
></div>
<div class="audio-type lossless-icon" v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container" v-b-tooltip.hover
></div>
<div class="audio-type ppe-icon" v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover
<div class="audio-type lossless-icon"
v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container"
v-b-tooltip.hover
></div>
<div class="audio-type ppe-icon"
v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')"
v-b-tooltip.hover
></div>
</div>
</div>
@ -75,7 +90,7 @@
</div>
<div class="playback-info">
<div class="song-name">
</div>
</div>
@ -86,8 +101,9 @@
</div>
<div class="app-chrome--center">
<div class="app-chrome-playback-duration-bottom">
<b-row v-if="mkReady()">
<b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{ convertTime(getSongProgress()) }}</b-col>
<b-row v-if="mkReady()">
<b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{ convertTime(getSongProgress()) }}
</b-col>
<b-col sm="auto" v-else>--:--</b-col>
<b-col>
<input type="range" step="0.01" min="0" :style="progressBarStyle()"
@ -96,13 +112,16 @@
@touchend="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
:max="mk.currentPlaybackDuration" :value="getSongProgress()">
</b-col>
<b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{ convertTime(mk.currentPlaybackDuration) }}</b-col>
<b-col sm="auto" v-if="!mk.nowPlayingItem?.isLiveRadioStation">{{
convertTime(mk.currentPlaybackDuration) }}
</b-col>
<b-col sm="auto" v-else>{{ getLz("term.live") }}</b-col>
</b-row>
</div>
<div class="app-chrome-playback-controls">
<div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" :class="isDisabled() && 'disabled'"
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
:class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'"
@ -114,7 +133,8 @@
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item">
<button class="playback-button stop" @click="mk.stop()" v-if="mk.isPlaying && mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
<button class="playback-button stop" @click="mk.stop()"
v-if="mk.isPlaying && mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="mk.pause()" v-else-if="mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
@ -126,13 +146,16 @@
:title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" :class="isDisabled() && 'disabled'"
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
:class="isDisabled() && 'disabled'"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2" :class="isDisabled() && 'disabled'"
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="isDisabled() && 'disabled'"
v-else-if="mk.repeatMode == 1" :title="$root.getLz('term.disableRepeatOne')"
v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0" :class="isDisabled() && 'disabled'"
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0"
:class="isDisabled() && 'disabled'"
v-else-if="mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button>
</div>
@ -149,27 +172,27 @@
v-b-tooltip.hover :title="formatVolumeTooltip()">
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small cast"
<button class="playback-button--small cast"
:title="$root.getLz('term.cast')"
v-b-tooltip.hover
@click="modals.castMenu = true"></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue" :class="{'active': drawer.panel == 'queue'}"
<button class="playback-button--small queue" :class="{'active': drawer.panel == 'queue'}"
:title="$root.getLz('term.queue')"
v-b-tooltip.hover
@click="invokeDrawer('queue')"></button>
</div>
<div class="app-chrome-item generic">
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<button class="playback-button--small lyrics"
<button class="playback-button--small lyrics"
:title="$root.getLz('term.lyrics')"
v-b-tooltip.hover
:class="{'active': drawer.panel == 'lyrics'}"
@click="invokeDrawer('lyrics')"></button>
</template>
<template v-else>
<button class="playback-button--small lyrics"
<button class="playback-button--small lyrics"
:style="{'opacity': 0.3, 'pointer-events': 'none'}"></button>
</template>

View file

@ -1,286 +1,313 @@
<div class="app-chrome" :style="{'display': chrome.topChromeVisible ? '' : 'none'}">
<div class="app-chrome--left">
<div class="app-chrome-item full-height" v-if="chrome.windowControlPosition == 'left' && !chrome.nativeControls">
<div class="window-controls-macos">
<div class="close" @click="ipcRenderer.send('close')"></div>
<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>
</div>
<div class="app-chrome-item full-height" v-else>
<button class="app-mainmenu" @blur="mainMenuVisibility(false)" @click="mainMenuVisibility(true)"
@contextmenu="mainMenuVisibility(true)" :class="{active: chrome.menuOpened}"
:aria-label="$root.getLz('term.quickNav')"></button>
</div>
<template v-if="getThemeDirective('appNavigation') != 'seperate'">
<div class="vdiv" v-if="getThemeDirective('windowLayout') == 'twopanel'"></div>
<div class="app-chrome-item">
<button class="playback-button navigation" @click="navigateBack()" :title="$root.getLz('term.navigateBack')"
v-b-tooltip.hover>
<svg-icon url="./views/svg/chevron-left.svg"></svg-icon>
</button>
</div>
<div class="app-chrome-item">
<button class="playback-button navigation" @click="navigateForward()"
:title="$root.getLz('term.navigateForward')" v-b-tooltip.hover>
<svg-icon url="./views/svg/chevron-right.svg"></svg-icon>
</button>
</div>
<div class="app-chrome-item" v-if="getThemeDirective('windowLayout') == 'twopanel'">
<button class="playback-button collapseLibrary" v-b-tooltip.hover
:title="chrome.sidebarCollapsed ? getLz('action.showLibrary') : getLz('action.hideLibrary')"
@click="chrome.sidebarCollapsed = !chrome.sidebarCollapsed">
<transition name="fade">
<span v-if="chrome.sidebarCollapsed"></span>
</transition>
<transition name="fade">
<span v-if="!chrome.sidebarCollapsed"></span>
</transition>
<div class="app-chrome--left">
<div class="app-chrome-item full-height"
v-if="chrome.windowControlPosition == 'left' && !chrome.nativeControls">
<div class="window-controls-macos">
<div class="close" @click="ipcRenderer.send('close')"></div>
<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>
</div>
<div class="app-chrome-item full-height" v-else>
<button class="app-mainmenu" @blur="mainMenuVisibility(false)" @click="mainMenuVisibility(true)"
@contextmenu="mainMenuVisibility(true)" :class="{active: chrome.menuOpened}"
:aria-label="$root.getLz('term.quickNav')"></button>
</div>
<template v-if="getThemeDirective('appNavigation') != 'seperate'">
<div class="vdiv" v-if="getThemeDirective('windowLayout') == 'twopanel'"></div>
<div class="app-chrome-item">
<button class="playback-button navigation" @click="navigateBack()"
:title="$root.getLz('term.navigateBack')"
v-b-tooltip.hover>
<svg-icon url="./views/svg/chevron-left.svg"></svg-icon>
</button>
</div>
<div class="app-chrome-item">
<button class="playback-button navigation" @click="navigateForward()"
:title="$root.getLz('term.navigateForward')" v-b-tooltip.hover>
<svg-icon url="./views/svg/chevron-right.svg"></svg-icon>
</button>
</div>
<div class="app-chrome-item" v-if="getThemeDirective('windowLayout') == 'twopanel'">
<button class="playback-button collapseLibrary" v-b-tooltip.hover
:title="chrome.sidebarCollapsed ? getLz('action.showLibrary') : getLz('action.hideLibrary')"
@click="chrome.sidebarCollapsed = !chrome.sidebarCollapsed">
<transition name="fade">
<span v-if="chrome.sidebarCollapsed"></span>
</transition>
<transition name="fade">
<span v-if="!chrome.sidebarCollapsed"></span>
</transition>
</button>
</div>
<div class="vdiv display--large" v-if="getThemeDirective('windowLayout') != 'twopanel'"></div>
</template>
<template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-chrome-item playback-control-buttons">
<div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" :class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button previous" @click="prevButton()" :class="isPrevDisabled() && 'disabled'"
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button stop" @click="mk.stop()"
v-if="mk.isPlaying && mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="mk.pause()" v-else-if="mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
<button class="playback-button play" @click="mk.play()" v-else :title="$root.getLz('term.play')"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button next" @click="skipToNextItem()" :class="isNextDisabled() && 'disabled'"
:title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" :class="isDisabled() && 'disabled'"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button>
</div>
</div>
</template>
</div>
<div class="app-chrome--center">
<div class="app-chrome-item playback-controls" v-if="getThemeDirective('windowLayout') != 'twopanel'">
<template v-if="mkReady()">
<div class="app-playback-controls" @mouseover="chrome.progresshover = true"
@mouseleave="chrome.progresshover = false" @contextmenu="nowPlayingContextMenu">
<div class="artwork" id="artworkLCD">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<b-popover custom-class="mediainfo-popover" target="artworkLCD" triggers="hover" placement="bottom">
<div class="content">
<div class="shadow-artwork">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="popover-artwork">
<mediaitem-artwork :size="210" :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="song-name">
{{ mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-album" @click="getNowPlayingItemDetailed(`album`)">
{{
mk.nowPlayingItem["attributes"]["albumName"]
? mk.nowPlayingItem["attributes"]["albumName"]
: ""
}}
</div>
<hr />
<div class="btn-group" style="width: 100%">
<button class="md-btn md-btn-small" style="width: 100%" @click="drawer.open = false; miniPlayer(true)">
{{ $root.getLz("term.miniplayer") }}
</button>
<button class="md-btn md-btn-small" style="width: 100%" @click="drawer.open = false; fullscreen(true)">
{{ $root.getLz("term.fullscreenView") }}
</button>
</div>
</div>
</b-popover>
<div class="playback-info">
<div class="chrome-icon-container">
<div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div>
<div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'" v-b-tooltip.hover
></div>
<div class="audio-type lossless-icon" v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container" v-b-tooltip.hover
></div>
<div class="audio-type ppe-icon" v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover
></div>
<div class="vdiv display--large" v-if="getThemeDirective('windowLayout') != 'twopanel'"></div>
</template>
<template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-chrome-item playback-control-buttons">
<div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
:class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else :class="isDisabled() && 'disabled'"
@click="mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button previous" @click="prevButton()"
:class="isPrevDisabled() && 'disabled'"
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button stop" @click="mk.stop()"
v-if="mk.isPlaying && mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="mk.pause()" v-else-if="mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
<button class="playback-button play" @click="mk.play()" v-else :title="$root.getLz('term.play')"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button next" @click="skipToNextItem()"
:class="isNextDisabled() && 'disabled'"
:title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
:class="isDisabled() && 'disabled'"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0"
:class="isDisabled() && 'disabled'" v-else-if="mk.repeatMode == 2"
:title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button>
</div>
</div>
<div class="info-rect">
<div class="song-name"
:class="[isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') ? 'marquee' : '']">
{{ mk.nowPlayingItem["attributes"]["name"] }}
<div class="explicit-icon" v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'"
style="display: inline-block"></div>
</div>
<div class="song-artist-album">
<div class="song-artist-album-content"
:class="[isElementOverflowing('#app-main > .app-chrome .app-chrome-item > .app-playback-controls > div >.song-artist-album > .song-artist-album-content') ? 'marquee' : '']"
style="
</template>
</div>
<div class="app-chrome--center">
<div class="app-chrome-item playback-controls" v-if="getThemeDirective('windowLayout') != 'twopanel'">
<template v-if="mkReady()">
<div class="app-playback-controls" @mouseover="chrome.progresshover = true"
@mouseleave="chrome.progresshover = false" @contextmenu="nowPlayingContextMenu">
<div class="artwork" id="artworkLCD">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<b-popover custom-class="mediainfo-popover" target="artworkLCD" triggers="hover" placement="bottom">
<div class="content">
<div class="shadow-artwork">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="popover-artwork">
<mediaitem-artwork :size="210" :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="song-name">
{{ mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist" @click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-album" @click="getNowPlayingItemDetailed(`album`)">
{{
mk.nowPlayingItem["attributes"]["albumName"]
? mk.nowPlayingItem["attributes"]["albumName"]
: ""
}}
</div>
<hr />
<div class="btn-group" style="width: 100%">
<button class="md-btn md-btn-small" style="width: 100%"
@click="drawer.open = false; miniPlayer(true)">
{{ $root.getLz("term.miniplayer") }}
</button>
<button class="md-btn md-btn-small" style="width: 100%"
@click="drawer.open = false; fullscreen(true)">
{{ $root.getLz("term.fullscreenView") }}
</button>
</div>
</div>
</b-popover>
<div class="playback-info">
<div class="chrome-icon-container">
<div class="audio-type private-icon" v-if="cfg.general.privateEnabled === true"></div>
<div class="audio-type spatial-icon" v-if="cfg.audio.maikiwiAudio.spatial === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization') + ' (' + getProfileLz('CTS', cfg.audio.maikiwiAudio.spatialProfile) + ')'"
v-b-tooltip.hover
></div>
<div class="audio-type lossless-icon"
v-if="(mk.nowPlayingItem?.localFilesMetadata?.lossless ?? false) === true"
:title="mk.nowPlayingItem?.localFilesMetadata?.bitDepth +'-bit / '+ mk.nowPlayingItem?.localFilesMetadata?.sampleRate/1000 + ' kHz ' + mk.nowPlayingItem.localFilesMetadata.container"
v-b-tooltip.hover
></div>
<div class="audio-type ppe-icon"
v-if="mk.nowPlayingItem.localFilesMetadata == null && cfg.audio.maikiwiAudio.ciderPPE === true"
:title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')"
v-b-tooltip.hover
></div>
</div>
<div class="info-rect">
<div class="song-name"
:class="[isElementOverflowing('#app-main > div.app-chrome > div.app-chrome--center > div > div > div.playback-info > div.song-name') ? 'marquee' : '']">
{{ mk.nowPlayingItem["attributes"]["name"] }}
<div class="explicit-icon"
v-if="mk.nowPlayingItem['attributes']['contentRating'] == 'explicit'"
style="display: inline-block"></div>
</div>
<div class="song-artist-album">
<div class="song-artist-album-content"
:class="[isElementOverflowing('#app-main > .app-chrome .app-chrome-item > .app-playback-controls > div >.song-artist-album > .song-artist-album-content') ? 'marquee' : '']"
style="
display: inline-block;
-webkit-box-orient: horizontal;
white-space: nowrap;
">
<div class="item-navigate song-artist" style="display: inline-block"
@click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block"
@click="getNowPlayingItemDetailed('album')"
v-if="mk.nowPlayingItem['attributes']['albumName'] != ''">
<div class="separator" style="display: inline-block">
{{ "—" }}
<div class="item-navigate song-artist" style="display: inline-block"
@click="getNowPlayingItemDetailed(`artist`)">
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block"
@click="getNowPlayingItemDetailed('album')"
v-if="mk.nowPlayingItem['attributes']['albumName'] != ''">
<div class="separator" style="display: inline-block">
{{ "—" }}
</div>
{{
mk.nowPlayingItem["attributes"]["albumName"]
? mk.nowPlayingItem["attributes"]["albumName"]
: ""
}}
</div>
</div>
</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">{{ convertTime(getSongProgress()) }}</p>
<p style="width: auto">
{{ convertTime(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);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
@touchend="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
:max="mk.currentPlaybackDuration" :value="getSongProgress()" />
</div>
</div>
{{
mk.nowPlayingItem["attributes"]["albumName"]
? mk.nowPlayingItem["attributes"]["albumName"]
: ""
}}
</div>
<template v-if="mk.nowPlayingItem['attributes']['playParams']">
<div class="actions">
<button class="lcdMenu" @click="nowPlayingContextMenu" :title="$root.getLz('term.more')"
v-b-tooltip.hover>
<div class="svg-icon"></div>
</button>
</div>
</template>
</div>
</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">{{ convertTime(getSongProgress()) }}</p>
<p style="width: auto">
{{ convertTime(mk.currentPlaybackDuration) }}
</p>
</div>
</template>
<template v-else>
<div class="app-playback-controls">
<div class="artwork" id="artworkLCD" style="pointer-events: none;">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="playback-info">
<div class="info-rect">
<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);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
@touchend="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
:max="mk.currentPlaybackDuration" :value="getSongProgress()" />
</div>
</div>
<template v-if="mk.nowPlayingItem['attributes']['playParams']">
<div class="actions">
<button class="lcdMenu" @click="nowPlayingContextMenu" :title="$root.getLz('term.more')"
v-b-tooltip.hover>
<div class="svg-icon"></div>
</button>
</div>
</template>
</div>
</div>
</div>
</template>
</div>
</template>
<template v-else>
<div class="app-playback-controls">
<div class="artwork" id="artworkLCD" style="pointer-events: none;">
<mediaitem-artwork :url="currentArtUrl"></mediaitem-artwork>
</div>
<div class="playback-info">
<div class="info-rect">
<div class="app-chrome-item" v-else>
<div class="top-nav-group">
<sidebar-library-item :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg"
svg-icon-name="home" page="home">
</sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg"
svg-icon-name="listenNow"
page="listen_now"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg"
svg-icon-name="browse" page="browse">
</sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg"
svg-icon-name="radio" page="radio">
</sidebar-library-item>
</div>
</div>
</div>
</template>
</div>
<div class="app-chrome-item" v-else>
<div class="top-nav-group">
<sidebar-library-item :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg" svg-icon-name="home" page="home">
</sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg" svg-icon-name="listenNow"
page="listen_now"></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg" svg-icon-name="browse" page="browse">
</sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg" svg-icon-name="radio" page="radio">
</sidebar-library-item>
</div>
</div>
</div>
<div class="app-chrome--right">
<template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-chrome-item volume display--large">
<button class="volume-button--small volume" @click="muteButtonPressed()"
:class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')" v-b-tooltip.hover></button>
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()" v-b-tooltip.hover
:title="formatVolumeTooltip()" />
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small cast" :title="$root.getLz('term.cast')"
@click="modals.castMenu = true"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue" :title="$root.getLz('term.queue')" v-b-tooltip.hover
:class="{'active': drawer.panel == 'queue'}" @click="invokeDrawer('queue')"></button>
</div>
<div class="app-chrome-item generic">
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<button class="playback-button--small lyrics" :title="$root.getLz('term.lyrics')" v-b-tooltip.hover
:class="{'active': drawer.panel == 'lyrics'}" @click="invokeDrawer('lyrics')"></button>
<div class="app-chrome--right">
<template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-chrome-item volume display--large">
<button class="volume-button--small volume" @click="muteButtonPressed()"
:class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')"
v-b-tooltip.hover></button>
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()"
v-b-tooltip.hover
:title="formatVolumeTooltip()" />
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small cast" :title="$root.getLz('term.cast')"
@click="modals.castMenu = true"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item generic">
<button class="playback-button--small queue" :title="$root.getLz('term.queue')" v-b-tooltip.hover
:class="{'active': drawer.panel == 'queue'}" @click="invokeDrawer('queue')"></button>
</div>
<div class="app-chrome-item generic">
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<button class="playback-button--small lyrics" :title="$root.getLz('term.lyrics')" v-b-tooltip.hover
:class="{'active': drawer.panel == 'lyrics'}" @click="invokeDrawer('lyrics')"></button>
</template>
<template v-else>
<button class="playback-button--small lyrics"
:style="{'opacity': 0.3, 'pointer-events': 'none'}"></button>
</template>
</div>
</template>
<template v-else>
<button class="playback-button--small lyrics" :style="{'opacity': 0.3, 'pointer-events': 'none'}" ></button>
</template>
</div>
</template>
<template v-else>
<div class="app-chrome-item search">
<div class="search-input-container">
<div class="search-input--icon"></div>
<input type="search" spellcheck="false" @click="$root.appRoute('search');" @focus="search.showHints = true"
@blur="setTimeout(()=>{search.showHints = false}, 300)"
v-on:keyup.enter="searchQuery();search.showHints = false" @change="$root.appRoute('search');" @input="getSearchHints()"
:placeholder="$root.getLz('term.search') + '...'" v-model="search.term" ref="searchInput"
class="search-input" />
<div class="app-chrome-item search">
<div class="search-input-container">
<div class="search-input--icon"></div>
<input type="search" spellcheck="false" @click="$root.appRoute('search');"
@focus="search.showHints = true"
@blur="setTimeout(()=>{search.showHints = false}, 300)"
v-on:keyup.enter="searchQuery();search.showHints = false" @change="$root.appRoute('search');"
@input="getSearchHints()"
:placeholder="$root.getLz('term.search') + '...'" v-model="search.term" ref="searchInput"
class="search-input" />
<div class="search-hints-container" v-if="search.showHints && search.hints.length != 0">
<div class="search-hints">
<button class="search-hint text-overflow-elipsis" v-for="hint in search.hints"
@click="search.term = hint;search.showHints = false;searchQuery(hint)">
{{ hint }}
</button>
<div class="search-hints-container" v-if="search.showHints && search.hints.length != 0">
<div class="search-hints">
<button class="search-hint text-overflow-elipsis" v-for="hint in search.hints"
@click="search.term = hint;search.showHints = false;searchQuery(hint)">
{{ hint }}
</button>
</div>
</div>
</div>
</div>
</template>
<div class="app-chrome-item full-height" id="window-controls-container"
v-if="chrome.windowControlPosition == 'right' && !chrome.nativeControls">
<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>
</template>
<div class="app-chrome-item full-height" id="window-controls-container"
v-if="chrome.windowControlPosition == 'right' && !chrome.nativeControls">
<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 class="app-chrome-item full-height" v-else-if="platform != 'darwin' && !chrome.nativeControls">
<button class="app-mainmenu" @blur="mainMenuVisibility(false)" @click="mainMenuVisibility(true)"
@contextmenu="mainMenuVisibility(true)" :class="{active: chrome.menuOpened}"></button>
</div>
</div>
<div class="app-chrome-item full-height" v-else-if="platform != 'darwin' && !chrome.nativeControls">
<button class="app-mainmenu" @blur="mainMenuVisibility(false)" @click="mainMenuVisibility(true)"
@contextmenu="mainMenuVisibility(true)" :class="{active: chrome.menuOpened}"></button>
</div>
</div>
</div>
</div>

View file

@ -50,10 +50,10 @@
<div id="apple-music-video-player-controls">
<div id="player-exit" title="Close" @click="exitMV();fullscreen(false);">
<svg fill="white" xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21"
aria-role="presentation" focusable="false" @click="exitMV();fullscreen(false);">
aria-role="presentation" focusable="false" @click="exitMV();fullscreen(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"/>
fill-rule="nonzero" />
</svg>
</div>
<div id="captions" v-if="lyricon">{{((lyricon) ? ((lyrics.length > 0 && lyrics[currentLyricsLine] &&
@ -67,7 +67,7 @@
</div>
<div class="playback-info music-player-info">
<div class="song-artist-album-content"
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;">
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap;">
<div class="song-artist" style="display: inline-block">
{{ mk.nowPlayingItem?.attributes?.artistName ?? '' }}
</div>
@ -85,7 +85,7 @@
@mouseup="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
@touchend="mk.seekToTime($event.target.value);setTimeout(()=>{playerLCD.desiredDuration = 0;playerLCD.userInteraction = false}, 1000);"
:max="mk.currentPlaybackDuration" :value="getSongProgress()">
<p style="width: auto">{{ convertTime(mk.currentPlaybackDuration) }}
<p style="width: auto">{{ convertTime(mk.currentPlaybackDuration) }}
</div>
<div class="app-chrome-item display--large">
<div class="app-chrome-item volume display--large">
@ -93,7 +93,8 @@
:class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')"
v-b-tooltip.hover></button>
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" :max="cfg.audio.maxVolume"
<input type="range" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0"
:max="cfg.audio.maxVolume"
v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" @change="checkMuteChange()"
v-b-tooltip.hover :title="formatVolumeTooltip()">
</div>
@ -102,30 +103,30 @@
<button class="playback-button play" @click="mk.play()" v-else
:title="$root.getLz('term.play')" v-b-tooltip.hover></button>
<div class="app-chrome-item generic">
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<button class="playback-button--small lyrics"
:title="$root.getLz('term.lyrics')"
v-b-tooltip.hover
:class="{'active': drawer.panel == 'lyrics'}"
@click="invokeDrawer('lyrics')"></button>
</template>
<template v-else>
<button class="playback-button--small lyrics"
:style="{'opacity': 0.3, 'pointer-events': 'none'}"></button>
</template>
</div>
<template v-if="lyrics && lyrics != [] && lyrics.length > 0">
<button class="playback-button--small lyrics"
:title="$root.getLz('term.lyrics')"
v-b-tooltip.hover
:class="{'active': drawer.panel == 'lyrics'}"
@click="invokeDrawer('lyrics')"></button>
</template>
<template v-else>
<button class="playback-button--small lyrics"
:style="{'opacity': 0.3, 'pointer-events': 'none'}"></button>
</template>
</div>
<div id="player-pip"
@click="pip()"
title="Picture-in-Picture"
v-b-tooltip.hover>
@click="pip()"
title="Picture-in-Picture"
v-b-tooltip.hover>
<%- include("../svg/pip.svg") %>
</div>
<div id="player-fullscreen"
@click="fullscreen(!fullscreenState)"
title="Fullscreen"
v-b-tooltip.hover>
@click="fullscreen(!fullscreenState)"
title="Fullscreen"
v-b-tooltip.hover>
<%- include("../svg/fullscreen.svg") %>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,88 +1,90 @@
<script type="text/x-template" id="add-to-playlist">
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()" @contextmenu.self="app.resetState()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToPlaylist')}}</div>
<button class="close-btn" @click="app.resetState()" :aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<button class="playlist-item"
@click="app.addSelectedToNewPlaylist()" style="width:100%;">
<div class="icon"><%- include("../svg/plus.svg") %></div>
<div class="name">{{app.getLz('action.createPlaylist')}}</div>
</button>
<sidebar-playlist :playlist-select="playlistSelect" :relate-media-items="relateItems" v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" :item="item">
</sidebar-playlist>
</div>
<div class="modal-search">
<div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div>
<input type="search"
ref="searchInput"
style="width:100%;"
spellcheck="false"
:placeholder="app.getLz('term.search') + '...'"
v-model="searchQuery"
@input="search()"
class="search-input">
</div>
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()"
@contextmenu.self="app.resetState()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToPlaylist')}}</div>
<button class="close-btn" @click="app.resetState()" :aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<button class="playlist-item"
@click="app.addSelectedToNewPlaylist()" style="width:100%;">
<div class="icon"><%- include("../svg/plus.svg") %></div>
<div class="name">{{app.getLz('action.createPlaylist')}}</div>
</button>
<sidebar-playlist :playlist-select="playlistSelect" :relate-media-items="relateItems"
v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" :item="item">
</sidebar-playlist>
</div>
<div class="modal-search">
<div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div>
<input type="search"
ref="searchInput"
style="width:100%;"
spellcheck="false"
:placeholder="app.getLz('term.search') + '...'"
v-model="searchQuery"
@input="search()"
class="search-input">
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function () {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
relateItems: []
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.relateItems = [this.$root.selectedMediaItems[0].id]
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
})
},
methods: {
playlistSelect(playlist) {
if (playlist.type != "library-playlist-folders") {
this.addToPlaylist(playlist.id)
}
},
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function() {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
relateItems: []
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.relateItems = [this.$root.selectedMediaItems[0].id]
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
});
</script>
})
},
methods: {
playlistSelect(playlist) {
if (playlist.type != "library-playlist-folders") {
this.addToPlaylist(playlist.id)
}
},
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
}
});
</script>

View file

@ -1,42 +1,45 @@
<script type="text/x-template" id="airplay-modal">
<div class="spatialproperties-panel castmenu modal-fullscreen airplay-modal" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window airplay-modal">
<div class="modal-header">
<div class="modal-title">{{'Enter password'}}</div>
<button class="close-btn" @click="close()" :aria-label="this.$root.getLz('action.close')"></button>
</div>
<div class="modal-content" style="overflow-y: overlay; padding: 3%">
<input type="text" v-model="passcode"/>
</div>
<div class="md-footer">
<div class="row">
<div class="col" >
<button style="width:100%" @click="enterPassword()" class="md-btn md-btn-block md-btn-primary">{{'OK'}}</button>
<div class="spatialproperties-panel castmenu modal-fullscreen airplay-modal" @click.self="close()"
@contextmenu.self="close()">
<div class="modal-window airplay-modal">
<div class="modal-header">
<div class="modal-title">{{'Enter password'}}</div>
<button class="close-btn" @click="close()" :aria-label="this.$root.getLz('action.close')"></button>
</div>
<div class="modal-content" style="overflow-y: overlay; padding: 3%">
<input type="text" v-model="passcode" />
</div>
<div class="md-footer">
<div class="row">
<div class="col">
<button style="width:100%" @click="enterPassword()" class="md-btn md-btn-block md-btn-primary">
{{'OK'}}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('airplay-modal', {
template: '#airplay-modal',
data: function () {
return {
passcode: '',
}
},
mounted() {
},
methods: {
close() {
this.$root.modals.airplayPW = false
},
enterPassword() {
console.log('Entered passCode: ', this.passcode)
ipcRenderer.send("setAirPlayPasscode",this.passcode)
this.close()
}
}
});
</script>
Vue.component('airplay-modal', {
template: '#airplay-modal',
data: function() {
return {
passcode: '',
}
},
mounted() {
},
methods: {
close() {
this.$root.modals.airplayPW = false
},
enterPassword() {
console.log('Entered passCode: ', this.passcode)
ipcRenderer.send("setAirPlayPasscode", this.passcode)
this.close()
}
}
});
</script>

View file

@ -5,93 +5,98 @@
</script>
<script>
Vue.component('animatedartwork-view', {
template: '#animatedartwork-view',
data: function () {
return {
app: this.$root,
hls: null,
}
},
props: {
video: {
type: String,
required: true
},
priority: {
type: Boolean,
default: false
}
},
async mounted() {
if (!this.priority && app.cfg.visual.animated_artwork == "limited") {
return
} else if (app.cfg.visual.animated_artwork == "disabled") {
return
}
if (this.video) {
this.$nextTick(function () {
var config = {
backBufferLength: 0,
enableWebVTT: false,
enableCEA708Captions: false,
emeEnabled: false,
abrEwmaDefaultEstimate: 10000,
testBandwidth: false,
};
if (this.hls) {
this.hls.detachMedia();
} else {
Vue.component('animatedartwork-view', {
template: '#animatedartwork-view',
data: function() {
return {
app: this.$root,
hls: null,
}
},
props: {
video: {
type: String,
required: true
},
priority: {
type: Boolean,
default: false
}
},
async mounted() {
if (!this.priority && app.cfg.visual.animated_artwork == "limited") {
return
} else if (app.cfg.visual.animated_artwork == "disabled") {
return
}
if (this.video) {
this.$nextTick(function() {
var config = {
backBufferLength: 0,
enableWebVTT: false,
enableCEA708Captions: false,
emeEnabled: false,
abrEwmaDefaultEstimate: 10000,
testBandwidth: false,
};
if (this.hls) {
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, p);
let u = this.hls;
var quality = app.cfg.visual.animated_artwork_qualityLevel;
setTimeout(() => {
let levelsnum = u.levels.map((level)=>{return level.width})
if (levelsnum.length > 0) {
let qualities = []
let qualities2 = []
for (let i = 0; i < levelsnum.length; i++) {
if (qualities2.indexOf(levelsnum[i]) == -1) {
qualities.push({level: i, quality: levelsnum[i]})
qualities2.push(levelsnum[i])
}
}
let actualnum = Math.floor(qualities[qualities.length -1 ].level * (quality / 4))
if (quality != 0 ){
quality = qualities[Math.min(actualnum,qualities.length -1 )].level
}
if (quality == 4 ){
quality = qualities[qualities.length - 1].level
}
}
try{
this.hls.loadLevel = parseInt( quality || 1);} catch(e){}},200)
}
})
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"
}
},
async beforeDestroy() {
if (this.hls) {
await this.hls.destroy();
this.hls = null
let p = {
platformInfo: { requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h },
appData: { serviceName: "Apple Music" }
}
}
});
</script>
this.hls.attachMedia(this.$refs.video);
this.hls.loadSource(this.video, p);
let u = this.hls;
var quality = app.cfg.visual.animated_artwork_qualityLevel;
setTimeout(() => {
let levelsnum = u.levels.map((level) => {
return level.width
})
if (levelsnum.length > 0) {
let qualities = []
let qualities2 = []
for (let i = 0; i < levelsnum.length; i++) {
if (qualities2.indexOf(levelsnum[i]) == -1) {
qualities.push({ level: i, quality: levelsnum[i] })
qualities2.push(levelsnum[i])
}
}
let actualnum = Math.floor(qualities[qualities.length - 1].level * (quality / 4))
if (quality != 0) {
quality = qualities[Math.min(actualnum, qualities.length - 1)].level
}
if (quality == 4) {
quality = qualities[qualities.length - 1].level
}
}
try {
this.hls.loadLevel = parseInt(quality || 1);
} catch (e) {
}
}, 200)
}
})
}
},
async beforeDestroy() {
if (this.hls) {
await this.hls.destroy();
this.hls = null
}
}
});
</script>

View file

@ -15,14 +15,14 @@
<transition
<% if(env.appRoutes[i].onEnter) {
%>
v-on:enter="<%- env.appRoutes[i].onEnter %>"
v-on:enter="<%- env.appRoutes[i].onEnter; %>"
<%
}
%>
:name="$root.chrome.desiredPageTransition">
<template
v-if="<%- env.appRoutes[i].condition %>">
<%- env.appRoutes[i].component %>
v-if="<%- env.appRoutes[i].condition; %>">
<%- env.appRoutes[i].component; %>
</template>
</transition>
<% } %>
@ -38,14 +38,13 @@
</script>
<script>
Vue.component('app-content-area', {
template: '#app-content-area',
data: function () {
return {
scrollPos: 0
}
},
methods: {
}
});
</script>
Vue.component('app-content-area', {
template: '#app-content-area',
data: function() {
return {
scrollPos: 0
}
},
methods: {}
});
</script>

View file

@ -1,49 +1,53 @@
<script type="text/x-template" id="artist-chip">
<div class="artist-chip" @click.self="route" tabindex="0">
<div class="artist-chip__image" v-if="image" :style="{backgroundColor: '#' + (artist.attributes.artwork?.bgColor ?? '000')}">
<mediaitem-artwork v-if="artist.id != null" :url="artist.attributes.artwork.url" :size="80"></mediaitem-artwork>
<div class="artist-chip__image" v-if="image"
:style="{backgroundColor: '#' + (artist.attributes.artwork?.bgColor ?? '000')}">
<mediaitem-artwork v-if="artist.id != null" :url="artist.attributes.artwork.url"
:size="80"></mediaitem-artwork>
</div>
<div class="artist-chip__image" v-else>
</div>
<div class="artist-chip__name">
<span>{{ item.attributes.name }}</span>
</div>
<button @click="$root.setArtistFavorite(artist.id, true)" title="Follow" v-if="!$root.followingArtist(artist.id)" class="artist-chip__follow codicon codicon-add"></button>
<button @click="$root.setArtistFavorite(artist.id, false)" title="Following" v-else class="artist-chip__follow codicon codicon-check"></button>
<button @click="$root.setArtistFavorite(artist.id, true)" title="Follow"
v-if="!$root.followingArtist(artist.id)" class="artist-chip__follow codicon codicon-add"></button>
<button @click="$root.setArtistFavorite(artist.id, false)" title="Following" v-else
class="artist-chip__follow codicon codicon-check"></button>
</div>
</script>
<script>
Vue.component('artist-chip', {
props: {
item: {
type: Object,
required: true
}
},
data: function() {
return {
image: false,
artist: {
id: null
}
}
},
template: '#artist-chip',
async mounted() {
let artistId = this.item.id
if (typeof this.item.relationships == "object") {
artistId = this.item.relationships.catalog.data[0].id
}
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artistId}`).then(response => {
this.artist = response.data.data[0];
this.image = true;
});
},
methods: {
route() {
app.appRoute(`artist/${this.artist.id}`);
}
Vue.component('artist-chip', {
props: {
item: {
type: Object,
required: true
}
},
data: function() {
return {
image: false,
artist: {
id: null
}
});
</script>
}
},
template: '#artist-chip',
async mounted() {
let artistId = this.item.id
if (typeof this.item.relationships == "object") {
artistId = this.item.relationships.catalog.data[0].id
}
app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artistId}`).then(response => {
this.artist = response.data.data[0];
this.image = true;
});
},
methods: {
route() {
app.appRoute(`artist/${this.artist.id}`);
}
}
});
</script>

View file

@ -1,37 +1,36 @@
<script type="text/x-template" id="artwork-material">
<div class="artworkMaterial">
<mediaitem-artwork :url="src" :size="500" v-for="image in images"/>
<mediaitem-artwork :url="src" :size="500" v-for="image in images" />
</div>
</script>
<script>
Vue.component('artwork-material', {
template: '#artwork-material',
data: function () {
return {
src: ""
}
},
mounted() {
this.src = app.getMediaItemArtwork(this.url, this.size)
},
props: {
url: {
type: String,
required: true
},
size: {
type: [String, Number],
required: false,
default: '32'
},
images: {
type: [String, Number],
required: false,
default: '2'
}
},
methods: {
}
});
</script>
Vue.component('artwork-material', {
template: '#artwork-material',
data: function() {
return {
src: ""
}
},
mounted() {
this.src = app.getMediaItemArtwork(this.url, this.size)
},
props: {
url: {
type: String,
required: true
},
size: {
type: [String, Number],
required: false,
default: '32'
},
images: {
type: [String, Number],
required: false,
default: '2'
}
},
methods: {}
});
</script>

View file

@ -4,7 +4,8 @@
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('term.audioControls')}}</div>
<button class="close-btn" @click="app.modals.audioControls = false" :aria-label="app.getLz('action.close')"></button>
<button class="close-btn" @click="app.modals.audioControls = false"
:aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<div class="md-option-line">
@ -14,7 +15,7 @@
<div class="md-option-segment md-option-segment_auto percent">
<input type="number"
style="width: 100%; text-align: center; margin-right: 5px;" min="0"
step="2" v-model="volume"/>
step="2" v-model="volume" />
</div>
</div>
<div class="md-option-line">
@ -23,7 +24,7 @@
</div>
<div class="md-option-segment md-option-segment_auto percent">
<input type="number" style="width: 100%; text-align: center; margin-right: 5px;" min="0"
v-model="volumeStep"/>
v-model="volumeStep" />
</div>
</div>
<div class="md-option-line">
@ -32,66 +33,66 @@
</div>
<div class="md-option-segment md-option-segment_auto percent">
<input type="number" style="width: 100%; text-align: center; margin-right: 5px;" min="0"
v-model="maxVolume"/>
v-model="maxVolume" />
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
{{$root.getLz('settings.option.audio.advanced')}}
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.audio.advanced" switch />
</label>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
{{$root.getLz('settings.option.audio.advanced')}}
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.audio.advanced" switch/>
</label>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('audio-controls', {
template: '#audio-controls',
data: function () {
return {
app: this.$root,
maxVolume: this.$root.cfg.audio.maxVolume * 100,
volumeStep: this.$root.cfg.audio.volumeStep * 100,
volume: this.$root.cfg.audio.volume * 100
}
},
watch: {
maxVolume: function (newValue, _oldValue) {
if (newValue > 100) {
newValue = 100
} else {
newValue = Math.round(newValue)
}
this.$root.cfg.audio.maxVolume = newValue / 100
this.maxVolume = newValue
console.log(newValue, _oldValue)
},
volume: function (newValue, _oldValue) {
if (newValue > this.maxVolume) {
newValue = 100
} else {
newValue = Math.round(newValue)
}
this.$root.mk.volume = newValue / 100
this.volume = newValue
console.log(newValue, _oldValue)
},
volumeStep: function (newValue, _oldValue) {
if (newValue > this.maxVolume) {
newValue = 100
} else {
newValue = Math.round(newValue)
}
this.$root.cfg.audio.volumeStep = newValue / 100
this.volumeStep = newValue
console.log(newValue, _oldValue)
}
}
});
</script>
Vue.component('audio-controls', {
template: '#audio-controls',
data: function() {
return {
app: this.$root,
maxVolume: this.$root.cfg.audio.maxVolume * 100,
volumeStep: this.$root.cfg.audio.volumeStep * 100,
volume: this.$root.cfg.audio.volume * 100
}
},
watch: {
maxVolume: function(newValue, _oldValue) {
if (newValue > 100) {
newValue = 100
} else {
newValue = Math.round(newValue)
}
this.$root.cfg.audio.maxVolume = newValue / 100
this.maxVolume = newValue
console.log(newValue, _oldValue)
},
volume: function(newValue, _oldValue) {
if (newValue > this.maxVolume) {
newValue = 100
} else {
newValue = Math.round(newValue)
}
this.$root.mk.volume = newValue / 100
this.volume = newValue
console.log(newValue, _oldValue)
},
volumeStep: function(newValue, _oldValue) {
if (newValue > this.maxVolume) {
newValue = 100
} else {
newValue = Math.round(newValue)
}
this.$root.cfg.audio.volumeStep = newValue / 100
this.volumeStep = newValue
console.log(newValue, _oldValue)
}
}
});
</script>

View file

@ -4,7 +4,8 @@
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('settings.option.audio.changePlaybackRate')}}</div>
<button class="close-btn" @click="app.modals.audioPlaybackRate = false" :aria-label="app.getLz('action.close')"></button>
<button class="close-btn" @click="app.modals.audioPlaybackRate = false"
:aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<div class="md-option-line">
@ -15,7 +16,8 @@
{{playbackRate}} ×
</div>
<div class="md-option-segment md-option-segment_auto">
<input type="range" :step="0.05" min="0.25" :max="2" @wheel="playbackRateWheel" v-model="playbackRate">
<input type="range" :step="0.05" min="0.25" :max="2" @wheel="playbackRateWheel"
v-model="playbackRate">
</div>
</div>
</div>
@ -24,36 +26,36 @@
</script>
<script>
Vue.component('audio-playbackrate', {
template: '#audio-playbackrate',
data: function () {
return {
app: this.$root,
playbackRate: this.$root.cfg.audio.playbackRate
}
},
watch: {
playbackRate: function (newValue, _oldValue) {
this.saveValue(newValue);
}
},
methods: {
playbackRateWheel(event) {
if (app.checkScrollDirectionIsUp(event)) {
this.saveValue(this.$root.cfg.audio.playbackRate + 0.05);
} else {
this.saveValue(this.$root.cfg.audio.playbackRate - 0.05);
}
},
saveValue(newValue) {
newValue = Number(newValue);
if (newValue >= 0.25 && newValue <= 2) {
newValue = String(newValue).length > 4 ? newValue.toFixed(2) : newValue;
this.$root.mk.playbackRate = newValue;
this.$root.cfg.audio.playbackRate = newValue;
this.playbackRate = newValue;
}
}
Vue.component('audio-playbackrate', {
template: '#audio-playbackrate',
data: function() {
return {
app: this.$root,
playbackRate: this.$root.cfg.audio.playbackRate
}
},
watch: {
playbackRate: function(newValue, _oldValue) {
this.saveValue(newValue);
}
},
methods: {
playbackRateWheel(event) {
if (app.checkScrollDirectionIsUp(event)) {
this.saveValue(this.$root.cfg.audio.playbackRate + 0.05);
} else {
this.saveValue(this.$root.cfg.audio.playbackRate - 0.05);
}
});
},
saveValue(newValue) {
newValue = Number(newValue);
if (newValue >= 0.25 && newValue <= 2) {
newValue = String(newValue).length > 4 ? newValue.toFixed(2) : newValue;
this.$root.mk.playbackRate = newValue;
this.$root.cfg.audio.playbackRate = newValue;
this.playbackRate = newValue;
}
}
}
});
</script>

View file

@ -1,10 +1,12 @@
<script type="text/x-template" id="audio-settings">
<template>
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.modals.audioSettings = false" @contextmenu.self="app.modals.audioSettings = false">
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.modals.audioSettings = false"
@contextmenu.self="app.modals.audioSettings = false">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('term.audioSettings')}}</div>
<button class="close-btn" @click="app.modals.audioSettings = false" :aria-label="app.getLz('action.close')"></button>
<button class="close-btn" @click="app.modals.audioSettings = false"
:aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<button class="playlist-item"
@ -34,29 +36,30 @@
</script>
<script>
Vue.component('audio-settings', {
template: '#audio-settings',
data: function () {
return {
app: this.$root,
}
},
props: {},
mounted() {},
methods: {
openEqualizer() {
app.modals.equalizer = true
app.modals.audioSettings = false
},
openAudioControls() {
app.modals.audioControls = true
app.modals.audioSettings = false
},
openAudioPlaybackRate() {
app.modals.audioPlaybackRate = true
app.modals.audioSettings = false
},
},
Vue.component('audio-settings', {
template: '#audio-settings',
data: function() {
return {
app: this.$root,
}
);
</script>
},
props: {},
mounted() {
},
methods: {
openEqualizer() {
app.modals.equalizer = true
app.modals.audioSettings = false
},
openAudioControls() {
app.modals.audioControls = true
app.modals.audioSettings = false
},
openAudioPlaybackRate() {
app.modals.audioPlaybackRate = true
app.modals.audioSettings = false
},
},
}
);
</script>

View file

@ -1,130 +1,151 @@
<script type="text/x-template" id="castmenu">
<div class="spatialproperties-panel castmenu modal-fullscreen" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{$root.getLz('action.cast.todevices')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
</div>
<div class="modal-content" style="overflow-y: overlay; padding: 3%">
<div class="md-labeltext">{{$root.getLz('action.cast.chromecast')}}</div>
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<template v-if="!scanning">
<template v-for="(device) in devices.cast">
<div class="md-option-line" style="cursor: pointer" @click="setCast(device)">
<div class="md-option-segment">
{{ device.name }}
<br>
<small>{{ device.host }}</small>
</div>
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)">
Connected
</div>
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg>
</div>
</div>
</template>
</template>
<template v-else>
<div class="md-option-line" style="cursor: pointer">
<div class="md-option-segment">
{{$root.getLz('action.cast.scanning')}}
</div>
</div>
</template>
</div>
<div class="md-labeltext" >{{$root.getLz('action.cast.airplay')}}</div>
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<div class="md-option-line">
<div class="md-option-segment">
{{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on Samsung/LG/Sony devices will be added later'}}
<!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} -->
<template v-if="true" v-for="(device) in devices.airplay">
<div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)">
<div class="spatialproperties-panel castmenu modal-fullscreen" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{$root.getLz('action.cast.todevices')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
</div>
<div class="modal-content" style="overflow-y: overlay; padding: 3%">
<div class="md-labeltext">{{$root.getLz('action.cast.chromecast')}}</div>
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<template v-if="!scanning">
<template v-for="(device) in devices.cast">
<div class="md-option-line" style="cursor: pointer" @click="setCast(device)">
<div class="md-option-segment">
{{ device.name }}
<br>
<small>{{ device.host }}</small>
</div>
<div class="md-option-segment_auto"
style="display: flex;justify-content: center;align-items: center"
v-if="activeCasts.includes(device)">
Connected
</div>
<div class="md-option-segment_auto" v-else
style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve" class="castPlayIndicator"><path
d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z"
style="fill-rule:nonzero"></path></svg>
</div>
</div>
</template>
</template>
<template v-else>
<div class="md-option-line" style="cursor: pointer">
<div class="md-option-segment">
{{ device.name }}
<br>
<small>{{ device.host }}</small>
</div>
<div class="md-option-segment_auto" style="display: flex;justify-content: center;align-items: center" v-if="activeCasts.includes(device)">
Connected
</div>
<div class="md-option-segment_auto" v-else style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" class="castPlayIndicator"><path d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z" style="fill-rule:nonzero"></path></svg>
{{$root.getLz('action.cast.scanning')}}
</div>
</div>
</template>
</div>
<div class="md-labeltext">{{$root.getLz('action.cast.airplay')}}</div>
<div class="md-option-container" style="margin-top: 12px;margin-bottom: 12px;overflow-y: scroll;">
<div class="md-option-line">
<div class="md-option-segment">
{{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on
Samsung/LG/Sony devices will be added later'}}
<!-- {{$root.getLz('action.cast.airplay.underdevelopment')}} -->
<template v-if="true" v-for="(device) in devices.airplay">
<div class="md-option-line" style="cursor: pointer" @click="setAirPlayCast(device)">
<div class="md-option-segment">
{{ device.name }}
<br>
<small>{{ device.host }}</small>
</div>
<div class="md-option-segment_auto"
style="display: flex;justify-content: center;align-items: center"
v-if="activeCasts.includes(device)">
Connected
</div>
<div class="md-option-segment_auto" v-else
style="display: flex;justify-content: center;align-items: center">
<svg width="20" height="20" viewBox="0 0 34 34" fill="#fff" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
class="castPlayIndicator"><path
d="M28.228,18.327l-16.023,8.983c-0.99,0.555 -2.205,-0.17 -2.205,-1.318l0,-17.984c0,-1.146 1.215,-1.873 2.205,-1.317l16.023,8.982c1.029,0.577 1.029,2.077 0,2.654Z"
style="fill-rule:nonzero"></path></svg>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
<div class="md-footer">
<div class="row">
<div class="col" v-if="activeCasts.length != 0">
<button style="width:100%" @click="stopCasting()" class="md-btn md-btn-block md-btn-primary">
{{$root.getLz('action.cast.stop')}}
</button>
</div>
<div class="col">
<button style="width:100%" class="md-btn md-btn-block" @click="scan()">
{{$root.getLz('action.cast.scan')}}
</button>
</div>
</div>
</div>
</div>
</div>
<div class="md-footer">
<div class="row">
<div class="col" v-if="activeCasts.length != 0">
<button style="width:100%" @click="stopCasting()" class="md-btn md-btn-block md-btn-primary">{{$root.getLz('action.cast.stop')}}</button>
</div>
<div class="col">
<button style="width:100%" class="md-btn md-btn-block" @click="scan()">{{$root.getLz('action.cast.scan')}}</button>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('castmenu', {
template: '#castmenu',
data: function () {
return {
devices: {
cast: [],
airplay: []
},
scanning: false,
activeCasts: this.$root.activeCasts,
Vue.component('castmenu', {
template: '#castmenu',
data: function() {
return {
devices: {
cast: [],
airplay: []
},
scanning: false,
activeCasts: this.$root.activeCasts,
}
},
mounted() {
this.scan();
},
watch:{
activeCasts: function (newVal, oldVal) {
this.$root.activeCasts = this.activeCasts;
}},
methods: {
close() {
this.$root.modals.castMenu = false
},
scan() {
let self = this;
this.scanning = true;
ipcRenderer.send('getChromeCastDevices', '');
ipcRenderer.send("getAirplayDevice","")
setTimeout(() => {
self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices");
self.devices.airplay = ipcRenderer.sendSync("getKnownAirplayDevices");
self.scanning = false;
}, 2000);
console.log(this.devices);
// vm.$forceUpdate();
},
setCast(device) {
CiderAudio.sendAudio();
this.activeCasts.push(device);
ipcRenderer.send('performGCCast', device, "Cider", "Playing ...", "Test build", '');
},
setAirPlayCast(device) {
this.activeCasts.push(device);
ipcRenderer.send("performAirplayPCM",device.host,device.port,null,"","","","",device.txt,device.airplay2)
},
stopCasting() {
CiderAudio.stopAudio();
ipcRenderer.send('disconnectAirplay', '');
ipcRenderer.send('stopGCast', '');
this.activeCasts = [];
// vm.$forceUpdate();
},
}
});
</script>
}
},
mounted() {
this.scan();
},
watch: {
activeCasts: function(newVal, oldVal) {
this.$root.activeCasts = this.activeCasts;
}
},
methods: {
close() {
this.$root.modals.castMenu = false
},
scan() {
let self = this;
this.scanning = true;
ipcRenderer.send('getChromeCastDevices', '');
ipcRenderer.send("getAirplayDevice", "")
setTimeout(() => {
self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices");
self.devices.airplay = ipcRenderer.sendSync("getKnownAirplayDevices");
self.scanning = false;
}, 2000);
console.log(this.devices);
// vm.$forceUpdate();
},
setCast(device) {
CiderAudio.sendAudio();
this.activeCasts.push(device);
ipcRenderer.send('performGCCast', device, "Cider", "Playing ...", "Test build", '');
},
setAirPlayCast(device) {
this.activeCasts.push(device);
ipcRenderer.send("performAirplayPCM", device.host, device.port, null, "", "", "", "", device.txt, device.airplay2)
},
stopCasting() {
CiderAudio.stopAudio();
ipcRenderer.send('disconnectAirplay', '');
ipcRenderer.send('stopGCast', '');
this.activeCasts = [];
// vm.$forceUpdate();
},
}
});
</script>

View file

@ -4,15 +4,17 @@
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToLibrary')}}</div>
<button class="close-btn" @click="app.resetState()" :aria-label="app.getLz('action.close')"></button>
<button class="close-btn" @click="app.resetState()"
:aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<button class="playlist-item"
:class="{ focused: playlist.id == focused }"
@click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted" v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'">
<div class="icon"><%- include("../svg/playlist.svg") %></div>
<div class="name">{{ playlist.attributes.name }}</div>
</button>
:class="{ focused: playlist.id == focused }"
@click="addToPlaylist(playlist.id)" style="width:100%;" v-for="playlist in playlistSorted"
v-if="playlist.attributes.canEdit && playlist.type != 'library-playlist-folders'">
<div class="icon"><%- include("../svg/playlist.svg") %></div>
<div class="name">{{ playlist.attributes.name }}</div>
</button>
</div>
<div class="modal-search">
<div class="search-input-container" style="width:100%;margin: 16px 0;">
@ -33,50 +35,50 @@
</script>
<script>
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function () {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
})
},
methods: {
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function() {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
});
</script>
})
},
methods: {
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
}
});
</script>

View file

@ -1,434 +1,483 @@
<script type="text/x-template" id="eq-view">
<div class="modal-fullscreen equalizer-panel" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window" >
<div class="modal-header">
<div class="modal-title">{{$root.getLz('term.equalizer')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select" style="width:220px;text-align:center;margin-right:245px" v-model="$root.cfg.audio.equalizer.preset" v-on:change="changePreset($root.cfg.audio.equalizer.preset)">
<optgroup :label="$root.getLz('term.userPresets')">
<option v-for="preset in $root.cfg.audio.equalizer.presets" :value="preset.preset">{{preset.name}}</option>
</optgroup>
<optgroup :label="$root.getLz('term.defaultPresets')">
<option v-for="preset in defaultPresets" :value="preset.preset">{{preset.name}}</option>
</optgroup>
</select>
</div>
</div>
<div class="modal-content">
<!-- BANDS = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]; -->
<div class="inputs-container">
<div class="input-container mini">
{{$root.cfg.audio.equalizer.vibrantBass}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="-15" max="15" step="1" v-model="$root.cfg.audio.equalizer.vibrantBass" @change="changeVibrantBass()">
Vibrant Bass
</div>
<div class="input-container mini">
{{$root.cfg.audio.equalizer.mix}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="0" max="2" step="0.1" v-model="$root.cfg.audio.equalizer.mix" @change="changeMix()">
Mix
</div>
<div class="input-container header mini">
Gain
<input type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" >
<div class="freq-header">Freq</div>
<div>Q</div>
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input type="number" class="eq-freq" orient="vertical" min="22" max="44" step="2" v-model="$root.cfg.audio.equalizer.frequencies[0]" @change="changeFreq(0)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[0]" @change="changeQ(0)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input type="number" class="eq-freq" orient="vertical" min="44" max="88" step="4" v-model="$root.cfg.audio.equalizer.frequencies[1]" @change="changeFreq(1)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[1]" @change="changeQ(1)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input type="number" class="eq-freq" orient="vertical" min="88" max="177" step="8" v-model="$root.cfg.audio.equalizer.frequencies[2]" @change="changeFreq(2)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[2]" @change="changeQ(2)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input type="number" class="eq-freq" orient="vertical" min="177" max="355" step="16" v-model="$root.cfg.audio.equalizer.frequencies[3]" @change="changeFreq(3)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[3]" @change="changeQ(3)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input type="number" class="eq-freq" orient="vertical" min="355" max="710" step="32" v-model="$root.cfg.audio.equalizer.frequencies[4]" @change="changeFreq(4)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[4]" @change="changeQ(4)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input type="number" class="eq-freq" orient="vertical" min="710" max="1420" step="64" v-model="$root.cfg.audio.equalizer.frequencies[5]" @change="changeFreq(5)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[5]" @change="changeQ(5)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input type="number" class="eq-freq" orient="vertical" min="1420" max="2840" step="128" v-model="$root.cfg.audio.equalizer.frequencies[6]" @change="changeFreq(6)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[6]" @change="changeQ(6)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input type="number" class="eq-freq" orient="vertical" min="2840" max="5680" step="256" v-model="$root.cfg.audio.equalizer.frequencies[7]" @change="changeFreq(7)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[7]" @change="changeQ(7)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input type="number" class="eq-freq" orient="vertical" min="5680" max="11360" step="512" v-model="$root.cfg.audio.equalizer.frequencies[8]" @change="changeFreq(8)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[8]" @change="changeQ(8)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input type="number" class="eq-freq" orient="vertical" min="11360" max="22720" step="1024" v-model="$root.cfg.audio.equalizer.frequencies[9]" @change="changeFreq(9)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1" v-model="$root.cfg.audio.equalizer.Q[9]" @change="changeQ(9)">
</div>
</div>
</div>
<div class="modal-lowercontent">
<div class="row">
<div class="col">
<button class="md-btn" style="width:100%" @click="resetGain()">{{$root.getLz('term.reset')}}</button>
</div>
<div class="col">
<button class="md-btn" style="width:100%" @click="presetOptions($event)">{{$root.getLz('term.menu')}}</button>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('eq-view', {
template: '#eq-view',
data: function () {
return {
// app: this.$root,
eqPreset: function () {
this.preset = uuidv4()
this.name = ""
this.frequencies = []
this.gain = []
this.Q = []
this.mix = 1
this.vibrantBass = 0
this.userGenerated = true
},
defaultPresets: [
{
'preset': 'default',
'name': 'Default',
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'boostAiriness',
'name': 'Boost Airiness',
'frequencies': [1169, 1733, 5962, 8688, 14125, 18628, 18628, 19000, 19500, 20000],
'gain': [-1.41, 0.25, 3.33, 0.22, -0.53, 0.2, 3.64, 0, 0, 0],
'Q': [0.405, 2.102, 0.025, 2.5, 7.071, 1.768, 1.146, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'acoustic',
'name': 'Acoustic',
'frequencies': [32, 75, 125, 220, 700, 1000, 2000, 4000, 10000, 16000],
'gain': [0, -8, 0, -0.1, -3, 0, 0, 0, 4, 0],
'Q': [1, 0.2, 1, 2.0, 1.4, 1, 1, 1, 0.1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'clearVocal',
'name': 'Clear Vocal',
'frequencies': [20, 63, 125, 250, 400, 1000, 2000, 4000, 8000, 20000],
'gain': [-22, 0, 0, 0, -3, 0, 1.8, 0, 0, 3.5],
'Q': [0.3, 1, 1, 1, 2.0, 1, 0.7, 1, 1, 0.8],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'instrumentClarity',
'name': 'Instrument Clarity',
'frequencies': [20, 63, 155, 250, 500, 1000, 2000, 5000, 11000, 16000],
'gain': [-15, 0, -3, 0, 0, 0, 0, 3.1, 0, 0],
'Q': [0.5, 1, 2, 1, 1, 1, 1, 1.5, 0.1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'reduceHarshness',
'name': 'Reduce Harshness',
'frequencies': [32, 63, 125, 250, 500, 1128, 2000, 4057, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 2, 0, -6.4, 0, 0],
'Q': [1, 1, 1, 1, 1, 2, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'smileyFace',
'name': 'Smiley Face',
'frequencies': [35, 63, 125, 250, 500, 800, 2000, 4000, 8000, 20000],
'gain': [5, 0, 0, 0, 0, -5, 0, 0, 0, 5],
'Q': [0.1, 1, 1, 1, 1, 0.6, 1, 1, 1, 0.2],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
},
{
'preset': 'bassBoostSurgical',
'name': 'Surgical Bass Boost',
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [2.7, 2.2, 1.6, 1.4, 0.6, 0, 0, 0, 0, 0],
'Q': [1.4, 1.4, 1.4, 1.4, 1.4, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'bassBoostClassic',
'name': 'Classic Bass Boost',
'frequencies': [32, 63, 160, 250, 500, 1000, 2000, 3500, 8000, 20000],
'gain': [2.7, 2.2, 1.6, 1.4, 0.6, 0, 0, 0, 0, 0],
'Q': [0.7, 0.7, 0.7, 0.7, 0.7, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}]
}
},
props: ["src", "url"],
mounted() {
},
methods: {
presetOptions(event) {
let menu = {
items: {
"new": {
"icon": "./assets/feather/plus.svg",
"name": app.getLz('action.newpreset'),
action: () => {
this.addPreset()
}
},
"delete": {
"icon": "./assets/feather/x-circle.svg",
"name": app.getLz('action.deletepreset'),
action: () => {
this.deletePreset()
}
},
"import": {
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.import'),
action: () => {
this.importPreset()
}
},
"export": {
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.export'),
action: () => {
this.exportPreset()
}
},
}
}
if (!this.$root.cfg.audio.equalizer.userGenerated) {
delete menu.items.delete
}
app.showMenuPanel(menu, event)
},
sharePreset(event) {
let menu = {
items: [
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.import'),
"action": function () {
notyf.error("Not implemented yet")
}
},
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.export'),
"action": function () {
notyf.error("Not implemented yet")
}
},
]
}
app.showMenuPanel(menu, event)
},
deletePreset() {
let presets = this.$root.cfg.audio.equalizer.presets
app.confirm(app.getLz('term.deletepreset.warn'), (result) => {
if (result) {
this.changePreset("default")
// find the preset by id (preset) and remove it
let index = presets.findIndex(p => p.preset == this.preset)
presets.splice(index, 1)
notyf.success(app.getLz('term.deletedpreset'))
}
})
},
close() {
app.modals.equalizer = false
},
changeVibrantBass() {
if (app.cfg.audio.equalizer.vibrantBass !== '0') {
try {
for (var i = 0; i < 21; i++) {
CiderAudio.audioNodes.vibrantbassNode[i].gain.value = app.cfg.audio.maikiwiAudio.vibrantBass.gain[i] * (app.cfg.audio.equalizer.vibrantBass / 10);
} CiderAudio.intelliGainComp_n0_0();
}
catch (e) {
CiderAudio.hierarchical_loading();
}
}
else {
CiderAudio.hierarchical_loading();
}
},
changeMix() {
if (Math.max(...app.cfg.audio.equalizer.gain) != 0) {
try {
for (var i = 0; i < 10; i++) {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
}
CiderAudio.intelliGainComp_n0_0();
} catch (e) { CiderAudio.hierarchical_loading(); }
}
},
changeGain(i) {
if (Math.max(...app.cfg.audio.equalizer.gain) != 0) {
try {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
CiderAudio.intelliGainComp_n0_0();
}
catch (e) { CiderAudio.hierarchical_loading(); }
}
else {
CiderAudio.hierarchical_loading();
}
},
changeFreq(i) {
CiderAudio.audioNodes.audioBands[i].frequency.value = app.cfg.audio.equalizer.frequencies[i]
},
changeQ(i) {
CiderAudio.audioNodes.audioBands[i].Q.value = app.cfg.audio.equalizer.Q[i]
},
resetGain() {
this.applyPreset({
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
})
if (app.cfg.audio.equalizer.userGenerated) {
this.saveSelectedPreset()
}
},
addPreset() {
let self = this
app.prompt(app.getLz('term.newpreset.name'), (res) => {
if (res) {
let eqSettings = Clone(app.cfg.audio.equalizer)
let newPreset = new self.eqPreset()
newPreset.name = res
newPreset.frequencies = eqSettings.frequencies
newPreset.gain = eqSettings.gain
newPreset.Q = eqSettings.Q
newPreset.mix = eqSettings.mix
newPreset.vibrantBass = eqSettings.vibrantBass
app.cfg.audio.equalizer.presets.push(newPreset)
notyf.success(app.getLz('term.addedpreset'))
self.changePreset(newPreset.preset)
}
})
},
saveSelectedPreset() {
// Save the current settings to the selected preset
let self = this
//let preset = app.cfg.audio.equalizer.presets[app.cfg.audio.equalizer.preset]
// find the preset by its id (preset)
let preset = app.cfg.audio.equalizer.presets.find(p => p.preset == app.cfg.audio.equalizer.preset)
preset.frequencies = app.cfg.audio.equalizer.frequencies
preset.gain = app.cfg.audio.equalizer.gain
preset.Q = app.cfg.audio.equalizer.Q
preset.mix = app.cfg.audio.equalizer.mix
preset.vibrantBass = app.cfg.audio.equalizer.vibrantBass
notyf.success("Saved Preset")
},
exportPreset() {
let preset = app.cfg.audio.equalizer.presets.find(p => p.preset == app.cfg.audio.equalizer.preset)
this.$root.copyToClipboard(btoa(JSON.stringify(preset)))
},
importPreset() {
let self = this
app.prompt("Enter preset share code", (res) => {
if (res) {
let preset = JSON.parse(atob(res))
if (preset.frequencies && preset.gain && preset.Q && preset.mix && preset.vibrantBass) {
// self.applyPreset(preset)
app.cfg.audio.equalizer.presets.push(preset)
notyf.success(`${preset.name} has been imported.`)
}
else {
notyf.error("Invalid Preset")
}
}
})
},
applyPreset(preset) {
Object.assign(this.$root.cfg.audio.equalizer, preset)
this.changeVibrantBass()
for (var i = 0; i < 10; i++) {
try { CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix }
catch (e) {
CiderAudio.hierarchical_loading();
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
}
CiderAudio.audioNodes.audioBands[i].frequency.value = app.cfg.audio.equalizer.frequencies[i]
CiderAudio.audioNodes.audioBands[i].Q.value = app.cfg.audio.equalizer.Q[i]
}
CiderAudio.intelliGainComp_n0_0();
},
changePreset(id) {
let userPresets = app.cfg.audio.equalizer.presets
let defaultPresets = Clone(this.defaultPresets)
let presets = defaultPresets.concat(userPresets)
console.log(presets)
let preset = presets.find(p => p.preset == id)
console.log(preset)
if (preset) {
this.applyPreset(preset)
}
}
}
});
</script>
<script type="text/x-template" id="eq-view">
<div class="modal-fullscreen equalizer-panel" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{$root.getLz('term.equalizer')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select" style="width:220px;text-align:center;margin-right:245px"
v-model="$root.cfg.audio.equalizer.preset"
v-on:change="changePreset($root.cfg.audio.equalizer.preset)">
<optgroup :label="$root.getLz('term.userPresets')">
<option v-for="preset in $root.cfg.audio.equalizer.presets" :value="preset.preset">
{{preset.name}}
</option>
</optgroup>
<optgroup :label="$root.getLz('term.defaultPresets')">
<option v-for="preset in defaultPresets" :value="preset.preset">{{preset.name}}</option>
</optgroup>
</select>
</div>
</div>
<div class="modal-content">
<!-- BANDS = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]; -->
<div class="inputs-container">
<div class="input-container mini">
{{$root.cfg.audio.equalizer.vibrantBass}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="-15" max="15"
step="1" v-model="$root.cfg.audio.equalizer.vibrantBass" @change="changeVibrantBass()">
Vibrant Bass
</div>
<div class="input-container mini">
{{$root.cfg.audio.equalizer.mix}}
<input tabindex="0" type="range" class="eq-slider mini" orient="vertical" min="0" max="2"
step="0.1" v-model="$root.cfg.audio.equalizer.mix" @change="changeMix()">
Mix
</div>
<div class="input-container header mini">
Gain
<input type="range" class="eq-slider" orient="vertical" min="-12" max="12" step="0.1">
<div class="freq-header">Freq</div>
<div>Q</div>
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[0]" @change="changeGain(0)">
<input type="number" class="eq-freq" orient="vertical" min="22" max="44" step="2"
v-model="$root.cfg.audio.equalizer.frequencies[0]" @change="changeFreq(0)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[0]" @change="changeQ(0)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[1]" @change="changeGain(1)">
<input type="number" class="eq-freq" orient="vertical" min="44" max="88" step="4"
v-model="$root.cfg.audio.equalizer.frequencies[1]" @change="changeFreq(1)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[1]" @change="changeQ(1)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[2]" @change="changeGain(2)">
<input type="number" class="eq-freq" orient="vertical" min="88" max="177" step="8"
v-model="$root.cfg.audio.equalizer.frequencies[2]" @change="changeFreq(2)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[2]" @change="changeQ(2)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[3]" @change="changeGain(3)">
<input type="number" class="eq-freq" orient="vertical" min="177" max="355" step="16"
v-model="$root.cfg.audio.equalizer.frequencies[3]" @change="changeFreq(3)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[3]" @change="changeQ(3)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[4]" @change="changeGain(4)">
<input type="number" class="eq-freq" orient="vertical" min="355" max="710" step="32"
v-model="$root.cfg.audio.equalizer.frequencies[4]" @change="changeFreq(4)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[4]" @change="changeQ(4)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[5]" @change="changeGain(5)">
<input type="number" class="eq-freq" orient="vertical" min="710" max="1420" step="64"
v-model="$root.cfg.audio.equalizer.frequencies[5]" @change="changeFreq(5)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[5]" @change="changeQ(5)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[6]" @change="changeGain(6)">
<input type="number" class="eq-freq" orient="vertical" min="1420" max="2840" step="128"
v-model="$root.cfg.audio.equalizer.frequencies[6]" @change="changeFreq(6)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[6]" @change="changeQ(6)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[7]" @change="changeGain(7)">
<input type="number" class="eq-freq" orient="vertical" min="2840" max="5680" step="256"
v-model="$root.cfg.audio.equalizer.frequencies[7]" @change="changeFreq(7)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[7]" @change="changeQ(7)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[8]" @change="changeGain(8)">
<input type="number" class="eq-freq" orient="vertical" min="5680" max="11360" step="512"
v-model="$root.cfg.audio.equalizer.frequencies[8]" @change="changeFreq(8)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[8]" @change="changeQ(8)">
</div>
<div class="input-container">
<input tabindex="0" type="number" class="eq-freq" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input tabindex="0" type="range" class="eq-slider" orient="vertical" min="-12" max="12"
step="0.1" v-model="$root.cfg.audio.equalizer.gain[9]" @change="changeGain(9)">
<input type="number" class="eq-freq" orient="vertical" min="11360" max="22720" step="1024"
v-model="$root.cfg.audio.equalizer.frequencies[9]" @change="changeFreq(9)">
<input type="number" class="eq-q" orient="vertical" min="0" max="5" step="0.1"
v-model="$root.cfg.audio.equalizer.Q[9]" @change="changeQ(9)">
</div>
</div>
</div>
<div class="modal-lowercontent">
<div class="row">
<div class="col">
<button class="md-btn" style="width:100%" @click="resetGain()">{{$root.getLz('term.reset')}}
</button>
</div>
<div class="col">
<button class="md-btn" style="width:100%" @click="presetOptions($event)">
{{$root.getLz('term.menu')}}
</button>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('eq-view', {
template: '#eq-view',
data: function() {
return {
// app: this.$root,
eqPreset: function() {
this.preset = uuidv4()
this.name = ""
this.frequencies = []
this.gain = []
this.Q = []
this.mix = 1
this.vibrantBass = 0
this.userGenerated = true
},
defaultPresets: [
{
'preset': 'default',
'name': 'Default',
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'boostAiriness',
'name': 'Boost Airiness',
'frequencies': [1169, 1733, 5962, 8688, 14125, 18628, 18628, 19000, 19500, 20000],
'gain': [-1.41, 0.25, 3.33, 0.22, -0.53, 0.2, 3.64, 0, 0, 0],
'Q': [0.405, 2.102, 0.025, 2.5, 7.071, 1.768, 1.146, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'acoustic',
'name': 'Acoustic',
'frequencies': [32, 75, 125, 220, 700, 1000, 2000, 4000, 10000, 16000],
'gain': [0, -8, 0, -0.1, -3, 0, 0, 0, 4, 0],
'Q': [1, 0.2, 1, 2.0, 1.4, 1, 1, 1, 0.1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'clearVocal',
'name': 'Clear Vocal',
'frequencies': [20, 63, 125, 250, 400, 1000, 2000, 4000, 8000, 20000],
'gain': [-22, 0, 0, 0, -3, 0, 1.8, 0, 0, 3.5],
'Q': [0.3, 1, 1, 1, 2.0, 1, 0.7, 1, 1, 0.8],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'instrumentClarity',
'name': 'Instrument Clarity',
'frequencies': [20, 63, 155, 250, 500, 1000, 2000, 5000, 11000, 16000],
'gain': [-15, 0, -3, 0, 0, 0, 0, 3.1, 0, 0],
'Q': [0.5, 1, 2, 1, 1, 1, 1, 1.5, 0.1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'reduceHarshness',
'name': 'Reduce Harshness',
'frequencies': [32, 63, 125, 250, 500, 1128, 2000, 4057, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 2, 0, -6.4, 0, 0],
'Q': [1, 1, 1, 1, 1, 2, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'smileyFace',
'name': 'Smiley Face',
'frequencies': [35, 63, 125, 250, 500, 800, 2000, 4000, 8000, 20000],
'gain': [5, 0, 0, 0, 0, -5, 0, 0, 0, 5],
'Q': [0.1, 1, 1, 1, 1, 0.6, 1, 1, 1, 0.2],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
},
{
'preset': 'bassBoostSurgical',
'name': 'Surgical Bass Boost',
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [2.7, 2.2, 1.6, 1.4, 0.6, 0, 0, 0, 0, 0],
'Q': [1.4, 1.4, 1.4, 1.4, 1.4, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}, {
'preset': 'bassBoostClassic',
'name': 'Classic Bass Boost',
'frequencies': [32, 63, 160, 250, 500, 1000, 2000, 3500, 8000, 20000],
'gain': [2.7, 2.2, 1.6, 1.4, 0.6, 0, 0, 0, 0, 0],
'Q': [0.7, 0.7, 0.7, 0.7, 0.7, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
'userGenerated': false
}]
}
},
props: ["src", "url"],
mounted() {
},
methods: {
presetOptions(event) {
let menu = {
items: {
"new": {
"icon": "./assets/feather/plus.svg",
"name": app.getLz('action.newpreset'),
action: () => {
this.addPreset()
}
},
"delete": {
"icon": "./assets/feather/x-circle.svg",
"name": app.getLz('action.deletepreset'),
action: () => {
this.deletePreset()
}
},
"import": {
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.import'),
action: () => {
this.importPreset()
}
},
"export": {
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.export'),
action: () => {
this.exportPreset()
}
},
}
}
if (!this.$root.cfg.audio.equalizer.userGenerated) {
delete menu.items.delete
}
app.showMenuPanel(menu, event)
},
sharePreset(event) {
let menu = {
items: [
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.import'),
"action": function() {
notyf.error("Not implemented yet")
}
},
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.export'),
"action": function() {
notyf.error("Not implemented yet")
}
},
]
}
app.showMenuPanel(menu, event)
},
deletePreset() {
let presets = this.$root.cfg.audio.equalizer.presets
app.confirm(app.getLz('term.deletepreset.warn'), (result) => {
if (result) {
this.changePreset("default")
// find the preset by id (preset) and remove it
let index = presets.findIndex(p => p.preset == this.preset)
presets.splice(index, 1)
notyf.success(app.getLz('term.deletedpreset'))
}
})
},
close() {
app.modals.equalizer = false
},
changeVibrantBass() {
if (app.cfg.audio.equalizer.vibrantBass !== '0') {
try {
for (var i = 0; i < 21; i++) {
CiderAudio.audioNodes.vibrantbassNode[i].gain.value = app.cfg.audio.maikiwiAudio.vibrantBass.gain[i] * (app.cfg.audio.equalizer.vibrantBass / 10);
}
CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading();
}
} else {
CiderAudio.hierarchical_loading();
}
},
changeMix() {
if (Math.max(...app.cfg.audio.equalizer.gain) != 0) {
try {
for (var i = 0; i < 10; i++) {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
}
CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading();
}
}
},
changeGain(i) {
if (Math.max(...app.cfg.audio.equalizer.gain) != 0) {
try {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
CiderAudio.intelliGainComp_n0_0();
} catch (e) {
CiderAudio.hierarchical_loading();
}
} else {
CiderAudio.hierarchical_loading();
}
},
changeFreq(i) {
CiderAudio.audioNodes.audioBands[i].frequency.value = app.cfg.audio.equalizer.frequencies[i]
},
changeQ(i) {
CiderAudio.audioNodes.audioBands[i].Q.value = app.cfg.audio.equalizer.Q[i]
},
resetGain() {
this.applyPreset({
'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
'gain': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'Q': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
'mix': 1,
'vibrantBass': 0,
})
if (app.cfg.audio.equalizer.userGenerated) {
this.saveSelectedPreset()
}
},
addPreset() {
let self = this
app.prompt(app.getLz('term.newpreset.name'), (res) => {
if (res) {
let eqSettings = Clone(app.cfg.audio.equalizer)
let newPreset = new self.eqPreset()
newPreset.name = res
newPreset.frequencies = eqSettings.frequencies
newPreset.gain = eqSettings.gain
newPreset.Q = eqSettings.Q
newPreset.mix = eqSettings.mix
newPreset.vibrantBass = eqSettings.vibrantBass
app.cfg.audio.equalizer.presets.push(newPreset)
notyf.success(app.getLz('term.addedpreset'))
self.changePreset(newPreset.preset)
}
})
},
saveSelectedPreset() {
// Save the current settings to the selected preset
let self = this
//let preset = app.cfg.audio.equalizer.presets[app.cfg.audio.equalizer.preset]
// find the preset by its id (preset)
let preset = app.cfg.audio.equalizer.presets.find(p => p.preset == app.cfg.audio.equalizer.preset)
preset.frequencies = app.cfg.audio.equalizer.frequencies
preset.gain = app.cfg.audio.equalizer.gain
preset.Q = app.cfg.audio.equalizer.Q
preset.mix = app.cfg.audio.equalizer.mix
preset.vibrantBass = app.cfg.audio.equalizer.vibrantBass
notyf.success("Saved Preset")
},
exportPreset() {
let preset = app.cfg.audio.equalizer.presets.find(p => p.preset == app.cfg.audio.equalizer.preset)
this.$root.copyToClipboard(btoa(JSON.stringify(preset)))
},
importPreset() {
let self = this
app.prompt("Enter preset share code", (res) => {
if (res) {
let preset = JSON.parse(atob(res))
if (preset.frequencies && preset.gain && preset.Q && preset.mix && preset.vibrantBass) {
// self.applyPreset(preset)
app.cfg.audio.equalizer.presets.push(preset)
notyf.success(`${preset.name} has been imported.`)
} else {
notyf.error("Invalid Preset")
}
}
})
},
applyPreset(preset) {
Object.assign(this.$root.cfg.audio.equalizer, preset)
this.changeVibrantBass()
for (var i = 0; i < 10; i++) {
try {
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
} catch (e) {
CiderAudio.hierarchical_loading();
CiderAudio.audioNodes.audioBands[i].gain.value = app.cfg.audio.equalizer.gain[i] * app.cfg.audio.equalizer.mix
}
CiderAudio.audioNodes.audioBands[i].frequency.value = app.cfg.audio.equalizer.frequencies[i]
CiderAudio.audioNodes.audioBands[i].Q.value = app.cfg.audio.equalizer.Q[i]
}
CiderAudio.intelliGainComp_n0_0();
},
changePreset(id) {
let userPresets = app.cfg.audio.equalizer.presets
let defaultPresets = Clone(this.defaultPresets)
let presets = defaultPresets.concat(userPresets)
console.log(presets)
let preset = presets.find(p => p.preset == id)
console.log(preset)
if (preset) {
this.applyPreset(preset)
}
}
}
});
</script>

View file

@ -14,19 +14,26 @@
</div>
<div class="fs-header" v-if="immersiveEnabled">
<div class="top-nav-group">
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg" svg-icon-name="home" page="home">
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('home.title')"
svg-icon="./assets/feather/home.svg" svg-icon-name="home" page="home">
</sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg" svg-icon-name="listenNow"
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.listenNow')"
svg-icon="./assets/feather/play-circle.svg" svg-icon-name="listenNow"
page="listen_now"></sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg" svg-icon-name="browse" page="browse">
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.browse')"
svg-icon="./assets/feather/globe.svg" svg-icon-name="browse" page="browse">
</sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg" svg-icon-name="radio" page="radio">
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.radio')"
svg-icon="./assets/feather/radio.svg" svg-icon-name="radio" page="radio">
</sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.library')" svg-icon="./assets/feather/radio.svg" svg-icon-name="library" page="library">
<sidebar-library-item @click.native="tabMode = 'catalog'" :name="$root.getLz('term.library')"
svg-icon="./assets/feather/radio.svg" svg-icon-name="library" page="library">
</sidebar-library-item>
<sidebar-library-item @click.native="tabMode = ''" :name="$root.getLz('term.nowPlaying')" svg-icon="./assets/play.svg" svg-icon-name="nowPlaying" page="nowPlaying">
<sidebar-library-item @click.native="tabMode = ''" :name="$root.getLz('term.nowPlaying')"
svg-icon="./assets/play.svg" svg-icon-name="nowPlaying" page="nowPlaying">
</sidebar-library-item>
<sidebar-library-item @click.native="tabMode = 'catalog'" name="" svg-icon="./assets/search.svg" svg-icon-name="search" page="search">
<sidebar-library-item @click.native="tabMode = 'catalog'" name="" svg-icon="./assets/search.svg"
svg-icon-name="search" page="search">
</sidebar-library-item>
</div>
</div>
@ -59,7 +66,8 @@
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed('album') && app.fullscreen(false)">
{{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ? (app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
{{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ?
(app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
</div>
@ -139,7 +147,7 @@
v-b-tooltip.hover :title="$root.formatVolumeTooltip()">
</div>
</div>
</div>
</div>
</template>
</div>
<div class="col right-col" v-if="tabMode != ''">
@ -175,77 +183,77 @@
</script>
<script>
Vue.component('fullscreen-view', {
template: '#fullscreen-view',
props: {
time: {
type: Number,
required: false
},
lyrics: {
type: Array,
required: false
},
richlyrics: {
type: Array,
required: false
},
image: {
type: String,
required: false
},
},
data: function () {
return {
app: this.$root,
tabMode: "lyrics",
video: null,
immersiveEnabled: app.cfg.advanced.experiments.includes("immersive-preview")
}
},
async mounted() {
if (app.mk.nowPlayingItem._container.type == "albums") {
try {
const result = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/${app.mk.nowPlayingItem._container.type}/${app.mk.nowPlayingItem._container.id}`, {
"fields": "editorialArtwork,editorialVideo",
})).data.data[0].attributes?.editorialVideo?.motionDetailSquare?.video
if (result) {
this.video = result
} else {
this.video = null
}
} catch (e) {
this.video = null
e = null
}
} else if (app.mk.nowPlayingItem._container.type == "library-albums") {
try {
const result = (await app.mk.api.v3.music(`/v1/me/library/albums/${app.mk.nowPlayingItem._container.id}/catalog`
, {"fields": "editorialArtwork,editorialVideo"})).data.data[0].attributes?.editorialVideo?.motionDetailSquare?.video
if (result) {
this.video = result
} else {
this.video = null
}
} catch (e) {
e = null
this.video = null
}
}
},
beforeMount() {
window.addEventListener('keyup', this.onEscapeKeyUp);
},
beforeDestroy() {
window.removeEventListener('keyup', this.onEscapeKeyUp)
},
methods: {
onEscapeKeyUp(event) {
if (event.which === 27) {
app.fullscreen(false);
console.log('js')
}
},
Vue.component('fullscreen-view', {
template: '#fullscreen-view',
props: {
time: {
type: Number,
required: false
},
lyrics: {
type: Array,
required: false
},
richlyrics: {
type: Array,
required: false
},
image: {
type: String,
required: false
},
},
data: function() {
return {
app: this.$root,
tabMode: "lyrics",
video: null,
immersiveEnabled: app.cfg.advanced.experiments.includes("immersive-preview")
}
},
async mounted() {
if (app.mk.nowPlayingItem._container.type == "albums") {
try {
const result = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/${app.mk.nowPlayingItem._container.type}/${app.mk.nowPlayingItem._container.id}`, {
"fields": "editorialArtwork,editorialVideo",
})).data.data[0].attributes?.editorialVideo?.motionDetailSquare?.video
if (result) {
this.video = result
} else {
this.video = null
}
} catch (e) {
this.video = null
e = null
}
});
</script>
} else if (app.mk.nowPlayingItem._container.type == "library-albums") {
try {
const result = (await app.mk.api.v3.music(`/v1/me/library/albums/${app.mk.nowPlayingItem._container.id}/catalog`
, { "fields": "editorialArtwork,editorialVideo" })).data.data[0].attributes?.editorialVideo?.motionDetailSquare?.video
if (result) {
this.video = result
} else {
this.video = null
}
} catch (e) {
e = null
this.video = null
}
}
},
beforeMount() {
window.addEventListener('keyup', this.onEscapeKeyUp);
},
beforeDestroy() {
window.removeEventListener('keyup', this.onEscapeKeyUp)
},
methods: {
onEscapeKeyUp(event) {
if (event.which === 27) {
app.fullscreen(false);
console.log('js')
}
},
}
});
</script>

View file

@ -3,12 +3,12 @@
</script>
<script>
var hw = Vue.component('hello-world', {
template: '#hello-world',
methods: {
sayHello: function () {
alert('Hello world!');
}
}
});
</script>
var hw = Vue.component('hello-world', {
template: '#hello-world',
methods: {
sayHello: function() {
alert('Hello world!');
}
}
});
</script>

View file

@ -6,7 +6,7 @@
<template v-if="item.type == 'artists'">
<mediaitem-square :item="item"></mediaitem-square>
</template>
<template v-else>
<template v-else>
<mediaitem-list-item
v-if="getKind(item) == 'song'"
:index="key"
@ -14,11 +14,13 @@
<mediaitem-square v-else :item="item" :type="getKind(item)"></mediaitem-square>
</template>
</template>
<button v-if="triggerEnabled" style="opacity:0;height: 32px;" v-observe-visibility="{callback: visibilityChanged}">{{this.app.getLz('term.showMore')}}
<button v-if="triggerEnabled" style="opacity:0;height: 32px;"
v-observe-visibility="{callback: visibilityChanged}">{{this.app.getLz('term.showMore')}}
</button>
</div>
<transition name="fabfade">
<button class="top-fab" v-show="showFab" @click="scrollToTop()" :aria-label="app.getLz('action.scrollToTop')">
<button class="top-fab" v-show="showFab" @click="scrollToTop()"
:aria-label="app.getLz('action.scrollToTop')">
<%- include("../svg/arrow-up.svg") %>
</button>
</transition>
@ -28,104 +30,104 @@
</div>
</script>
<script>
Vue.component('inline-collection-list', {
template: "#inline-collection-list",
props: {
data: {
required: true
},
title: {
type: String,
required: false
},
type: {
type: String,
required: false,
default: "artists"
},
parentSelector: {
type: String,
required: false,
default: null
},
},
data: function() {
return {
triggerEnabled: true,
canSeeTrigger: false,
showFab: false,
commonKind: "song",
api: this.$root.mk.api,
loading: false,
app: this.$root,
}
},
methods: {
getKind(item) {
if (typeof item.kind != "undefined") {
this.commonKind = item.kind;
return item.kind
}
if (typeof item.attributes.playParams != "undefined") {
this.commonKind = item.attributes.playParams.kind
return item.attributes.playParams.kind
}
return this.commonKind
},
scrollToTop() {
let target = document.querySelector(".header-text")
document.querySelector(this.parentSelector ?? ".collection-page").scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
})
},
getNext() {
let self = this
this.triggerEnabled = false;
if (typeof this.data.next == "undefined") {
return
}
this.loading = true
this.api.v3.music(this.data.next, app.collectionList.requestBody).then((response) => {
console.log(response)
if (!app.collectionList.response.groups) {
this.data.data = this.data.data.concat(response.data.data);
if (response.data.next) {
this.data.next = response.data.next;
this.triggerEnabled = true;
}
this.loading = false
} else {
if (!response.data.results[app.collectionList.response.groups]) {
this.loading = false
return
}
this.data.data = this.data.data.concat(response.data.results[app.collectionList.response.groups].data);
if (response.data.results[app.collectionList.response.groups].next) {
this.data.next = response.data.results[app.collectionList.response.groups].next;
this.triggerEnabled = true;
this.loading = false
}
}
})
},
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;
}
}
Vue.component('inline-collection-list', {
template: "#inline-collection-list",
props: {
data: {
required: true
},
title: {
type: String,
required: false
},
type: {
type: String,
required: false,
default: "artists"
},
parentSelector: {
type: String,
required: false,
default: null
},
},
data: function() {
return {
triggerEnabled: true,
canSeeTrigger: false,
showFab: false,
commonKind: "song",
api: this.$root.mk.api,
loading: false,
app: this.$root,
}
},
methods: {
getKind(item) {
if (typeof item.kind != "undefined") {
this.commonKind = item.kind;
return item.kind
}
})
</script>
if (typeof item.attributes.playParams != "undefined") {
this.commonKind = item.attributes.playParams.kind
return item.attributes.playParams.kind
}
return this.commonKind
},
scrollToTop() {
let target = document.querySelector(".header-text")
document.querySelector(this.parentSelector ?? ".collection-page").scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
})
},
getNext() {
let self = this
this.triggerEnabled = false;
if (typeof this.data.next == "undefined") {
return
}
this.loading = true
this.api.v3.music(this.data.next, app.collectionList.requestBody).then((response) => {
console.log(response)
if (!app.collectionList.response.groups) {
this.data.data = this.data.data.concat(response.data.data);
if (response.data.next) {
this.data.next = response.data.next;
this.triggerEnabled = true;
}
this.loading = false
} else {
if (!response.data.results[app.collectionList.response.groups]) {
this.loading = false
return
}
this.data.data = this.data.data.concat(response.data.results[app.collectionList.response.groups].data);
if (response.data.results[app.collectionList.response.groups].next) {
this.data.next = response.data.results[app.collectionList.response.groups].next;
this.triggerEnabled = true;
this.loading = false
}
}
})
},
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

@ -4,7 +4,7 @@
<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)">
v-if="verseInRange(segment.ts, segment.te, verse.o)">
<span class="verse verse-active">{{ verse.c }}</span>
</template>
<template v-else>
@ -17,4 +17,4 @@
</template>
</div>
</div>
</script>
</script>

View file

@ -1,192 +1,197 @@
<script type="text/x-template" id="libraryartist-item">
<div v-observe-visibility="{callback: visibilityChanged}"
<div v-observe-visibility="{callback: visibilityChanged}"
@click="select"
class="cd-mediaitem-list-item"
:class="{'mediaitem-selected': app.select_hasMediaItem(guid)}"
@contextmenu="contextMenu">
<div class="artwork" v-show="isVisible" v-if="showArtwork == true">
<mediaitem-artwork
:url="getArtwork()"
size="50"
:type="item.type"></mediaitem-artwork>
</div>
<div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}"
@dblclick="app.routeView(item)">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
<div class="artwork" v-show="isVisible" v-if="showArtwork == true">
<mediaitem-artwork
:url="getArtwork()"
size="50"
:type="item.type"></mediaitem-artwork>
</div>
<div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}"
@dblclick="app.routeView(item)">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
</div>
</div>
</script>
<script>
Vue.component('libraryartist-item', {
template: '#libraryartist-item',
data: function () {
return {
isVisible: false,
addedToLibrary: false,
guid: this.uuidv4(),
app: this.$root
}
},
props: {
'item': {type: Object, required: true},
'parent': {type: String, required: false},
'index': {type: Number, 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},
'contextExt': {type: Object, required: false},
},
methods: {
uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
},
msToMinSec(ms) {
var minutes = Math.floor(ms / 60000);
var seconds = ((ms % 60000) / 1000).toFixed(0);
return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
},
getDataType() {
return this.item.type
},
async select(e) {
let u = this.item
let u1 = await app.mk.api.v3.music(`/v1/me/library/artists/${u.id}/albums`, {
"platform": "web",
"include[library-albums]": "artists,tracks",
"include[library-artists]": "catalog",
"fields[artists]": "url",
"includeOnly": "catalog,artists"
})
app.showCollection({data : Object.assign({},u1.data.data)}, u.attributes.name?? '', '');
app.select_selectMediaItem(u.id, this.getDataType(), this.index, this.guid, true)
},
getArtwork(){
let u = ""
try{
u = this.item.relationships.catalog.data[0].attributes.artwork.url}
catch (e){};
return u;
},
contextMenu(event) {
let self = this
let data_type = this.getDataType()
let item = self.item
item.attributes.artistName = item.attributes.name;
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
app.selectedMediaItems = []
app.select_selectMediaItem(this.item.id, data_type, this.index, this.guid, true)
} else {
useMenu = "multiple"
}
let menus = {
multiple: {
items: [] //
},
normal: {
items: [
{
"name": app.getLz('action.goToArtist'),
"icon": "./assets/feather/user.svg",
"action": function () {
app.searchAndNavigate(self.item, 'artist')
console.log(self.item)
}
},
{
"icon": "./assets/feather/radio.svg",
"name": app.getLz('action.startRadio'),
"action": function () {
app.mk.setStationQueue({song: self.item.attributes.playParams.id ?? self.item.id}).then(() => {
app.mk.play()
app.selectedMediaItems = []
})
}
},
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.share'),
"action": function () {
if (!self.item.attributes.url && self.item.relationships){
if (self.item.relationships.catalog){
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0)? u.data.data[0].attributes.url : u.data.data.attributes.url)})
}
} else {
self.app.copyToClipboard(self.item.attributes.url)}
}
},
]
}
}
if (this.contextExt) {
// if this.context-ext.normal is true append all options to the 'normal' menu which is a kvp of arrays
if (this.contextExt.normal) {
menus.normal.items = menus.normal.items.concat(this.contextExt.normal)
}
if (this.contextExt.multiple) {
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
}
}
//CiderContextMenu.Create(event, menus[useMenu]); // Depreciated Context Menu
app.showMenuPanel(menus[useMenu], event);
},
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
}
},
async removeFromLibrary() {
let item = this.item
let params = {"fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"}
let id = item.id ?? item.attributes.playParams.id
let res = await app.mkapi(item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.playParams.id ?? item.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
id = res.relationships.library.data[0].id
}
let kind = this.item.attributes.playParams.kind ?? this.data.item ?? '';
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
if (item.attributes.playParams.id) {
console.log('remove from library', id)
app.removeFromLibrary(truekind, id)
this.addedToLibrary = false
} else if (item.id) {
console.log('remove from library', id)
app.removeFromLibrary(truekind, id)
this.addedToLibrary = false
}
},
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)
}
}
Vue.component('libraryartist-item', {
template: '#libraryartist-item',
data: function() {
return {
isVisible: false,
addedToLibrary: false,
guid: this.uuidv4(),
app: this.$root
}
},
props: {
'item': { type: Object, required: true },
'parent': { type: String, required: false },
'index': { type: Number, 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 },
'contextExt': { type: Object, required: false },
},
methods: {
uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
},
msToMinSec(ms) {
var minutes = Math.floor(ms / 60000);
var seconds = ((ms % 60000) / 1000).toFixed(0);
return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
},
getDataType() {
return this.item.type
},
async select(e) {
let u = this.item
let u1 = await app.mk.api.v3.music(`/v1/me/library/artists/${u.id}/albums`, {
"platform": "web",
"include[library-albums]": "artists,tracks",
"include[library-artists]": "catalog",
"fields[artists]": "url",
"includeOnly": "catalog,artists"
})
app.showCollection({ data: Object.assign({}, u1.data.data) }, u.attributes.name ?? '', '');
app.select_selectMediaItem(u.id, this.getDataType(), this.index, this.guid, true)
},
getArtwork() {
let u = ""
try {
u = this.item.relationships.catalog.data[0].attributes.artwork.url
} catch (e) {
}
});
</script>
;
return u;
},
contextMenu(event) {
let self = this
let data_type = this.getDataType()
let item = self.item
item.attributes.artistName = item.attributes.name;
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
app.selectedMediaItems = []
app.select_selectMediaItem(this.item.id, data_type, this.index, this.guid, true)
} else {
useMenu = "multiple"
}
let menus = {
multiple: {
items: [] //
},
normal: {
items: [
{
"name": app.getLz('action.goToArtist'),
"icon": "./assets/feather/user.svg",
"action": function() {
app.searchAndNavigate(self.item, 'artist')
console.log(self.item)
}
},
{
"icon": "./assets/feather/radio.svg",
"name": app.getLz('action.startRadio'),
"action": function() {
app.mk.setStationQueue({ song: self.item.attributes.playParams.id ?? self.item.id }).then(() => {
app.mk.play()
app.selectedMediaItems = []
})
}
},
{
"icon": "./assets/feather/share.svg",
"name": app.getLz('action.share'),
"action": function() {
if (!self.item.attributes.url && self.item.relationships) {
if (self.item.relationships.catalog) {
app.mkapi(self.item.attributes.playParams.kind, false, self.item.relationships.catalog.data[0].id).then(u => {
self.app.copyToClipboard((u.data.data.length && u.data.data.length > 0) ? u.data.data[0].attributes.url : u.data.data.attributes.url)
})
}
} else {
self.app.copyToClipboard(self.item.attributes.url)
}
}
},
]
}
}
if (this.contextExt) {
// if this.context-ext.normal is true append all options to the 'normal' menu which is a kvp of arrays
if (this.contextExt.normal) {
menus.normal.items = menus.normal.items.concat(this.contextExt.normal)
}
if (this.contextExt.multiple) {
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
}
}
//CiderContextMenu.Create(event, menus[useMenu]); // Depreciated Context Menu
app.showMenuPanel(menus[useMenu], event);
},
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
}
},
async removeFromLibrary() {
let item = this.item
let params = { "fields[songs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" }
let id = item.id ?? item.attributes.playParams.id
let res = await app.mkapi(item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.playParams.id ?? item.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
id = res.relationships.library.data[0].id
}
let kind = this.item.attributes.playParams.kind ?? this.data.item ?? '';
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
if (item.attributes.playParams.id) {
console.log('remove from library', id)
app.removeFromLibrary(truekind, id)
this.addedToLibrary = false
} else if (item.id) {
console.log('remove from library', id)
app.removeFromLibrary(truekind, id)
this.addedToLibrary = false
}
},
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

@ -1,92 +1,106 @@
<script type="text/x-template" id="listennow-child">
<div v-observe-visibility="{callback: visibilityChanged}">
<template v-if="isVisible && recom.attributes.display.kind != 'MusicSuperHeroShelf'">
<div class="row">
<div class="col" v-if="recom?.relationships['primary-content']?.data?.length > 0" style="display: flex; margin-block:1rem;">
<div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')" class="listennow-chip" style="height: 40px;width: 40px;align-self: center;margin-right: 10px;" :class="{ 'circle': recom?.relationships['primary-content']?.data[0]?.type == 'artists' }">
<mediaitem-artwork v-if="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork != null" :url="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork?.url" :size="100"></mediaitem-artwork>
</div>
<div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')" style="width: fit-content;" :class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}">
<span style="opacity: 0.5; font-weight: bold;"> {{ recom.attributes.titleWithoutName.stringForDisplay }} </span>
<h3 style="margin-block: 0"> {{ recom?.relationships['primary-content']?.data[0].attributes?.name ?? recom.attributes.title.stringForDisplay.replace(recom.attributes.titleWithoutName.stringForDisplay, '') }}</h3>
<template v-if="isVisible && recom.attributes.display.kind != 'MusicSuperHeroShelf'">
<div class="row">
<div class="col" v-if="recom?.relationships['primary-content']?.data?.length > 0"
style="display: flex; margin-block:1rem;">
<div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')"
class="listennow-chip" style="height: 40px;width: 40px;align-self: center;margin-right: 10px;"
:class="{ 'circle': recom?.relationships['primary-content']?.data[0]?.type == 'artists' }">
<mediaitem-artwork
v-if="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork != null"
:url="recom?.relationships['primary-content']?.data[0]?.attributes?.artwork?.url"
:size="100"></mediaitem-artwork>
</div>
<div @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')"
style="width: fit-content;"
:class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}">
<span style="opacity: 0.5; font-weight: bold;"> {{ recom.attributes.titleWithoutName.stringForDisplay }} </span>
<h3 style="margin-block: 0"> {{
recom?.relationships['primary-content']?.data[0].attributes?.name ??
recom.attributes.title.stringForDisplay.replace(recom.attributes.titleWithoutName.stringForDisplay,
'') }}</h3>
</div>
</div>
<div class="col" v-else style="display: flex; margin-block:1rem;">
<h3 @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')"
style="width: fit-content; margin-block:0;"
:class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}">
{{ recom.attributes.title ? recom.attributes.title.stringForDisplay : " "}}</h3>
</div>
<div class="col-auto cider-flex-center" v-if="recom.relationships.contents.data.length >= 10">
<button class="cd-btn-seeall" @click="showCollection(recom)">{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<div class="col" v-else style="display: flex; margin-block:1rem;">
<h3 @click="navigateContent(recom?.relationships['primary-content']?.data[0] ?? recom?.attributes?.title?.contentIds[0] ?? '')" style="width: fit-content; margin-block:0;" :class="{'item-navigate' : (recom?.attributes?.title?.contentIds?.length ?? 0) > 0 | recom?.relationships['primary-content']?.data?.length > 0}">
{{ recom.attributes.title ? recom.attributes.title.stringForDisplay : " "}}</h3>
</div>
<div class="col-auto cider-flex-center" v-if="recom.relationships.contents.data.length >= 10">
<button class="cd-btn-seeall" @click="showCollection(recom)" >{{app.getLz('term.seeAll')}}</button>
</div>
</div>
<template v-if="recom.attributes.display.kind == 'MusicCoverShelf' || recom.attributes.display.kind == 'MusicCircleCoverShelf'">
<mediaitem-scroller-horizontal-large
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
<template
v-if="recom.attributes.display.kind == 'MusicCoverShelf' || recom.attributes.display.kind == 'MusicCircleCoverShelf'">
<mediaitem-scroller-horizontal-large
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else>
<mediaitem-scroller-horizontal-sp
:withReason="index==0"
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-sp>
</template>
</template>
<template v-else>
<mediaitem-scroller-horizontal-sp
:withReason="index==0"
:items="recom.relationships.contents.data.limit(10)"></mediaitem-scroller-horizontal-sp>
<template v-else-if="recom.attributes.display.kind != 'MusicSuperHeroShelf'">
<div style="height:330px"></div>
</template>
</template>
<template v-else-if="recom.attributes.display.kind != 'MusicSuperHeroShelf'">
<div style="height:330px"> </div>
</template>
</div>
</div>
</script>
<script>
Vue.component('listennow-child', {
template: "#listennow-child",
props: ["recom","index"],
data: function () {
return {
isVisible: true,
app: this.$root
}
},
methods: {
visibilityChanged: function (isVisible, entry) {
// this.isVisible = isVisible
},
showCollection: function (recom) {
console.debug(recom)
app.showCollection(recom.relationships.contents, recom.attributes.title ? recom.attributes.title.stringForDisplay : '', 'listen_now')
},
navigateContent: async function (id) {
Vue.component('listennow-child', {
template: "#listennow-child",
props: ["recom", "index"],
data: function() {
return {
isVisible: true,
app: this.$root
}
},
methods: {
visibilityChanged: function(isVisible, entry) {
// this.isVisible = isVisible
},
showCollection: function(recom) {
console.debug(recom)
app.showCollection(recom.relationships.contents, recom.attributes.title ? recom.attributes.title.stringForDisplay : '', 'listen_now')
},
navigateContent: async function(id) {
if (typeof id != "string") {
app.routeView(id)
} else {
try{
let a = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[albums]=${id}`)
let q1 = a.data?.data[0]
if (q1) {
app.routeView(q1)
} else {
let b = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[artists]=${id}`)
let q2 = b.data?.data[0]
if (q2) {
app.routeView(q2)
} else {
let c = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[playlists]=${id}`)
let q3 = c.data?.data[0]
if (q3) {
app.routeView(q3)
}
}
}
} catch (e) {
let b = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[artists]=${id}`)
let q2 = b.data?.data[0]
if (q2) {
app.routeView(q2)
}
}
if (typeof id != "string") {
app.routeView(id)
} else {
try {
let a = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[albums]=${id}`)
let q1 = a.data?.data[0]
if (q1) {
app.routeView(q1)
} else {
let b = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[artists]=${id}`)
let q2 = b.data?.data[0]
if (q2) {
app.routeView(q2)
} else {
let c = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[playlists]=${id}`)
let q3 = c.data?.data[0]
if (q3) {
app.routeView(q3)
}
}
}
} catch (e) {
let b = await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}?ids[artists]=${id}`)
let q2 = b.data?.data[0]
if (q2) {
app.routeView(q2)
}
}
}
})
</script>
}
}
})
</script>

View file

@ -1,69 +1,74 @@
<script type="text/x-template" id="listitem-horizontal">
<div class="listitem-horizontal">
<vue-horizontal>
<div v-for="items in itemPages">
<mediaitem-list-item
v-for="(song, index) in items" :show-library-status="showLibraryStatus" :parent="'listitem-hr' + simplifiedParent"
:index="song.index"
:item="song"></mediaitem-list-item>
</div>
</vue-horizontal>
</div>
<div class="listitem-horizontal">
<vue-horizontal>
<div v-for="items in itemPages">
<mediaitem-list-item
v-for="(song, index) in items" :show-library-status="showLibraryStatus"
:parent="'listitem-hr' + simplifiedParent"
:index="song.index"
:item="song"></mediaitem-list-item>
</div>
</vue-horizontal>
</div>
</script>
<script>
Vue.component('listitem-horizontal', {
template: '#listitem-horizontal',
name: "listitem-horizontal",
props: {
items: {
type: Array,
required: true
},
showLibraryStatus: {
type: Boolean,
default: true
}
},
data: function () {
return {
itemPages: [],
simplifiedParent : []
}
},
mounted() {
// give every item an id
this.items.forEach(function (item, index) {
item.id = index;
});
// split items into pages
this.itemPages = app.arrayToChunk(this.items, 4);
try{
this.simplifiedParent = JSON.stringify(this.items.map ( function(x){return x.attributes.playParams}));
console.debug("simplifiedParent: " + this.simplifiedParent);
}
catch (e){}
},
watch: {
items: function (items) {
// give every item an id
this.items.forEach(function (item, index) {
item.id = index;
});
// split items into pages
this.itemPages = app.arrayToChunk(this.items, 4);
try{
this.simplifiedParent = JSON.stringify(this.items.map ( function(x){return x.attributes.playParams}));
console.log("simplifiedParent: " + this.simplifiedParent);
}
catch (e){}
}
},
methods: {
sayHello: function () {
alert('Hello world!');
}
Vue.component('listitem-horizontal', {
template: '#listitem-horizontal',
name: "listitem-horizontal",
props: {
items: {
type: Array,
required: true
},
showLibraryStatus: {
type: Boolean,
default: true
}
},
data: function() {
return {
itemPages: [],
simplifiedParent: []
}
},
mounted() {
// give every item an id
this.items.forEach(function(item, index) {
item.id = index;
});
// split items into pages
this.itemPages = app.arrayToChunk(this.items, 4);
try {
this.simplifiedParent = JSON.stringify(this.items.map(function(x) {
return x.attributes.playParams
}));
console.debug("simplifiedParent: " + this.simplifiedParent);
} catch (e) {
}
},
watch: {
items: function(items) {
// give every item an id
this.items.forEach(function(item, index) {
item.id = index;
});
// split items into pages
this.itemPages = app.arrayToChunk(this.items, 4);
try {
this.simplifiedParent = JSON.stringify(this.items.map(function(x) {
return x.attributes.playParams
}));
console.log("simplifiedParent: " + this.simplifiedParent);
} catch (e) {
}
});
</script>
}
},
methods: {
sayHello: function() {
alert('Hello world!');
}
}
});
</script>

View file

@ -5,16 +5,17 @@
<h3 class="lyric-line" @click="seekTo(lyric.startTime)" :class="{unsynced : lyric.startTime == 9999999}"
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>
<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>
<div class="norm">
{{ lyric.line }}
</div>
</template>
<div class="lyrics-translation" v-if="lyric.translation && lyric.translation != ''">
{{ lyric.translation }}
@ -35,173 +36,182 @@
</template>
<template v-else>
<div class="no-lyrics">
{{app.getLz('term.noLyrics')}}</div>
{{app.getLz('term.noLyrics')}}
</div>
</template>
</div>
</script>
<script>
Vue.component('lyrics-view', {
template: '#lyrics-view',
props: ["time", "lyrics", "richlyrics", "translation", "onindex", "yoffset"],
data: function () {
return {
app: this.$root,
Vue.component('lyrics-view', {
template: '#lyrics-view',
props: ["time", "lyrics", "richlyrics", "translation", "onindex", "yoffset"],
data: function() {
return {
app: this.$root,
}
},
watch: {
time: function() {
if (((app.lyricon && app.drawer.open) || app.appMode == 'fullscreen') && 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;
}
},
watch: {
time: function () {
if (((app.lyricon && app.drawer.open) || app.appMode == 'fullscreen') && 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: {
seekTo(startTime) {
if (startTime != 9999999) this.app.seekTo(startTime, false);
},
getActiveLyric() {
const delayfix = 0.1
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) || app.appMode == 'fullscreen') && this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)) {
if (this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`)) {
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).classList.remove("active");
}
this.getActiveLyric();
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).classList.add("active")
if (this.checkIfScrollIsStatic) {
let lyricElement = this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)
// this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).scrollIntoView({
// behavior: "smooth",
// block: "nearest", inline: 'start'
// })
let parent = lyricElement.parentElement
let parentRect = parent.getBoundingClientRect()
let lyricElementRect = lyricElement.getBoundingClientRect()
let parentScrollTop = parent.scrollTop
let parentScrollLeft = parent.scrollLeft
let parentScrollTopDiff = parentScrollTop - parentRect.top
let parentScrollLeftDiff = parentScrollLeft - parentRect.left
let lyricElementScrollTop = lyricElementRect.top + parentScrollTopDiff
let lyricElementScrollLeft = lyricElementRect.left + parentScrollLeftDiff
let scrollTopDiff = lyricElementScrollTop - parentScrollTop
let scrollLeftDiff = lyricElementScrollLeft - parentScrollLeft
let scrollTop = parent.scrollTop + scrollTopDiff
let scrollLeft = parent.scrollLeft + scrollLeftDiff
parent.scrollTo({
top: scrollTop - (this.yoffset ?? 128),
left: scrollLeft,
behavior: 'smooth'
})
}
}
} 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");
}
},
methods: {
seekTo(startTime) {
if (startTime != 9999999) this.app.seekTo(startTime, false);
},
getActiveLyric() {
const delayfix = 0.1
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) || app.appMode == 'fullscreen') && this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)) {
if (this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`)) {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 (this.checkIfScrollIsStatic) {
let lyricElement = this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`)
// this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${i}"]`).scrollIntoView({
// behavior: "smooth",
// block: "nearest", inline: 'start'
// })
let parent = lyricElement.parentElement
let parentRect = parent.getBoundingClientRect()
let lyricElementRect = lyricElement.getBoundingClientRect()
let parentScrollTop = parent.scrollTop
let parentScrollLeft = parent.scrollLeft
let parentScrollTopDiff = parentScrollTop - parentRect.top
let parentScrollLeftDiff = parentScrollLeft - parentRect.left
let lyricElementScrollTop = lyricElementRect.top + parentScrollTopDiff
let lyricElementScrollLeft = lyricElementRect.left + parentScrollLeftDiff
let scrollTopDiff = lyricElementScrollTop - parentScrollTop
let scrollLeftDiff = lyricElementScrollLeft - parentScrollLeft
let scrollTop = parent.scrollTop + scrollTopDiff
let scrollLeft = parent.scrollLeft + scrollLeftDiff
parent.scrollTo({
top: scrollTop - (this.yoffset ?? 128),
left: scrollLeft,
behavior: 'smooth'
})
}
}
} 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) || app.appMode == 'fullscreen'){
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 []
},
qqInstrumental(lyrics) {
for(lyric of lyrics){
if (lyric.line.includes("纯音乐") && lyric.line.includes("欣赏")){
return true
}
}
return false
},
checkIfScrollIsStatic : setInterval(() => {
break;
}
}
try {
if ((app.drawer.open) || app.appMode == 'fullscreen') {
try {
if (position === this.$refs.lyricsview.scrollTop) {
clearInterval(checkIfScrollIsStatic)
// do something
}
position = this.$refs.lyricsview.scrollTop
this.$refs.lyricsview.querySelector(`.lyric-line[line-index="${prevLine}"]`).childNodes.classList.remove("verse-active");
} catch (e) {
}
}, 50)
,
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) {
}
});
</script>
},
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 []
},
qqInstrumental(lyrics) {
for (lyric of lyrics) {
if (lyric.line.includes("纯音乐") && lyric.line.includes("欣赏")) {
return true
}
}
return false
},
checkIfScrollIsStatic: setInterval(() => {
try {
if (position === this.$refs.lyricsview.scrollTop) {
clearInterval(checkIfScrollIsStatic)
// do something
}
position = this.$refs.lyricsview.scrollTop
} catch (e) {
}
}, 50)
,
}
});
</script>

View file

@ -1,5 +1,6 @@
<script type="text/x-template" id="mediaitem-artwork">
<div class="mediaitem-artwork" :style="awStyle" @contextmenu="contextMenu" :class="[{'rounded': (type == 'artists')}, classes]" :key="url">
<div class="mediaitem-artwork" :style="awStyle" @contextmenu="contextMenu"
:class="[{'rounded': (type == 'artists')}, classes]" :key="url">
<img :src="imgSrc"
ref="image"
decoding="async"
@ -14,138 +15,138 @@
</script>
<script>
Vue.component('mediaitem-artwork', {
template: '#mediaitem-artwork',
props: {
size: {
type: [String, Number],
default: '120'
},
width: {
type: [String, Number],
required: false
},
bgcolor: {
type: String,
default: ''
},
url: {
type: String,
default: ''
},
type: {
type: String,
default: ''
},
video: {
type: String,
required: false
},
videoPriority: {
type: Boolean,
required: false
},
shadow: {
type: String,
default: ''
},
upscaling: {
type: Boolean,
default: false
}
Vue.component('mediaitem-artwork', {
template: '#mediaitem-artwork',
props: {
size: {
type: [String, Number],
default: '120'
},
width: {
type: [String, Number],
required: false
},
bgcolor: {
type: String,
default: ''
},
url: {
type: String,
default: ''
},
type: {
type: String,
default: ''
},
video: {
type: String,
required: false
},
videoPriority: {
type: Boolean,
required: false
},
shadow: {
type: String,
default: ''
},
upscaling: {
type: Boolean,
default: false
}
},
data: function() {
return {
app: this.$root,
isVisible: false,
style: {
"box-shadow": ""
},
data: function () {
return {
app: this.$root,
isVisible: false,
style: {
"box-shadow": ""
},
awStyle: {
background: this.bgcolor
},
imgStyle: {
opacity: 0,
transition: "opacity .25s linear"
},
classes: [],
imgSrc: ""
}
awStyle: {
background: this.bgcolor
},
computed: {
windowRelativeScale: function () {
return app.$store.state.windowRelativeScale;
}
imgStyle: {
opacity: 0,
transition: "opacity .25s linear"
},
watch: {
windowRelativeScale: function (newValue, oldValue) {
this.swapImage(newValue)
},
url: function (newValue, oldValue) {
this.imgSrc = app.getMediaItemArtwork(this.url, this.size, this.width)
}
},
mounted() {
this.getClasses()
this.imgSrc = app.getMediaItemArtwork(this.url, this.size, this.width)
},
methods: {
swapImage(newValue) {
if(!this.upscaling || window.devicePixelRatio !== 1) return
if (newValue > 1.5) {
this.imgSrc = app.getMediaItemArtwork(this.url, parseInt(this.size * 2.0), parseInt(this.size * 2.0));
}
},
imgLoaded() {
this.imgStyle.opacity = 1
this.swapImage(app.$store.state.windowRelativeScale)
// this.awStyle.background = ""
},
contextMenu(event) {
let self = this
app.showMenuPanel({
items: {
"save": {
name: app.getLz('action.openArtworkInBrowser'),
action: () => {
window.open(app.getMediaItemArtwork(self.url, 1024, 1024))
}
}
}
}, event)
},
getVideoPriority() {
if (app.cfg.visual.animated_artwork == "always") {
return true;
} else if (this.videoPriority && app.cfg.visual.animated_artwork == "limited") {
return true
} else if (app.cfg.visual.animated_artwork == "disabled") {
return false
}
return this.videoPriority
},
getClasses() {
switch (this.shadow) {
case "none":
this.classes.push("no-shadow")
break;
case "large":
this.classes.push("shadow")
break;
case "subtle":
this.classes.push("subtle-shadow")
break;
default:
break;
}
return this.classes;
},
getArtworkStyle() {
return {
width: this.size + 'px',
height: this.size + 'px'
};
}
classes: [],
imgSrc: ""
}
},
computed: {
windowRelativeScale: function() {
return app.$store.state.windowRelativeScale;
}
},
watch: {
windowRelativeScale: function(newValue, oldValue) {
this.swapImage(newValue)
},
url: function(newValue, oldValue) {
this.imgSrc = app.getMediaItemArtwork(this.url, this.size, this.width)
}
},
mounted() {
this.getClasses()
this.imgSrc = app.getMediaItemArtwork(this.url, this.size, this.width)
},
methods: {
swapImage(newValue) {
if (!this.upscaling || window.devicePixelRatio !== 1) return
if (newValue > 1.5) {
this.imgSrc = app.getMediaItemArtwork(this.url, parseInt(this.size * 2.0), parseInt(this.size * 2.0));
}
});
</script>
},
imgLoaded() {
this.imgStyle.opacity = 1
this.swapImage(app.$store.state.windowRelativeScale)
// this.awStyle.background = ""
},
contextMenu(event) {
let self = this
app.showMenuPanel({
items: {
"save": {
name: app.getLz('action.openArtworkInBrowser'),
action: () => {
window.open(app.getMediaItemArtwork(self.url, 1024, 1024))
}
}
}
}, event)
},
getVideoPriority() {
if (app.cfg.visual.animated_artwork == "always") {
return true;
} else if (this.videoPriority && app.cfg.visual.animated_artwork == "limited") {
return true
} else if (app.cfg.visual.animated_artwork == "disabled") {
return false
}
return this.videoPriority
},
getClasses() {
switch (this.shadow) {
case "none":
this.classes.push("no-shadow")
break;
case "large":
this.classes.push("shadow")
break;
case "subtle":
this.classes.push("subtle-shadow")
break;
default:
break;
}
return this.classes;
},
getArtworkStyle() {
return {
width: this.size + 'px',
height: this.size + 'px'
};
}
}
});
</script>

View file

@ -24,14 +24,14 @@
</script>
<script>
Vue.component('mediaitem-hrect', {
template: '#mediaitem-hrect',
props: ['item'],
data: function () {
return {
app: this.$root,
}
},
methods: {}
});
</script>
Vue.component('mediaitem-hrect', {
template: '#mediaitem-hrect',
props: ['item'],
data: function() {
return {
app: this.$root,
}
},
methods: {}
});
</script>

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@
<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')) ?
: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': '205px',
'margin-left': '260px', 'margin-bottom': '140px',
width: '30px',
@ -57,18 +57,18 @@
</script>
<script>
Vue.component('mediaitem-mvview-sp', {
template: '#mediaitem-mvview-sp',
props: ['item', "imagesize", "badge"],
data: function () {
return {
app: this.$root,
}
},
methods: {
log(item) {
console.log(item);
},
}
});
</script>
Vue.component('mediaitem-mvview-sp', {
template: '#mediaitem-mvview-sp',
props: ['item', "imagesize", "badge"],
data: function() {
return {
app: this.$root,
}
},
methods: {
log(item) {
console.log(item);
},
}
});
</script>

View file

@ -27,7 +27,9 @@
<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')">
<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>
@ -51,14 +53,14 @@
</script>
<script>
Vue.component('mediaitem-mvview', {
template: '#mediaitem-mvview',
props: ['item', "imagesize"],
data: function () {
return {
app: this.$root,
}
},
methods: {}
});
</script>
Vue.component('mediaitem-mvview', {
template: '#mediaitem-mvview',
props: ['item', "imagesize"],
data: function() {
return {
app: this.$root,
}
},
methods: {}
});
</script>

View file

@ -1,14 +1,14 @@
<script type="text/x-template" id="mediaitem-scroller-horizontal-large">
<vue-horizontal>
<mediaitem-square :item="item" :key="item?.id ?? ''"
v-for="item in items"></mediaitem-square>
v-for="item in items"></mediaitem-square>
</vue-horizontal>
</script>
<script>
Vue.component('mediaitem-scroller-horizontal-large', {
template: '#mediaitem-scroller-horizontal-large',
props: ['items']
});
</script>
Vue.component('mediaitem-scroller-horizontal-large', {
template: '#mediaitem-scroller-horizontal-large',
props: ['items']
});
</script>

View file

@ -2,15 +2,15 @@
<vue-horizontal>
<template v-if="browsesp">
<mediaitem-mvview-sp
:item="
:item="
((item?.attributes?.kind != null || item?.attributes?.type == 'editorial-elements')
? item :
? item :
((item.relationships && item.relationships.contents ) ? item.relationships.contents.data[0] : item)) ?? (item)"
:imagesize="imagesize"
:badge="item.attributes ?? [] " v-for="item in items"></mediaitem-mvview-sp>
</template>
<template v-else>
<mediaitem-square :kind="kind" size="600" :key="item?.id ?? ''"
<mediaitem-square :kind="kind" size="600" :key="item?.id ?? ''"
:item="item ? ((item.attributes?.kind != null || item.type == 'editorial-elements') ? item : ((item.relationships && item.relationships.contents ) ? item.relationships.contents.data[0] : item)) : []"
:imagesize="imagesize"
v-for="item in items"></mediaitem-square>
@ -20,34 +20,34 @@
<script>
Vue.component('mediaitem-scroller-horizontal-mvview', {
template: '#mediaitem-scroller-horizontal-mvview',
props: {
items: {
type: Array,
required: true
},
imagesize: {
type: Number,
required: false
},
browsesp: {
type: Boolean,
required: false
},
kind: {
type: String,
required: false,
default: ""
}
},
data: function () {
return {
app: this.$root,
}
},
// mounted(){
// console.log('hes',this.items)
// }
});
</script>
Vue.component('mediaitem-scroller-horizontal-mvview', {
template: '#mediaitem-scroller-horizontal-mvview',
props: {
items: {
type: Array,
required: true
},
imagesize: {
type: Number,
required: false
},
browsesp: {
type: Boolean,
required: false
},
kind: {
type: String,
required: false,
default: ""
}
},
data: function() {
return {
app: this.$root,
}
},
// mounted(){
// console.log('hes',this.items)
// }
});
</script>

View file

@ -1,26 +1,26 @@
<script type="text/x-template" id="mediaitem-scroller-horizontal-sp">
<div class="cd-hmedia-scroller hmedia-scroller-card">
<vue-horizontal>
<template>
<mediaitem-square kind="card" :item="item" size="300" :reasonShown="withReason"
v-for="item in items"></mediaitem-square>
</template>
</vue-horizontal>
</div>
<div class="cd-hmedia-scroller hmedia-scroller-card">
<vue-horizontal>
<template>
<mediaitem-square kind="card" :item="item" size="300" :reasonShown="withReason"
v-for="item in items"></mediaitem-square>
</template>
</vue-horizontal>
</div>
</script>
<script>
Vue.component('mediaitem-scroller-horizontal-sp', {
template: '#mediaitem-scroller-horizontal-sp',
props: {
'items': { type: Array , required: false },
'withReason': { type: Boolean, required: false, default: false },
},
data: function () {
return {
app: this.$root,
}
},
methods: {}
});
Vue.component('mediaitem-scroller-horizontal-sp', {
template: '#mediaitem-scroller-horizontal-sp',
props: {
'items': { type: Array, required: false },
'withReason': { type: Boolean, required: false, default: false },
},
data: function() {
return {
app: this.$root,
}
},
methods: {}
});
</script>

View file

@ -6,27 +6,27 @@
</script>
<script>
Vue.component('mediaitem-scroller-horizontal', {
template: '#mediaitem-scroller-horizontal',
props: {
'items': {
type: Array,
required: false
},
'kind': {
type: String,
required: false,
defualt: ""
}
},
data: function() {
return {
app: this.$root,
}
},
mounted() {
// this.$refs.horizontal.refresh()
},
methods: {}
});
</script>
Vue.component('mediaitem-scroller-horizontal', {
template: '#mediaitem-scroller-horizontal',
props: {
'items': {
type: Array,
required: false
},
'kind': {
type: String,
required: false,
defualt: ""
}
},
data: function() {
return {
app: this.$root,
}
},
mounted() {
// this.$refs.horizontal.refresh()
},
methods: {}
});
</script>

File diff suppressed because it is too large Load diff

View file

@ -36,105 +36,105 @@
<script>
Vue.component('cider-menu-panel', {
template: '#cider-menu-panel',
data: function () {
return {
app: this.$root,
menuPanel: this.$root.menuPanel,
content: this.$root.menuPanel.content,
getSvgIcon: this.$root.getSvgIcon,
position: [0, 0],
size: [0, 0],
event: this.$root.menuPanel.event,
direction: "down",
elStyle: {
opacity: 0
}
}
},
mounted() {
if (this.event) {
this.position = [this.event.clientX, this.event.clientY];
}
this.$nextTick(() => {
// this.size = [document.querySelector(".menu-panel-body").offsetWidth, document.querySelector(".menu-panel-body").offsetHeight];
// ugly hack
setTimeout(this.getStyle, 1)
});
},
methods: {
getBodyClasses() {
if (this.direction == "down") {
return ["menu-panel-body-down"]
} else if (this.direction == "up") {
return ["menu-panel-body-up"]
} else {
return ["foo"]
}
},
getClasses(item) {
if (item["active"]) {
return "active";
}
},
getStyle() {
let style = {}
this.size = [this.$refs.menubody.offsetWidth, this.$refs.menubody.offsetHeight];
if (this.event) {
style["position"] = "absolute";
style["left"] = this.event.clientX + "px";
style["top"] = this.event.clientY + "px";
// make sure the menu panel isnt off the screen
if (this.event.clientX + this.size[0] > window.innerWidth) {
style["left"] = (this.event.clientX - this.size[0]) + "px";
}
if (this.event.clientY + this.size[1] > window.innerHeight) {
style["top"] = (this.event.clientY - this.size[1]) + "px";
}
// if the panel is above the mouse, set the direction to up
if (this.event.clientY < this.size[1]) {
this.direction = "up";
} else {
this.direction = "down";
}
// check if the panel is too long and goes off the screen vertically,
// if so move it upwards
if (this.event.clientY + this.size[1] > window.innerHeight) {
style["top"] = (this.event.clientY - this.size[1]) + "px";
}
}
style["opacity"] = 1
this.elStyle = style;
},
getItemStyle(item) {
let style = {}
if (item["disabled"]) {
style = Object.assign(style, {
"pointer-events": "none",
"opacity": "0.5",
});
}
return style
},
canDisplay(item) {
if (!item["hidden"]) {
return true
} else {
return false
}
},
async getActions() {
return this.content.items;
},
action(item) {
item.action()
if (!item["keepOpen"]) {
this.menuPanel.visible = false
}
}
Vue.component('cider-menu-panel', {
template: '#cider-menu-panel',
data: function() {
return {
app: this.$root,
menuPanel: this.$root.menuPanel,
content: this.$root.menuPanel.content,
getSvgIcon: this.$root.getSvgIcon,
position: [0, 0],
size: [0, 0],
event: this.$root.menuPanel.event,
direction: "down",
elStyle: {
opacity: 0
}
});
</script>
}
},
mounted() {
if (this.event) {
this.position = [this.event.clientX, this.event.clientY];
}
this.$nextTick(() => {
// this.size = [document.querySelector(".menu-panel-body").offsetWidth, document.querySelector(".menu-panel-body").offsetHeight];
// ugly hack
setTimeout(this.getStyle, 1)
});
},
methods: {
getBodyClasses() {
if (this.direction == "down") {
return ["menu-panel-body-down"]
} else if (this.direction == "up") {
return ["menu-panel-body-up"]
} else {
return ["foo"]
}
},
getClasses(item) {
if (item["active"]) {
return "active";
}
},
getStyle() {
let style = {}
this.size = [this.$refs.menubody.offsetWidth, this.$refs.menubody.offsetHeight];
if (this.event) {
style["position"] = "absolute";
style["left"] = this.event.clientX + "px";
style["top"] = this.event.clientY + "px";
// make sure the menu panel isnt off the screen
if (this.event.clientX + this.size[0] > window.innerWidth) {
style["left"] = (this.event.clientX - this.size[0]) + "px";
}
if (this.event.clientY + this.size[1] > window.innerHeight) {
style["top"] = (this.event.clientY - this.size[1]) + "px";
}
// if the panel is above the mouse, set the direction to up
if (this.event.clientY < this.size[1]) {
this.direction = "up";
} else {
this.direction = "down";
}
// check if the panel is too long and goes off the screen vertically,
// if so move it upwards
if (this.event.clientY + this.size[1] > window.innerHeight) {
style["top"] = (this.event.clientY - this.size[1]) + "px";
}
}
style["opacity"] = 1
this.elStyle = style;
},
getItemStyle(item) {
let style = {}
if (item["disabled"]) {
style = Object.assign(style, {
"pointer-events": "none",
"opacity": "0.5",
});
}
return style
},
canDisplay(item) {
if (!item["hidden"]) {
return true
} else {
return false
}
},
async getActions() {
return this.content.items;
},
action(item) {
item.action()
if (!item["keepOpen"]) {
this.menuPanel.visible = false
}
}
}
});
</script>

View file

@ -2,186 +2,206 @@
<div class="mini-view" tabindex="0">
<div class="background">
</div>
<div class="player-pin" title="Pin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === false" @click="app.pinMiniPlayer()">
<div class="player-pin" title="Pin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === false"
@click="app.pinMiniPlayer()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" class="feather feather-pin">
<path d="M7.05664 16.3613C7.05664 17.1523 7.59277 17.6797 8.42773 17.6797H13.1299V21.8369C13.1299 23.0762 13.7539 24.3242 14 24.3242C14.2373 24.3242 14.8613 23.0762 14.8613 21.8369V17.6797H19.5635C20.3984 17.6797 20.9346 17.1523 20.9346 16.3613C20.9346 14.4717 19.4316 12.5293 16.9531 11.6152L16.6631 7.52832C17.9727 6.78125 19.0098 5.96387 19.4668 5.38379C19.7041 5.06738 19.8271 4.75098 19.8271 4.46973C19.8271 3.88965 19.3789 3.45898 18.7197 3.45898H9.27148C8.6123 3.45898 8.16406 3.88965 8.16406 4.46973C8.16406 4.75098 8.28711 5.06738 8.52441 5.38379C8.98145 5.96387 10.0186 6.78125 11.3281 7.52832L11.0469 11.6152C8.55957 12.5293 7.05664 14.4717 7.05664 16.3613Z" fill="#ff2654"/>
<path d="M7.05664 16.3613C7.05664 17.1523 7.59277 17.6797 8.42773 17.6797H13.1299V21.8369C13.1299 23.0762 13.7539 24.3242 14 24.3242C14.2373 24.3242 14.8613 23.0762 14.8613 21.8369V17.6797H19.5635C20.3984 17.6797 20.9346 17.1523 20.9346 16.3613C20.9346 14.4717 19.4316 12.5293 16.9531 11.6152L16.6631 7.52832C17.9727 6.78125 19.0098 5.96387 19.4668 5.38379C19.7041 5.06738 19.8271 4.75098 19.8271 4.46973C19.8271 3.88965 19.3789 3.45898 18.7197 3.45898H9.27148C8.6123 3.45898 8.16406 3.88965 8.16406 4.46973C8.16406 4.75098 8.28711 5.06738 8.52441 5.38379C8.98145 5.96387 10.0186 6.78125 11.3281 7.52832L11.0469 11.6152C8.55957 12.5293 7.05664 14.4717 7.05664 16.3613Z"
fill="#ff2654" />
</svg>
</div>
<div class="player-pin" title="Unpin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === true" @click="app.pinMiniPlayer(false)">
<div class="player-pin" title="Unpin to Top" v-if="app.cfg.visual.miniplayer_top_toggle === true"
@click="app.pinMiniPlayer(false)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none" class="feather feather-pin-slashed">
<path d="M9.271 3.459c-.659 0-1.107.43-1.107 1.01 0 .282.114.59.352.897.448.59 1.494 1.415 2.777 2.162l-.07 1.02 8.99 8.991c.458-.202.722-.615.722-1.178 0-1.89-1.503-3.832-3.947-4.746l-.29-4.087c1.275-.747 2.312-1.555 2.76-2.144.246-.308.37-.633.37-.914 0-.58-.45-1.011-1.108-1.011H9.27ZM5.15 6.061l16.076 16.057c.272.281.73.273.993 0a.703.703 0 0 0 0-.984L6.15 5.076a.716.716 0 0 0-1.002 0 .711.711 0 0 0 0 .985Zm1.908 10.3c0 .791.536 1.319 1.37 1.319h4.703v4.157c0 1.24.624 2.487.861 2.487.246 0 .87-1.248.87-2.487V17.81h.413l-5.537-5.545c-1.678 1.002-2.68 2.557-2.68 4.095Z" fill="#ff2654"/>
<path d="M9.271 3.459c-.659 0-1.107.43-1.107 1.01 0 .282.114.59.352.897.448.59 1.494 1.415 2.777 2.162l-.07 1.02 8.99 8.991c.458-.202.722-.615.722-1.178 0-1.89-1.503-3.832-3.947-4.746l-.29-4.087c1.275-.747 2.312-1.555 2.76-2.144.246-.308.37-.633.37-.914 0-.58-.45-1.011-1.108-1.011H9.27ZM5.15 6.061l16.076 16.057c.272.281.73.273.993 0a.703.703 0 0 0 0-.984L6.15 5.076a.716.716 0 0 0-1.002 0 .711.711 0 0 0 0 .985Zm1.908 10.3c0 .791.536 1.319 1.37 1.319h4.703v4.157c0 1.24.624 2.487.861 2.487.246 0 .87-1.248.87-2.487V17.81h.413l-5.537-5.545c-1.678 1.002-2.68 2.557-2.68 4.095Z"
fill="#ff2654" />
</svg>
</div>
<div class="player-exit" title="Close" @click="app.miniPlayer(false)">
<svg fill="#323232e3" width="21" height="21" viewBox="0 0 21 21" aria-role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg">
<svg fill="#323232e3" width="21" height="21" viewBox="0 0 21 21" aria-role="presentation" focusable="false"
xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient gradientUnits="userSpaceOnUse" cx="10.5" cy="10.5" r="10.5" id="gradient-0">
<stop offset="0" style="stop-color: rgba(168, 163, 163, 1)"/>
<stop offset="1" style="stop-color: rgba(118, 111, 111, 1)"/>
<stop offset="0" style="stop-color: rgba(168, 163, 163, 1)" />
<stop offset="1" style="stop-color: rgba(118, 111, 111, 1)" />
</radialGradient>
</defs>
<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" style="stroke-miterlimit: 11; vector-effect: non-scaling-stroke; stroke-width: 31px; fill: url(#gradient-0);"/>
fill-rule="nonzero"
style="stroke-miterlimit: 11; vector-effect: non-scaling-stroke; stroke-width: 31px; fill: url(#gradient-0);" />
</svg>
</div>
</div>
<div class="col artwork-col">
<div class="artwork" @click="app.miniPlayer(false)">
<div class="artwork" @click="app.miniPlayer(false)">
<mediaitem-artwork
:size="600"
:url="image ?? ''"
></mediaitem-artwork>
</div>
<div class="controls-parents">
<template v-if="app.mkReady()">
<div class="app-playback-controls" @mouseover="app.chrome.progresshover = true"
@mouseleave="app.chrome.progresshover = false" @contextmenu="app.nowPlayingContextMenu">
<div class="playback-info">
<div class="song-name">
{{ app.mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap; margin-top: 0.25vh; overflow: hidden; margin-bottom: 5px;">
<div class="item-navigate song-artist" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed(`artist`)">
{{ app.mk.nowPlayingItem["attributes"]["artistName"] }}
<template v-if="app.mkReady()">
<div class="app-playback-controls" @mouseover="app.chrome.progresshover = true"
@mouseleave="app.chrome.progresshover = false" @contextmenu="app.nowPlayingContextMenu">
<div class="playback-info">
<div class="song-name">
{{ app.mk.nowPlayingItem["attributes"]["name"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed('album')">
{{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ? (" — " +
app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
</div>
<div class="song-progress">
<div class="song-duration" style="justify-content: space-between; height: 1px; margin-bottom: 1px;"
:style="[app.chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]">
<p style="width: auto">{{ app.convertTime(app.getSongProgress()) }}</p>
<p style="width: auto">{{ app.convertTime(app.mk.currentPlaybackDuration) }}</p>
<div
style="display: inline-block; -webkit-box-orient: horizontal; white-space: nowrap; margin-top: 0.25vh; overflow: hidden; margin-bottom: 5px;">
<div class="item-navigate song-artist" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed(`artist`)">
{{ app.mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@click="app.getNowPlayingItemDetailed('album')">
{{ (app.mk.nowPlayingItem["attributes"]["albumName"]) ? (" — " +
app.mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div>
</div>
<input type="range" step="0.01" min="0" :style="app.progressBarStyle()"
@input="app.playerLCD.desiredDuration = $event.target.value;app.playerLCD.userInteraction = true"
@mouseup="app.mk.seekToTime($event.target.value);app.playerLCD.desiredDuration = 0;app.playerLCD.userInteraction = false"
:max="app.mk.currentPlaybackDuration" :value="app.getSongProgress()">
<div class="song-progress">
<div class="song-duration"
style="justify-content: space-between; height: 1px; margin-bottom: 1px;"
:style="[app.chrome.progresshover ? {'display': 'flex'} : {'display' : 'none'} ]">
<p style="width: auto">{{ app.convertTime(app.getSongProgress()) }}</p>
<p style="width: auto">{{ app.convertTime(app.mk.currentPlaybackDuration) }}</p>
</div>
<input type="range" step="0.01" min="0" :style="app.progressBarStyle()"
@input="app.playerLCD.desiredDuration = $event.target.value;app.playerLCD.userInteraction = true"
@mouseup="app.mk.seekToTime($event.target.value);app.playerLCD.desiredDuration = 0;app.playerLCD.userInteraction = false"
:max="app.mk.currentPlaybackDuration" :value="app.getSongProgress()">
</div>
</div>
<div class="control-buttons">
<div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="$root.mk.shuffleMode == 0"
:class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')"
v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else
:class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button previous" @click="$root.prevButton()"
:class="$root.isPrevDisabled() && 'disabled'"
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button stop" @click="$root.mk.stop()"
v-if="$root.mk.isPlaying && $root.mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="$root.mk.pause()"
v-else-if="$root.mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
<button class="playback-button play" @click="$root.mk.play()" v-else
:title="$root.getLz('term.play')"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button next" @click="$root.skipToNextItem()"
:class="$root.isNextDisabled() && 'disabled'"
:title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="$root.mk.repeatMode == 0"
:class="$root.isDisabled() && 'disabled'"
@click="$root.mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="$root.mk.repeatMode = 0"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 2"
:title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button>
</div>
</div>
<div class="app-chrome-item volume display--large">
<div class="input-container">
<button class="volume-button--small volume" @click="app.muteButtonPressed()"
:class="{'active': app.cfg.audio.volume == 0}"></button>
<input type="range" class="slider" @wheel="app.volumeWheel"
:step="app.cfg.audio.volumeStep" min="0" :max="app.cfg.audio.maxVolume"
v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()">
</div>
</div>
</div>
<div class="control-buttons">
<div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="$root.mk.shuffleMode == 0" :class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" v-b-tooltip.hover></button>
<button class="playback-button--small shuffle active" v-else :class="$root.isDisabled() && 'disabled'"
@click="$root.mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button previous" @click="$root.prevButton()" :class="$root.isPrevDisabled() && 'disabled'"
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button stop" @click="$root.mk.stop()"
v-if="$root.mk.isPlaying && $root.mk.nowPlayingItem.attributes.playParams.kind == 'radioStation'"
:title="$root.getLz('term.stop')" v-b-tooltip.hover></button>
<button class="playback-button pause" @click="$root.mk.pause()" v-else-if="$root.mk.isPlaying"
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button>
<button class="playback-button play" @click="$root.mk.play()" v-else :title="$root.getLz('term.play')"
v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button next" @click="$root.skipToNextItem()" :class="$root.isNextDisabled() && 'disabled'"
:title="$root.getLz('term.next')" v-b-tooltip.hover></button>
</div>
<div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="$root.mk.repeatMode == 0" :class="$root.isDisabled() && 'disabled'"
@click="$root.mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')" v-b-tooltip.hover></button>
<button class="playback-button--small repeat active" @click="$root.mk.repeatMode = 0"
:class="$root.isDisabled() && 'disabled'" v-else-if="$root.mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover></button>
</div>
</div>
<div class="app-chrome-item volume display--large">
<div class="input-container">
<button class="volume-button--small volume" @click="app.muteButtonPressed()" :class="{'active': app.cfg.audio.volume == 0}"></button>
<input type="range" class="slider" @wheel="app.volumeWheel" :step="app.cfg.audio.volumeStep" min="0" :max="app.cfg.audio.maxVolume" v-model="app.mk.volume"
v-if="typeof app.mk.volume != 'undefined'" @change="app.checkMuteChange()">
</div>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
<!-- <div class="row fs-row">
<div class="col right-col" v-if="tabMode != ''">
<div class="fs-info">
<div>Name</div>
<div>Name</div>
<div>Name</div>
</div>
<div class="lyrics-col" v-if="tabMode == 'lyrics'">
<lyrics-view :yoffset="120" :time="time" :lyrics="lyrics"
:richlyrics="richlyrics"></lyrics-view>
</div>
<div class="queue-col" v-if="tabMode == 'queue'">
<cider-queue v-if="tabMode == 'queue'" ref="queue" ></cider-queue>
</div>
</div>
<!-- <div class="row fs-row">
<div class="col right-col" v-if="tabMode != ''">
<div class="fs-info">
<div>Name</div>
<div>Name</div>
<div>Name</div>
</div>
<div class="lyrics-col" v-if="tabMode == 'lyrics'">
<lyrics-view :yoffset="120" :time="time" :lyrics="lyrics"
:richlyrics="richlyrics"></lyrics-view>
</div>
<div class="queue-col" v-if="tabMode == 'queue'">
<cider-queue v-if="tabMode == 'queue'" ref="queue" ></cider-queue>
</div>
</div>
</div> -->
<!-- <div class="tab-toggles">
<div class="lyrics" :class="{active: tabMode == 'lyrics'}" @click="tabMode = (tabMode == 'lyrics') ? '' : 'lyrics'"></div>
<div class="queue" :class="{active: tabMode == 'queue'}" @click="tabMode = (tabMode == 'queue') ? '' :'queue'"></div>
</div> -->
</div> -->
<!-- <div class="tab-toggles">
<div class="lyrics" :class="{active: tabMode == 'lyrics'}" @click="tabMode = (tabMode == 'lyrics') ? '' : 'lyrics'"></div>
<div class="queue" :class="{active: tabMode == 'queue'}" @click="tabMode = (tabMode == 'queue') ? '' :'queue'"></div>
</div> -->
</div>
</script>
<script>
Vue.component('mini-view', {
template: '#mini-view',
props: {
time: {
type: Number,
required: false
},
lyrics: {
type: Array,
required: false
},
richlyrics: {
type: Array,
required: false
},
image: {
type: String,
required: false
},
},
data: function () {
return {
app: this.$root,
tabMode: "",
}
},
beforeMount() {
window.addEventListener('keyup', this.onEscapeKeyUp);
},
beforeDestroy() {
window.removeEventListener('keyup', this.onEscapeKeyUp)
},
mounted() {
app.pinMiniPlayer(true)
},
methods: {
onEscapeKeyUp(event) {
if (event.which === 27) {
app.miniPlayer(false);
console.log('js')
}
},
Vue.component('mini-view', {
template: '#mini-view',
props: {
time: {
type: Number,
required: false
},
lyrics: {
type: Array,
required: false
},
richlyrics: {
type: Array,
required: false
},
image: {
type: String,
required: false
},
},
data: function() {
return {
app: this.$root,
tabMode: "",
}
},
beforeMount() {
window.addEventListener('keyup', this.onEscapeKeyUp);
},
beforeDestroy() {
window.removeEventListener('keyup', this.onEscapeKeyUp)
},
mounted() {
app.pinMiniPlayer(true)
},
methods: {
onEscapeKeyUp(event) {
if (event.which === 27) {
app.miniPlayer(false);
console.log('js')
}
});
</script>
},
}
});
</script>

View file

@ -1,6 +1,6 @@
<script type="text/x-template" id="moreinfo-modal">
<div class="modal-fullscreen spatialproperties-panel moreinfo-modal" @click.self="if(timedelay) close()">
<div class="modal-window" >
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{data.title}}</div>
<div class="modal-subtitle modal-title">{{data.subtitle ?? ""}}</div>
@ -15,28 +15,28 @@
</script>
<script>
Vue.component('moreinfo-modal', {
template: '#moreinfo-modal',
data: function () {
return {
app: this.$root,
timedelay: false,
}
},
props: ["data"],
mounted() {
let self = this;
this.$nextTick(()=>{
setTimeout(function(){
self.timedelay = true
}, 1000);
})
Vue.component('moreinfo-modal', {
template: '#moreinfo-modal',
data: function() {
return {
app: this.$root,
timedelay: false,
}
},
props: ["data"],
mounted() {
let self = this;
this.$nextTick(() => {
setTimeout(function() {
self.timedelay = true
}, 1000);
})
},
methods: {
close() {
app.modals.moreInfo = false;
},
}
});
</script>
},
methods: {
close() {
app.modals.moreInfo = false;
},
}
});
</script>

View file

@ -1,37 +1,38 @@
<script type="text/x-template" id="pagination">
<div class="pagination-container" v-if="!isInfinite">
<button
class="md-btn page-btn"
:disabled="effectivePage === 1"
@click="goToPage(1)"
class="md-btn page-btn"
:disabled="effectivePage === 1"
@click="goToPage(1)"
>
<img class="md-ico-first"/>
<img class="md-ico-first" />
</button>
<button
class="md-btn page-btn prev"
:disabled="effectivePage === 1"
@click="goToPrevious()"
class="md-btn page-btn prev"
:disabled="effectivePage === 1"
@click="goToPrevious()"
>
<img class="md-ico-prev"/>
<img class="md-ico-prev" />
</button>
<button
:class="`md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
@click="goToPage(page)"
v-for="page in pagesToShow"
>{{ page }}</button>
<button
class="md-btn page-btn next"
:disabled="effectivePage === numPages"
@click="goToNext()"
>
<img class="md-ico-next"/>
:class="`md-btn page-btn${ isCurrentPage(page) ? ' md-btn-primary': ''}`"
@click="goToPage(page)"
v-for="page in pagesToShow"
>{{ page }}
</button>
<button
class="md-btn page-btn last"
:disabled="effectivePage === numPages"
@click="goToEnd()"
class="md-btn page-btn next"
:disabled="effectivePage === numPages"
@click="goToNext()"
>
<img class="md-ico-last"/>
<img class="md-ico-next" />
</button>
<button
class="md-btn page-btn last"
:disabled="effectivePage === numPages"
@click="goToEnd()"
>
<img class="md-ico-last" />
</button>
<div class="page-btn md-input-number">
<input type="number" min="1" :max="numPages" :value="effectivePage" @change="changePage" />
@ -41,135 +42,135 @@
</script>
<script>
Vue.component('pagination', {
template: "#pagination",
props: {
'length': { type: Number, required: true },
'pageSize': { type: Number, required: true },
'scroll': { type: String, required: true },
'scrollSelector': { type: String, required: true }
},
data: function () {
return { currentPage: 1 }
},
mounted() {
document.querySelector(this.scrollSelector)
.addEventListener("scroll", this.handleScroll)
},
destroyed() {
document.querySelector(this.scrollSelector)
.removeEventListener("scroll", this.handleScroll)
},
watch: {
'length': function () {
if (this.isInfinite) {
// If a search reduces the number of things to show, we want to limit
// the number of songs shown as well. This is to prevent you scrolling
// to load your entire library, searching for one song, and then having
// th re-render the entire library
if (this.currentPage > this.numPages) {
this.currentPage = this.numPages;
this.$emit("onRangeChange", this.currentRange);
}
} else {
this.$emit("onRangeChange", this.currentRange);
}
},
'scroll': function () {
// When changing modes, set the page to 1. This is primarily to
// prevent going to a high page (e.g., 50) and then switching to infinite
// and showing 12.5k songs
this.currentPage = 1;
this.$emit("onRangeChange", this.currentRange);
}
},
computed: {
isInfinite: function () {
return this.scroll === "infinite"
},
currentRange: function () {
if (this.isInfinite) {
return [0, this.currentPage * this.pageSize];
} else {
const startingPage = Math.min(this.numPages, this.currentPage);
return [
(startingPage - 1) * this.pageSize,
startingPage * this.pageSize
];
}
},
effectivePage: function () {
return Math.min(this.currentPage, this.numPages)
},
numPages: function () {
return Math.ceil(this.length / this.pageSize) || 1;
},
pagesToShow: function () {
let start = this.currentPage - 2;
let end = this.currentPage + 2;
if (start < 1) {
end += (1 - start);
start = 1;
}
const endDifference = end - this.numPages;
if (endDifference > 0) {
end = this.numPages;
start = Math.max(1, start - endDifference);
}
const array = [];
for (let idx = start; idx <= end; idx++) {
array.push(idx);
}
return array;
}
},
methods: {
// Infinite Scrolling
handleScroll: function (event) {
if (this.isInfinite &&
this.currentPage < this.numPages &&
event.target.scrollTop >= event.target.scrollHeight - event.target.clientHeight) {
this.currentPage += 1;
this.$emit("onRangeChange", this.currentRange);
}
},
// Pagination
isCurrentPage: function (idx) {
return idx === this.currentPage ||
(idx === this.numPages && this.currentPage > this.numPages);
},
changePage: function (event) {
const value = event.target.valueAsNumber;
if (!isNaN(value) && value >= 1 && value <= this.numPages) {
this.currentPage = value;
this.$emit("onRangeChange", this.currentRange);
}
},
goToPage: function (page) {
this.currentPage = page;
this.$emit("onRangeChange", this.currentRange);
},
goToPrevious: function () {
if (this.currentPage > 1) {
this.currentPage -= 1;
this.$emit("onRangeChange", this.currentRange);
}
},
goToNext: function () {
if (this.currentPage < this.numPages) {
this.currentPage += 1;
this.$emit("onRangeChange", this.currentRange);
}
},
goToEnd: function () {
this.currentPage = this.numPages;
this.$emit("onRangeChange", this.currentRange);
}
Vue.component('pagination', {
template: "#pagination",
props: {
'length': { type: Number, required: true },
'pageSize': { type: Number, required: true },
'scroll': { type: String, required: true },
'scrollSelector': { type: String, required: true }
},
data: function() {
return { currentPage: 1 }
},
mounted() {
document.querySelector(this.scrollSelector)
.addEventListener("scroll", this.handleScroll)
},
destroyed() {
document.querySelector(this.scrollSelector)
.removeEventListener("scroll", this.handleScroll)
},
watch: {
'length': function() {
if (this.isInfinite) {
// If a search reduces the number of things to show, we want to limit
// the number of songs shown as well. This is to prevent you scrolling
// to load your entire library, searching for one song, and then having
// th re-render the entire library
if (this.currentPage > this.numPages) {
this.currentPage = this.numPages;
this.$emit("onRangeChange", this.currentRange);
}
} else {
this.$emit("onRangeChange", this.currentRange);
}
})
</script>
},
'scroll': function() {
// When changing modes, set the page to 1. This is primarily to
// prevent going to a high page (e.g., 50) and then switching to infinite
// and showing 12.5k songs
this.currentPage = 1;
this.$emit("onRangeChange", this.currentRange);
}
},
computed: {
isInfinite: function() {
return this.scroll === "infinite"
},
currentRange: function() {
if (this.isInfinite) {
return [0, this.currentPage * this.pageSize];
} else {
const startingPage = Math.min(this.numPages, this.currentPage);
return [
(startingPage - 1) * this.pageSize,
startingPage * this.pageSize
];
}
},
effectivePage: function() {
return Math.min(this.currentPage, this.numPages)
},
numPages: function() {
return Math.ceil(this.length / this.pageSize) || 1;
},
pagesToShow: function() {
let start = this.currentPage - 2;
let end = this.currentPage + 2;
if (start < 1) {
end += (1 - start);
start = 1;
}
const endDifference = end - this.numPages;
if (endDifference > 0) {
end = this.numPages;
start = Math.max(1, start - endDifference);
}
const array = [];
for (let idx = start; idx <= end; idx++) {
array.push(idx);
}
return array;
}
},
methods: {
// Infinite Scrolling
handleScroll: function(event) {
if (this.isInfinite &&
this.currentPage < this.numPages &&
event.target.scrollTop >= event.target.scrollHeight - event.target.clientHeight) {
this.currentPage += 1;
this.$emit("onRangeChange", this.currentRange);
}
},
// Pagination
isCurrentPage: function(idx) {
return idx === this.currentPage ||
(idx === this.numPages && this.currentPage > this.numPages);
},
changePage: function(event) {
const value = event.target.valueAsNumber;
if (!isNaN(value) && value >= 1 && value <= this.numPages) {
this.currentPage = value;
this.$emit("onRangeChange", this.currentRange);
}
},
goToPage: function(page) {
this.currentPage = page;
this.$emit("onRangeChange", this.currentRange);
},
goToPrevious: function() {
if (this.currentPage > 1) {
this.currentPage -= 1;
this.$emit("onRangeChange", this.currentRange);
}
},
goToNext: function() {
if (this.currentPage < this.numPages) {
this.currentPage += 1;
this.$emit("onRangeChange", this.currentRange);
}
},
goToEnd: function() {
this.currentPage = this.numPages;
this.$emit("onRangeChange", this.currentRange);
}
}
})
</script>

View file

@ -1,65 +1,66 @@
<script type="text/x-template" id="pathmenu">
<div class="spatialproperties-panel castmenu pathmenu modal-fullscreen" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{'Edit Paths'}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
</div>
<div class="modal-content">
<template v-for="folder of folders">
<div class="md-option-line">
<div class="md-option-segment">
{{folder}}
<div class="spatialproperties-panel castmenu pathmenu modal-fullscreen" @click.self="close()"
@contextmenu.self="close()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{'Edit Paths'}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
</div>
<div class="modal-content">
<template v-for="folder of folders">
<div class="md-option-line">
<div class="md-option-segment">
{{folder}}
</div>
<div class="md-option-segment md-option-segment_auto">
<button class="md-btn" @click="remove(folder)">
{{'Remove'}}
</button>
</div>
</div>
</template>
<div class="md-option-line">
<div class="md-option-segment md-option-segment_auto">
<button class="md-btn" @click="remove(folder)">
{{'Remove'}}
<button class="md-btn" @click="add()">
{{'Add Path'}}
</button>
</div>
</div>
</template>
<div class="md-option-line">
<div class="md-option-segment md-option-segment_auto">
<button class="md-btn" @click="add()">
{{'Add Path'}}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('pathmenu', {
template: '#pathmenu',
data: function () {
return {
folders: [],
}
},
mounted() {
this.folders = this.$root.cfg.libraryPrefs.localPaths;
},
watch:{},
methods: {
close() {
this.$root.modals.pathMenu = false
},
async add(){
const result = await ipcRenderer.invoke('folderSelector')
for (i of result){
if (this.folders.findIndex(x => x.startsWith(i)) == -1){
this.folders.push(i)
}
}
this.$root.cfg.libraryPrefs.localPaths = this.folders;
ipcRenderer.invoke("scanLibrary")
},
remove(dir){
this.folders = this.folders.filter(item => item !== dir)
this.$root.cfg.libraryPrefs.localPaths = this.folders;
ipcRenderer.invoke("scanLibrary")
}
Vue.component('pathmenu', {
template: '#pathmenu',
data: function() {
return {
folders: [],
}
},
mounted() {
this.folders = this.$root.cfg.libraryPrefs.localPaths;
},
watch: {},
methods: {
close() {
this.$root.modals.pathMenu = false
},
async add() {
const result = await ipcRenderer.invoke('folderSelector')
for (i of result) {
if (this.folders.findIndex(x => x.startsWith(i)) == -1) {
this.folders.push(i)
}
}
});
</script>
this.$root.cfg.libraryPrefs.localPaths = this.folders;
ipcRenderer.invoke("scanLibrary")
},
remove(dir) {
this.folders = this.folders.filter(item => item !== dir)
this.$root.cfg.libraryPrefs.localPaths = this.folders;
ipcRenderer.invoke("scanLibrary")
}
}
});
</script>

View file

@ -1,5 +1,6 @@
<script type="text/x-template" id="plugin-menu">
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()" @contextmenu.self="app.resetState()">
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()"
@contextmenu.self="app.resetState()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{$root.getLz('term.pluginMenu')}}</div>
@ -10,30 +11,32 @@
<span class="icon"><%- include("../svg/x.svg") %></span>
<span class="name" style="top: 0.5px;">{{$root.getLz('term.pluginMenu.none')}}</span>
</span>
<button class="playlist-item" @click="entry.onClick(); closeMenu();" v-for="entry in app.pluginMenuEntries">
<span class="icon"><%- include("../svg/grid.svg") %></span>
<span class="name" style="top: 0.5px;">{{ entry.name }}</span>
</button>
<button class="playlist-item" @click="entry.onClick(); closeMenu();"
v-for="entry in app.pluginMenuEntries">
<span class="icon"><%- include("../svg/grid.svg") %></span>
<span class="name" style="top: 0.5px;">{{ entry.name }}</span>
</button>
</div>
</div>
</div>
</script>
<script>
Vue.component('plugin-menu', {
template: '#plugin-menu',
data: function () {
return {
app: this.$root,
}
},
props: {},
mounted() {},
methods: {
closeMenu() {
app.modals.pluginMenu = false
},
},
Vue.component('plugin-menu', {
template: '#plugin-menu',
data: function() {
return {
app: this.$root,
}
);
</script>
},
props: {},
mounted() {
},
methods: {
closeMenu() {
app.modals.pluginMenu = false
},
},
}
);
</script>

View file

@ -1,34 +1,34 @@
<script type="text/x-template" id="qrcode-modal">
<div class="modal-fullscreen spatialproperties-panel">
<div class="modal-window" >
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{`Web Remote QR : ` + url }}</div>
<button class="close-btn" @click="close()" :aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<img class="qrimg" :src="src"/>
<img class="qrimg" :src="src" />
</div>
</div>
</div>
</script>
<script>
Vue.component('qrcode-modal', {
template: '#qrcode-modal',
data: function () {
return {
app: this.$root,
Vue.component('qrcode-modal', {
template: '#qrcode-modal',
data: function() {
return {
app: this.$root,
}
},
props: ["src","url"],
mounted() {
}
},
props: ["src", "url"],
mounted() {
},
methods: {
close() {
app.resetState()
},
}
});
</script>
},
methods: {
close() {
app.resetState()
},
}
});
</script>

View file

@ -14,7 +14,8 @@
</div>
</div>
<div class="queue-body" v-if="page == 'history'">
<mediaitem-list-item :show-library-status="false" v-for="item in history" :item="item"></mediaitem-list-item>
<mediaitem-list-item :show-library-status="false" v-for="item in history"
:item="item"></mediaitem-list-item>
</div>
<div class="queue-body" v-if="page == 'queue'">
<draggable v-model="queueItems" @start="drag=true" @end="drag=false;move()">
@ -27,18 +28,26 @@
<div class="row">
<div class="col-auto cider-flex-center">
<div class="artwork">
<mediaitem-artwork :url="queueItem.item.attributes.artwork ? queueItem.item.attributes.artwork.url : ''" :size="32"></mediaitem-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.artistName }} — {{ queueItem.item.attributes.albumName }}</div>
<div class="queue-title text-overflow-elipsis">{{ queueItem.item.attributes.name }}
</div>
<div class="queue-subtitle text-overflow-elipsis">{{
queueItem.item.attributes.artistName }} — {{ queueItem.item.attributes.albumName }}
</div>
</div>
<div class="queue-explicit-icon cider-flex-center" v-if="queueItem.item.attributes.contentRating == 'explicit'">
<div class="queue-explicit-icon cider-flex-center"
v-if="queueItem.item.attributes.contentRating == 'explicit'">
<div class="explicit-icon"></div>
</div>
<div class="col queue-duration-info">
<div class="queue-duration cider-flex-center">{{convertTimeToString(queueItem.item.attributes.durationInMillis)}}</div>
<div class="queue-duration cider-flex-center">
{{convertTimeToString(queueItem.item.attributes.durationInMillis)}}
</div>
</div>
</div>
</div>
@ -47,138 +56,144 @@
</div>
<div class="queue-footer">
<div class="btn-group" style="width:100%;">
<button class="md-btn md-btn-small" :class="{'md-btn-primary': (page == 'queue')}" @click="page = 'queue'">{{app.getLz('term.queue')}}</button>
<button class="md-btn md-btn-small" :class="{'md-btn-primary': (page == 'history')}" @click="getHistory();page = 'history'">{{app.getLz('term.history')}}</button>
<button class="md-btn md-btn-small" :class="{'md-btn-primary': (page == 'queue')}"
@click="page = 'queue'">{{app.getLz('term.queue')}}
</button>
<button class="md-btn md-btn-small" :class="{'md-btn-primary': (page == 'history')}"
@click="getHistory();page = 'history'">{{app.getLz('term.history')}}
</button>
</div>
<button class="md-btn md-btn-small" style="width:100%;margin-top:6px;" v-if="queueItems.length > 1" @click="app.mk.clearQueue();updateQueue()">{{app.getLz('term.clearAll')}}</button>
<button class="md-btn md-btn-small" style="width:100%;margin-top:6px;" v-if="queueItems.length > 1"
@click="app.mk.clearQueue();updateQueue()">{{app.getLz('term.clearAll')}}
</button>
</div>
</div>
</script>
<script>
Vue.component('cider-queue', {
template: '#cider-queue',
data: function () {
return {
drag: false,
queuePosition: 0,
queueItems: [],
selected: -1,
selectedItems: [],
history: [],
page: "queue",
app: this.$root
}
},
computed: {
displayQueueItems() {
const displayLimit = 50;
const lastDisplayPosition = Math.min(displayLimit + this.queuePosition, this.queueItems.length);
return this.queueItems.slice(this.queuePosition, lastDisplayPosition);
}
},
mounted() {
this.updateQueue()
},
methods: {
async getHistory() {
let history = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, { l : this.$root.mklang})
this.history = history.data.data
},
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": app.getLz('action.removeFromQueue'),
"action": function () {
self.queueItems.splice(position, 1)
app.mk.queue._queueItems = self.queueItems;
app.mk.queue._reindex()
}
},
{
"name": app.getLz('action.startRadio'),
"action": function () {
app.mk.setStationQueue({
song: item.attributes.playParams.id ?? item.id
}).then(() => {
app.mk.play()
})
}
},
{
"name": app.getLz('action.goToArtist'),
"action": function () {
app.searchAndNavigate(item,'artist')
}
},
{
"name": app.getLz('action.goToAlbum'),
"action": function () {
app.searchAndNavigate(item,'album')
}
},
]
},
multiple: {
items: [{
"name": app.getLz('action.removeTracks').replace('${self.selectedItems.length}', self.selectedItems.length.toString()),
"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 = []
}
}]
}
}
app.showMenuPanel(menus[useMenu], event);
},
playQueueItem(id) {
app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(id))
},
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()
},
convertTimeToString(timeInMilliseconds) {
var seconds = ((timeInMilliseconds % 60000) / 1000).toFixed(0);
return Math.floor(timeInMilliseconds/60000) + ":" + (seconds < 10 ? '0' : '') + seconds;
}
Vue.component('cider-queue', {
template: '#cider-queue',
data: function() {
return {
drag: false,
queuePosition: 0,
queueItems: [],
selected: -1,
selectedItems: [],
history: [],
page: "queue",
app: this.$root
}
},
computed: {
displayQueueItems() {
const displayLimit = 50;
const lastDisplayPosition = Math.min(displayLimit + this.queuePosition, this.queueItems.length);
return this.queueItems.slice(this.queuePosition, lastDisplayPosition);
}
},
mounted() {
this.updateQueue()
},
methods: {
async getHistory() {
let history = await app.mk.api.v3.music(`/v1/me/recent/played/tracks`, { l: this.$root.mklang })
this.history = history.data.data
},
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]
}
});
</script>
},
queueContext(event, item, position) {
let self = this
let useMenu = "single"
if (this.selectedItems.length > 1) {
useMenu = "multiple"
}
let menus = {
single: {
items: [{
"name": app.getLz('action.removeFromQueue'),
"action": function() {
self.queueItems.splice(position, 1)
app.mk.queue._queueItems = self.queueItems;
app.mk.queue._reindex()
}
},
{
"name": app.getLz('action.startRadio'),
"action": function() {
app.mk.setStationQueue({
song: item.attributes.playParams.id ?? item.id
}).then(() => {
app.mk.play()
})
}
},
{
"name": app.getLz('action.goToArtist'),
"action": function() {
app.searchAndNavigate(item, 'artist')
}
},
{
"name": app.getLz('action.goToAlbum'),
"action": function() {
app.searchAndNavigate(item, 'album')
}
},
]
},
multiple: {
items: [{
"name": app.getLz('action.removeTracks').replace('${self.selectedItems.length}', self.selectedItems.length.toString()),
"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 = []
}
}]
}
}
app.showMenuPanel(menus[useMenu], event);
},
playQueueItem(id) {
app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(id))
},
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()
},
convertTimeToString(timeInMilliseconds) {
var seconds = ((timeInMilliseconds % 60000) / 1000).toFixed(0);
return Math.floor(timeInMilliseconds / 60000) + ":" + (seconds < 10 ? '0' : '') + seconds;
}
}
});
</script>

View file

@ -1,8 +1,8 @@
<script type="text/x-template" id="keybinds-settings">
<div class="keybinds-page">
<div class="md-option-header">
<span>{{$root.getLz('settings.option.general.keybindings')}}</span>
</div>
<div class="md-option-header">
<span>{{$root.getLz('settings.option.general.keybindings')}}</span>
</div>
<div class="settings-option-body">
<div class="md-option-header-sub">
<span>{{$root.getLz('settings.option.general.keybindings.library')}}</span>
@ -207,7 +207,7 @@
</div>
</div>
<button class="md-btn md-btn-large md-btn-block" @click="keyBindReset()">
{{$root.getLz('term.reset')}}
{{$root.getLz('term.reset')}}
</button>
</div>
@ -215,109 +215,109 @@
</script>
<script>
Vue.component('keybinds-settings', {
template: "#keybinds-settings",
props: [],
data: function () {
return {
app: this.$root
}
},
methods: {
keyBindUpdate: function (action) {
const blur = document.createElement('div');
blur.className = 'blur';
blur.style.backgroundColor = 'rgba(0,0,0,0.25)';
blur.style.position = 'fixed';
blur.style.top = '0';
blur.style.left = '0';
blur.style.width = '100%';
blur.style.height = '100%';
blur.style.zIndex = '9999';
blur.style.display = 'flex';
blur.style.alignItems = 'center';
blur.style.justifyContent = 'center';
blur.style.fontSize = '2em';
blur.style.color = 'white';
blur.innerHTML = `<center>${app.getLz('settings.option.general.keybindings.pressCombination')}<br />${app.getLz('settings.option.general.keybindings.pressEscape')}</center>`
document.body.appendChild(blur);
Vue.component('keybinds-settings', {
template: "#keybinds-settings",
props: [],
data: function() {
return {
app: this.$root
}
},
methods: {
keyBindUpdate: function(action) {
const blur = document.createElement('div');
blur.className = 'blur';
blur.style.backgroundColor = 'rgba(0,0,0,0.25)';
blur.style.position = 'fixed';
blur.style.top = '0';
blur.style.left = '0';
blur.style.width = '100%';
blur.style.height = '100%';
blur.style.zIndex = '9999';
blur.style.display = 'flex';
blur.style.alignItems = 'center';
blur.style.justifyContent = 'center';
blur.style.fontSize = '2em';
blur.style.color = 'white';
blur.innerHTML = `<center>${app.getLz('settings.option.general.keybindings.pressCombination')}<br />${app.getLz('settings.option.general.keybindings.pressEscape')}</center>`
document.body.appendChild(blur);
let keyBind = [];
const keyBindTimeout = setTimeout(function () {
keyBind = [];
document.body.removeChild(blur);
}, 30000);
const keyBindUpdate = function (e) {
if (document.body.contains(blur)) {
if (e.key == 'Escape') {
document.body.removeChild(blur);
clearTimeout(keyBindTimeout);
return;
} else {
if (e.keyCode >= 65 && e.keyCode <= 90 && e.keyCode <= 97 && e.keyCode <= 122) {
keyBind.push(e.key.toUpperCase());
} else {
keyBind.push(e.key);
}
if (keyBind.length === 2) {
if (keyBind[0] !== keyBind[1]) {
app.cfg.general.keybindings[action] = keyBind
document.body.removeChild(blur);
clearTimeout(keyBindTimeout);
notyf.success(app.getLz('settings.notyf.general.keybindings.update.success'));
app.confirm(app.getLz("settings.prompt.general.keybindings.update.success"), (ok) => {
if (ok) ipcRenderer.invoke("relaunchApp")
})
} else {
keyBind = [];
}
}
}
}
};
document.addEventListener('keydown', keyBindUpdate);
},
keyBindReset: function () {
app.cfg.general.keybindings.search = [app.platform == "darwin" ? "Command" : "Control", "F"];
app.cfg.general.keybindings.listnow = [app.platform == "darwin" ? "Command" : "Control", "L"];
app.cfg.general.keybindings.browse = [app.platform == "darwin" ? "Command" : "Control", "B"];
app.cfg.general.keybindings.recentAdd = [app.platform == "darwin" ? "Command" : "Control", "G"];
app.cfg.general.keybindings.songs = [app.platform == "darwin" ? "Command" : "Control", "J"];
app.cfg.general.keybindings.albums = [app.platform == "darwin" ? "Command" : "Control", "A"];
app.cfg.general.keybindings.artists = [app.platform == "darwin" ? "Command" : "Control", "D"];
app.cfg.general.keybindings.togglePrivateSession = [app.platform == "darwin" ? "Command" : "Control", "P"];
app.cfg.general.keybindings.webRemote = [app.platform == "darwin" ? "Command" : "Control",app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "W"];
app.cfg.general.keybindings.audioSettings = [app.platform == "darwin" ? "Command" : "Control",app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "A"];
app.cfg.general.keybindings.pluginMenu = [app.platform == "darwin" ? "Command" : "Control",app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "P"];
app.cfg.general.keybindings.castToDevices = [app.platform == "darwin" ? "Command" : "Control",app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "C"];
app.cfg.general.keybindings.settings = [app.platform == "darwin" ? "Command" : "Control", ","];
app.cfg.general.keybindings.zoomn = [app.platform == "darwin" ? "Command" : "Control", "numadd"];
app.cfg.general.keybindings.zoomt = [app.platform == "darwin" ? "Command" : "Control", "numsub"];
app.cfg.general.keybindings.zoomrst = [app.platform == "darwin" ? "Command" : "Control", "num0"];
app.cfg.general.keybindings.openDeveloperTools = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : "Shift", "I"];
notyf.success(app.getLz('settings.notyf.general.keybindings.update.success'));
app.confirm(app.getLz("settings.prompt.general.keybindings.update.success"), (ok) => {
let keyBind = [];
const keyBindTimeout = setTimeout(function() {
keyBind = [];
document.body.removeChild(blur);
}, 30000);
const keyBindUpdate = function(e) {
if (document.body.contains(blur)) {
if (e.key == 'Escape') {
document.body.removeChild(blur);
clearTimeout(keyBindTimeout);
return;
} else {
if (e.keyCode >= 65 && e.keyCode <= 90 && e.keyCode <= 97 && e.keyCode <= 122) {
keyBind.push(e.key.toUpperCase());
} else {
keyBind.push(e.key);
}
if (keyBind.length === 2) {
if (keyBind[0] !== keyBind[1]) {
app.cfg.general.keybindings[action] = keyBind
document.body.removeChild(blur);
clearTimeout(keyBindTimeout);
notyf.success(app.getLz('settings.notyf.general.keybindings.update.success'));
app.confirm(app.getLz("settings.prompt.general.keybindings.update.success"), (ok) => {
if (ok) ipcRenderer.invoke("relaunchApp")
})
},
getLanguages: function () {
let langs = this.$root.lzListing
let categories = {
"main": [],
"fun": [],
"unsorted": []
})
} else {
keyBind = [];
}
// sort by category if category is undefined or empty put it in "unsorted"
for (let i = 0; i < langs.length; i++) {
if (langs[i].category === undefined || langs[i].category === "") {
categories.unsorted.push(langs[i])
} else {
categories[langs[i].category].push(langs[i])
}
}
// return
return categories
},
}
}
}
};
document.addEventListener('keydown', keyBindUpdate);
},
keyBindReset: function() {
app.cfg.general.keybindings.search = [app.platform == "darwin" ? "Command" : "Control", "F"];
app.cfg.general.keybindings.listnow = [app.platform == "darwin" ? "Command" : "Control", "L"];
app.cfg.general.keybindings.browse = [app.platform == "darwin" ? "Command" : "Control", "B"];
app.cfg.general.keybindings.recentAdd = [app.platform == "darwin" ? "Command" : "Control", "G"];
app.cfg.general.keybindings.songs = [app.platform == "darwin" ? "Command" : "Control", "J"];
app.cfg.general.keybindings.albums = [app.platform == "darwin" ? "Command" : "Control", "A"];
app.cfg.general.keybindings.artists = [app.platform == "darwin" ? "Command" : "Control", "D"];
app.cfg.general.keybindings.togglePrivateSession = [app.platform == "darwin" ? "Command" : "Control", "P"];
app.cfg.general.keybindings.webRemote = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "W"];
app.cfg.general.keybindings.audioSettings = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "A"];
app.cfg.general.keybindings.pluginMenu = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "P"];
app.cfg.general.keybindings.castToDevices = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : (app.platform == "linux" ? "Shift" : "Alt"), "C"];
app.cfg.general.keybindings.settings = [app.platform == "darwin" ? "Command" : "Control", ","];
app.cfg.general.keybindings.zoomn = [app.platform == "darwin" ? "Command" : "Control", "numadd"];
app.cfg.general.keybindings.zoomt = [app.platform == "darwin" ? "Command" : "Control", "numsub"];
app.cfg.general.keybindings.zoomrst = [app.platform == "darwin" ? "Command" : "Control", "num0"];
app.cfg.general.keybindings.openDeveloperTools = [app.platform == "darwin" ? "Command" : "Control", app.platform == "darwin" ? "Option" : "Shift", "I"];
notyf.success(app.getLz('settings.notyf.general.keybindings.update.success'));
app.confirm(app.getLz("settings.prompt.general.keybindings.update.success"), (ok) => {
if (ok) ipcRenderer.invoke("relaunchApp")
})
},
getLanguages: function() {
let langs = this.$root.lzListing
let categories = {
"main": [],
"fun": [],
"unsorted": []
}
})
</script>
// sort by category if category is undefined or empty put it in "unsorted"
for (let i = 0; i < langs.length; i++) {
if (langs[i].category === undefined || langs[i].category === "") {
categories.unsorted.push(langs[i])
} else {
categories[langs[i].category].push(langs[i])
}
}
// return
return categories
},
}
})
</script>

View file

@ -21,7 +21,8 @@
<div class="row">
<div class="col cider-flex-center">
<div>
<h4 class="repo-name">{{ (repo.description != null) ? repo.description : repo.full_name }}</h4>
<h4 class="repo-name">{{ (repo.description != null) ? repo.description :
repo.full_name }}</h4>
<div>⭐ {{ repo.stargazers_count }}</div>
</div>
</div>
@ -37,7 +38,8 @@
<div>
<h3 class="repo-preview-name">{{ openRepo.description }}</h3>
<div>
<div class="svg-icon inline" :style="{'--url': 'url(\'./assets/github.svg\')'}"></div>
<div class="svg-icon inline"
:style="{'--url': 'url(\'./assets/github.svg\')'}"></div>
<a class="repo-url" target="_blank" :href="openRepo.html_url">{{ openRepo.full_name
}}</a></div>
<div>⭐ {{ openRepo.stargazers_count }}</div>
@ -62,127 +64,127 @@
</div>
</script>
<script>
Vue.component('plugins-github', {
template: "#plugins-github",
props: [],
data: function () {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
themesInstalled: []
}
Vue.component('plugins-github', {
template: "#plugins-github",
props: [],
data: function() {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
mounted() {
this.getRepos();
// this.getInstalledThemes();
},
methods: {
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "") {
self.themesInstalled.push(theme.github_repo)
}
})
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
themesInstalled: []
}
},
mounted() {
this.getRepos();
// this.getInstalledThemes();
},
methods: {
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "") {
self.themesInstalled.push(theme.github_repo)
}
})
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.plugin.github.install.confirm'), {
repo: repo.full_name
});
app.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("plugin-installed", (event, arg) => {
if (arg.success) {
self.themes = []
notyf.success(app.getLz('settings.notyf.visual.plugin.install.success'));
app.confirm(app.getLz("settings.prompt.visual.plugin.github.success"), (ok)=>{
if (ok) {
ipcRenderer.invoke("relaunchApp")
} else {
return
}
})
} else {
notyf.error(app.getLz('settings.notyf.visual.plugin.install.error'));
}
});
ipcRenderer.invoke("get-github-plugin", repo.html_url)
}
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.plugin.github.install.confirm'), {
repo: repo.full_name
});
app.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("plugin-installed", (event, arg) => {
if (arg.success) {
self.themes = []
notyf.success(app.getLz('settings.notyf.visual.plugin.install.success'));
app.confirm(app.getLz("settings.prompt.visual.plugin.github.success"), (ok) => {
if (ok) {
ipcRenderer.invoke("relaunchApp")
} else {
return
}
})
},
installThemeURL() {
let self = this
app.prompt(app.getLz('settings.prompt.visual.plugin.github.URL'), (result) => {
if (result) {
ipcRenderer.once("plugin-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
app.confirm(app.getLz("settings.prompt.visual.plugin.github.success"), (ok)=>{
if (ok) {
ipcRenderer.invoke("relaunchApp")
} else {
return
}
})
notyf.success(app.getLz('settings.notyf.visual.plugin.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.plugin.install.error'));
}
});
ipcRenderer.invoke("get-github-plugin", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
} else {
notyf.error(app.getLz('settings.notyf.visual.plugin.install.error'));
}
});
ipcRenderer.invoke("get-github-plugin", repo.html_url)
}
})
},
installThemeURL() {
let self = this
app.prompt(app.getLz('settings.prompt.visual.plugin.github.URL'), (result) => {
if (result) {
ipcRenderer.once("plugin-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
app.confirm(app.getLz("settings.prompt.visual.plugin.github.success"), (ok) => {
if (ok) {
ipcRenderer.invoke("relaunchApp")
} else {
return
}
})
notyf.success(app.getLz('settings.notyf.visual.plugin.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.plugin.install.error'));
}
});
ipcRenderer.invoke("get-github-plugin", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch("https://api.github.com/search/repositories?q=topic:cidermusicplugin fork:true&per_page=100", requestOptions)
.then(response => response.text())
.then(result => {
self.repos = JSON.parse(result).items
})
.catch(error => console.log('error', error));
}
}
})
fetch("https://api.github.com/search/repositories?q=topic:cidermusicplugin fork:true&per_page=100", requestOptions)
.then(response => response.text())
.then(result => {
self.repos = JSON.parse(result).items
})
.catch(error => console.log('error', error));
}
}
})
</script>

View file

@ -78,120 +78,120 @@
</div>
</script>
<script>
Vue.component('themes-github', {
template: "#themes-github",
props: [],
data: function () {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
themesInstalled: [],
themes: []
}
Vue.component('themes-github', {
template: "#themes-github",
props: [],
data: function() {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
mounted() {
this.themes = ipcRenderer.sendSync("get-themes")
this.getRepos();
this.getInstalledThemes();
},
methods: {
openThemesFolder() {
ipcRenderer.invoke("open-path", "themes")
},
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "" && typeof theme.commit != "") {
self.themesInstalled.push(theme.github_repo.toLowerCase())
}
})
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
themesInstalled: [],
themes: []
}
},
mounted() {
this.themes = ipcRenderer.sendSync("get-themes")
this.getRepos();
this.getInstalledThemes();
},
methods: {
openThemesFolder() {
ipcRenderer.invoke("open-path", "themes")
},
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "" && typeof theme.commit != "") {
self.themesInstalled.push(theme.github_repo.toLowerCase())
}
})
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.theme.github.install.confirm'), {
repo: repo.full_name
});
app.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
self.getInstalledThemes()
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", repo.html_url)
}
})
},
installThemeURL() {
let self = this
app.prompt(app.getLz('settings.prompt.visual.theme.github.URL'), (result) => {
if (result) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.theme.github.install.confirm'), {
repo: repo.full_name
});
app.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
self.getInstalledThemes()
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", repo.html_url)
}
})
},
installThemeURL() {
let self = this
app.prompt(app.getLz('settings.prompt.visual.theme.github.URL'), (result) => {
if (result) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch("https://api.github.com/search/repositories?q=topic:cidermusictheme fork:true&per_page=100", requestOptions)
.then(response => response.text())
.then(result => {
let items = JSON.parse(result).items
self.repos = items
})
.catch(error => console.log('error', error));
}
}
})
</script>
fetch("https://api.github.com/search/repositories?q=topic:cidermusictheme fork:true&per_page=100", requestOptions)
.then(response => response.text())
.then(result => {
let items = JSON.parse(result).items
self.repos = items
})
.catch(error => console.log('error', error));
}
}
})
</script>

View file

@ -32,9 +32,9 @@
<ul class="list-group list-group-flush">
<template v-for="theme in themes">
<li @click="addStyle(theme.file)"
@contextmenu="contextMenu($event, theme)"
class="list-group-item list-group-item-dark"
:class="{'applied': $root.cfg.visual.styles.includes(theme.file)}">
@contextmenu="contextMenu($event, theme)"
class="list-group-item list-group-item-dark"
:class="{'applied': $root.cfg.visual.styles.includes(theme.file)}">
<b-row>
<b-col class="themeLabel">{{theme.name}}</b-col>
@ -51,33 +51,34 @@
<button class="themeContextMenu codicon codicon-package"></button>
</b-col>
<b-col sm="auto">
<button @click.stop="contextMenu($event, theme)" class="themeContextMenu codicon codicon-list-unordered"></button>
<button @click.stop="contextMenu($event, theme)"
class="themeContextMenu codicon codicon-list-unordered"></button>
</b-col>
</template>
</b-row>
</li>
<li @click="addStyle(packEntry.file)"
@contextmenu="contextMenu($event, theme)"
class="list-group-item list-group-item-dark addon"
v-for="packEntry in theme.pack"
:class="{'applied': $root.cfg.visual.styles.includes(packEntry.file)}"
v-if="theme.pack">
@contextmenu="contextMenu($event, theme)"
class="list-group-item list-group-item-dark addon"
v-for="packEntry in theme.pack"
:class="{'applied': $root.cfg.visual.styles.includes(packEntry.file)}"
v-if="theme.pack">
<b-row>
<b-col class="themeLabel">{{packEntry.name}}</b-col>
<template v-if="$root.cfg.visual.styles.includes(packEntry.file)">
<b-col sm="auto">
<button class="themeContextMenu codicon codicon-check"></button>
</b-col>
</template>
<template v-else>
<b-col sm="auto">
<button class="themeContextMenu codicon codicon-diff-added"></button>
</b-col>
</template>
</b-row>
</li>
<b-row>
<b-col class="themeLabel">{{packEntry.name}}</b-col>
<template v-if="$root.cfg.visual.styles.includes(packEntry.file)">
<b-col sm="auto">
<button class="themeContextMenu codicon codicon-check"></button>
</b-col>
</template>
<template v-else>
<b-col sm="auto">
<button class="themeContextMenu codicon codicon-diff-added"></button>
</b-col>
</template>
</b-row>
</li>
</template>
</ul>
</div>
@ -86,17 +87,17 @@
<div class="repo-header">
<h4>{{ $root.getLz("settings.option.visual.theme.github.applied") }} </h4>
</div>
<stylestack-editor ref="stackEditor" v-if="themes.length != 0" :themes="themes"/>
<stylestack-editor ref="stackEditor" v-if="themes.length != 0" :themes="themes" />
</div>
</div>
</div>
</script>
<script>
// do not translate
Vue.component('stylestack-editor', {
/*html*/
template: `
// do not translate
Vue.component('stylestack-editor', {
/*html*/
template: `
<div class="stylestack-editor" >
<draggable class="list-group" v-model="$root.cfg.visual.styles" @end="$root.reloadStyles()">
<b-list-group-item variant="dark" v-for="theme in $root.cfg.visual.styles" :key="theme">
@ -113,255 +114,255 @@
</draggable>
</div>
`,
props: {
themes: {
type: Array,
default: [],
required: true
}
},
data: function () {
return {
selected: null,
newTheme: null,
themeList: []
}
},
mounted() {
console.log(this.themes)
this.themeList = [...this.themes]
props: {
themes: {
type: Array,
default: [],
required: true
}
},
data: function() {
return {
selected: null,
newTheme: null,
themeList: []
}
},
mounted() {
console.log(this.themes)
this.themeList = [...this.themes]
this.themeList.forEach(theme => {
if (theme.pack) {
theme.pack.forEach(packEntry => {
packEntry.file = theme.file.replace('index.less', '') + packEntry.file
this.themeList.push(packEntry)
})
}
})
},
methods: {
gitHubExplore() {
this.$root.openSettingsPage("github-themes")
},
getThemeName(filename) {
try {
return this.themeList.find(theme => theme.file === filename).name;
} catch (e) {
return filename;
}
},
moveUp() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index > 0) {
styles.splice(index, 1)
styles.splice(index - 1, 0, this.selected)
}
this.$root.reloadStyles()
},
moveDown() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index < styles.length - 1) {
styles.splice(index, 1)
styles.splice(index + 1, 0, this.selected)
}
this.$root.reloadStyles()
},
remove(style) {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(style)
styles.splice(index, 1)
this.$root.reloadStyles()
},
addStyle(style) {
const styles = this.$root.cfg.visual.styles
styles.push(style)
this.$root.reloadStyles()
}
this.themeList.forEach(theme => {
if (theme.pack) {
theme.pack.forEach(packEntry => {
packEntry.file = theme.file.replace('index.less', '') + packEntry.file
this.themeList.push(packEntry)
})
}
})
})
},
methods: {
gitHubExplore() {
this.$root.openSettingsPage("github-themes")
},
getThemeName(filename) {
try {
return this.themeList.find(theme => theme.file === filename).name;
} catch (e) {
return filename;
}
},
moveUp() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index > 0) {
styles.splice(index, 1)
styles.splice(index - 1, 0, this.selected)
}
this.$root.reloadStyles()
},
moveDown() {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(this.selected)
if (index < styles.length - 1) {
styles.splice(index, 1)
styles.splice(index + 1, 0, this.selected)
}
this.$root.reloadStyles()
},
remove(style) {
const styles = this.$root.cfg.visual.styles
const index = styles.indexOf(style)
styles.splice(index, 1)
this.$root.reloadStyles()
},
addStyle(style) {
const styles = this.$root.cfg.visual.styles
styles.push(style)
this.$root.reloadStyles()
}
}
})
</script>
<script>
Vue.component('installed-themes', {
template: "#installed-themes",
props: [],
data: function () {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
themesInstalled: [],
themes: []
}
Vue.component('installed-themes', {
template: "#installed-themes",
props: [],
data: function() {
return {
repos: [],
openRepo: {
id: -1,
name: '',
description: '',
html_url: '',
stargazers_count: 0,
owner: {
avatar_url: ''
},
readme: ""
},
mounted() {
this.getThemesList();
},
methods: {
getThemesList() {
let self = this
let themes = ipcRenderer.sendSync("get-themes")
themes.unshift({
name: "Acrylic Grain",
file: "grain.less"
})
themes.unshift({
name: "Sweetener",
file: "sweetener.less"
})
themes.unshift({
name: "Reduce Visuals",
file: "reduce_visuals.less"
})
// themes.unshift({
// name: "Inline Drawer",
// file: "inline_drawer.less"
// })
themes.unshift({
name: "Dark",
file: "dark.less"
})
this.themes = themes
},
contextMenu(event, theme) {
let self = this
let menu = {
items: {
"uninstall": {
name: app.getLz("settings.option.visual.theme.uninstall"),
disabled: true,
action: () => {
app.confirm(app.stringTemplateParser(app.getLz("settings.prompt.visual.theme.uninstallTheme"), {
theme: theme.name ?? theme.file
}), (res) => {
if (res) {
console.debug(theme)
ipcRenderer.once("theme-uninstalled", (event, args) => {
console.debug(event, args)
self.getThemesList()
})
ipcRenderer.invoke("uninstall-theme", theme.path)
}
})
}
},
"viewInfo": {
name: app.getLz("settings.option.visual.theme.viewInfo"),
disabled: true,
action: () => {
}
}
}
}
if (theme.path) {
menu.items.uninstall.disabled = false
}
this.$root.showMenuPanel(menu, event)
},
openThemesFolder() {
ipcRenderer.invoke("open-path", "themes")
},
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "" && typeof theme.commit != "") {
self.themesInstalled.push(theme.github_repo.toLowerCase())
}
})
},
addStyle(filename) {
this.$refs.stackEditor.addStyle(filename)
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
themesInstalled: [],
themes: []
}
},
mounted() {
this.getThemesList();
},
methods: {
getThemesList() {
let self = this
let themes = ipcRenderer.sendSync("get-themes")
themes.unshift({
name: "Acrylic Grain",
file: "grain.less"
})
themes.unshift({
name: "Sweetener",
file: "sweetener.less"
})
themes.unshift({
name: "Reduce Visuals",
file: "reduce_visuals.less"
})
// themes.unshift({
// name: "Inline Drawer",
// file: "inline_drawer.less"
// })
themes.unshift({
name: "Dark",
file: "dark.less"
})
this.themes = themes
},
contextMenu(event, theme) {
let self = this
let menu = {
items: {
"uninstall": {
name: app.getLz("settings.option.visual.theme.uninstall"),
disabled: true,
action: () => {
app.confirm(app.stringTemplateParser(app.getLz("settings.prompt.visual.theme.uninstallTheme"), {
theme: theme.name ?? theme.file
}), (res) => {
if (res) {
console.debug(theme)
ipcRenderer.once("theme-uninstalled", (event, args) => {
console.debug(event, args)
self.getThemesList()
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.theme.github.install.confirm'), {
repo: repo.full_name
});
app.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
self.getInstalledThemes()
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", repo.html_url)
}
ipcRenderer.invoke("uninstall-theme", theme.path)
}
})
},
installThemeURL() {
let self = this
app.prompt(app.getLz('settings.prompt.visual.theme.github.URL'), (result) => {
if (result) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch("https://api.github.com/search/repositories?q=topic:cidermusictheme fork:true", requestOptions)
.then(response => response.text())
.then(result => {
let items = JSON.parse(result).items
self.repos = items
})
.catch(error => console.log('error', error));
}
},
"viewInfo": {
name: app.getLz("settings.option.visual.theme.viewInfo"),
disabled: true,
action: () => {
}
}
}
}
})
</script>
if (theme.path) {
menu.items.uninstall.disabled = false
}
this.$root.showMenuPanel(menu, event)
},
openThemesFolder() {
ipcRenderer.invoke("open-path", "themes")
},
getInstalledThemes() {
let self = this
const themes = ipcRenderer.sendSync("get-themes")
// for each theme, get the github_repo property and push it to the themesInstalled array, if not blank
themes.forEach(theme => {
if (theme.github_repo !== "" && typeof theme.commit != "") {
self.themesInstalled.push(theme.github_repo.toLowerCase())
}
})
},
addStyle(filename) {
this.$refs.stackEditor.addStyle(filename)
},
showRepo(repo) {
const self = this
const readmeUrl = `https://raw.githubusercontent.com/${repo.full_name}/main/README.md`;
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch(readmeUrl, requestOptions)
.then(response => response.text())
.then(result => {
self.openRepo = repo
self.openRepo.readme = self.convertReadMe(result);
})
.catch(error => {
self.openRepo = repo
self.openRepo.readme = `This repository doesn't have a README.md file.`;
console.log('error', error)
});
},
convertReadMe(text) {
return marked.parse(text)
},
installThemeRepo(repo) {
let self = this
let msg = app.stringTemplateParser(app.getLz('settings.option.visual.theme.github.install.confirm'), {
repo: repo.full_name
});
app.confirm(msg, (res) => {
if (res) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
self.getInstalledThemes()
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", repo.html_url)
}
})
},
installThemeURL() {
let self = this
app.prompt(app.getLz('settings.prompt.visual.theme.github.URL'), (result) => {
if (result) {
ipcRenderer.once("theme-installed", (event, arg) => {
if (arg.success) {
self.themes = ipcRenderer.sendSync("get-themes")
notyf.success(app.getLz('settings.notyf.visual.theme.install.success'));
} else {
notyf.error(app.getLz('settings.notyf.visual.theme.install.error'));
}
});
ipcRenderer.invoke("get-github-theme", result)
}
});
},
getRepos() {
let self = this
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch("https://api.github.com/search/repositories?q=topic:cidermusictheme fork:true", requestOptions)
.then(response => response.text())
.then(result => {
let items = JSON.parse(result).items
self.repos = items
})
.catch(error => console.log('error', error));
}
}
})
</script>

View file

@ -11,7 +11,7 @@
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/settings.svg" classes="svg-md" name="settings-general"/>
<svg-icon url="./assets/settings.svg" classes="svg-md" name="settings-general" />
</div>
<div>
{{ $root.getLz('settings.header.general') }}
@ -61,7 +61,7 @@
<label>
<input type="checkbox" v-model="$root.cfg.general.privateEnabled"
v-on:change="$root.mk.privateEnabled = $root.cfg.general.privateEnabled"
switch/>
switch />
</label>
</div>
</div>
@ -71,7 +71,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.general.onStartup.enabled" switch/>
<input type="checkbox" v-model="app.cfg.general.onStartup.enabled" switch />
</label>
</div>
</div>
@ -81,7 +81,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.general.onStartup.hidden" switch/>
<input type="checkbox" v-model="app.cfg.general.onStartup.hidden" switch />
</label>
</div>
</div>
@ -163,7 +163,7 @@
<label>
<input type="checkbox"
v-model="app.cfg.general.sidebarItems.recentlyAdded"
switch/>
switch />
</label>
</div>
</div>
@ -175,7 +175,7 @@
<label>
<input type="checkbox"
v-model="app.cfg.general.sidebarItems.songs"
switch/>
switch />
</label>
</div>
</div>
@ -187,7 +187,7 @@
<label>
<input type="checkbox"
v-model="app.cfg.general.sidebarItems.albums"
switch/>
switch />
</label>
</div>
</div>
@ -199,7 +199,7 @@
<label>
<input type="checkbox"
v-model="app.cfg.general.sidebarItems.artists"
switch/>
switch />
</label>
</div>
</div>
@ -211,7 +211,7 @@
<label>
<input type="checkbox"
v-model="app.cfg.general.sidebarItems.videos"
switch/>
switch />
</label>
</div>
</div>
@ -223,7 +223,7 @@
<label>
<input type="checkbox"
v-model="app.cfg.general.sidebarItems.podcasts"
switch/>
switch />
</label>
</div>
</div>
@ -247,7 +247,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.general.themeUpdateNotification"
switch/>
switch />
</label>
</div>
</div>
@ -258,7 +258,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.general.showLovedTracksInline"
switch/>
switch />
</label>
</div>
</div>
@ -279,7 +279,7 @@
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/headphones.svg" classes="svg-md" name="settings-audio"/>
<svg-icon url="./assets/feather/headphones.svg" classes="svg-md" name="settings-audio" />
</div>
<div>
{{ $root.getLz('settings.header.audio') }}
@ -343,7 +343,7 @@
<label>
<input type="checkbox" v-model="app.cfg.audio.seamless_audio"
v-on:change="app.mk._bag.features['seamless-audio-transitions'] = app.cfg.audio.seamless_audio"
switch/>
switch />
</label>
</div>
</div>
@ -372,7 +372,7 @@
<input type="checkbox" v-model="app.cfg.audio.normalization"
v-on:change="toggleNormalization"
:disabled="app.cfg.audio.maikiwiAudio.spatial === true || app.cfg.audio.maikiwiAudio.ciderPPE === true || app.cfg.audio.maikiwiAudio.atmosphereRealizer1 === true || app.cfg.audio.maikiwiAudio.atmosphereRealizer2 === true"
switch/>
switch />
</label>
</div>
</div>
@ -385,7 +385,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.audio.dBSPL" switch/>
<input type="checkbox" v-model="app.cfg.audio.dBSPL" switch />
</label>
</div>
</div>
@ -397,7 +397,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="number" v-model="app.cfg.audio.dBSPLcalibration"/>
<input type="number" v-model="app.cfg.audio.dBSPLcalibration" />
</label>
</div>
</div>
@ -408,33 +408,33 @@
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/zap.svg" classes="svg-md" name="settings-audiolabs"/>
<svg-icon url="./assets/feather/zap.svg" classes="svg-md" name="settings-audiolabs" />
</div>
<div>
{{ $root.getLz('settings.option.audio.audioLab') }}
</div>
</template>
<div class="settings-tab-content">
<audiolabs-page/>
<audiolabs-page />
</div>
</b-tab>
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/style.svg" classes="svg-md" name="settings-styles"/>
<svg-icon url="./assets/feather/style.svg" classes="svg-md" name="settings-styles" />
</div>
<div>
{{ $root.getLz('settings.header.visual.styles') }}
</div>
</template>
<div class="settings-tab-content">
<installed-themes/>
<installed-themes />
</div>
</b-tab>
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/pen-tool.svg" classes="svg-md" name="settings-visual"/>
<svg-icon url="./assets/feather/pen-tool.svg" classes="svg-md" name="settings-visual" />
</div>
<div>
{{ $root.getLz('settings.header.visual') }}
@ -504,7 +504,7 @@
{{$root.getLz('settings.option.visual.windowColor')}}
</div>
<div class="md-option-segment_auto">
<input type="color" v-model="app.cfg.visual.windowColor"/>
<input type="color" v-model="app.cfg.visual.windowColor" />
</div>
</div>
<div class="md-option-line">
@ -513,7 +513,7 @@
</div>
<div class="md-option-segment_auto">
<input type="checkbox" v-model="app.cfg.visual.customAccentColor"
:disabled="app.cfg.visual.purplePodcastPlaybackBar" switch/>
:disabled="app.cfg.visual.purplePodcastPlaybackBar" switch />
</div>
</div>
<div class="md-option-line child" v-if="app.cfg.visual.customAccentColor">
@ -521,7 +521,7 @@
{{$root.getLz('settings.option.visual.accentColor')}}
</div>
<div class="md-option-segment_auto">
<input type="color" v-model="app.cfg.visual.accentColor"/>
<input type="color" v-model="app.cfg.visual.accentColor" />
</div>
</div>
<div class="md-option-line">
@ -530,7 +530,7 @@
</div>
<div class="md-option-segment_auto">
<input type="checkbox" v-model="app.cfg.visual.purplePodcastPlaybackBar"
:disabled="app.cfg.visual.customAccentColor" switch/>
:disabled="app.cfg.visual.customAccentColor" switch />
</div>
</div>
<div class="md-option-line">
@ -538,7 +538,7 @@
{{$root.getLz('settings.option.visual.compactArtistHeader')}}
</div>
<div class="md-option-segment_auto">
<input type="checkbox" v-model="app.cfg.visual.compactArtistHeader" switch/>
<input type="checkbox" v-model="app.cfg.visual.compactArtistHeader" switch />
</div>
</div>
<div class="md-option-line">
@ -569,7 +569,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.visual.showuserinfo"
v-on:change="toggleUserInfo" switch/>
v-on:change="toggleUserInfo" switch />
</label>
</div>
</div>
@ -604,7 +604,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.general.close_button_hide" switch/>
<input type="checkbox" v-model="app.cfg.general.close_button_hide" switch />
</label>
</div>
</div>
@ -616,7 +616,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.visual.nativeTitleBar" switch
@change="promptForRelaunch()"/>
@change="promptForRelaunch()" />
</label>
</div>
</div>
@ -697,7 +697,8 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" switch v-model="app.cfg.visual.bg_artwork_rotation"/>
<input type="checkbox" switch
v-model="app.cfg.visual.bg_artwork_rotation" />
</label>
</div>
</div>
@ -708,20 +709,20 @@
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/plugins.svg" classes="svg-md" name="settings-plugins"/>
<svg-icon url="./assets/feather/plugins.svg" classes="svg-md" name="settings-plugins" />
</div>
<div>
{{ $root.getLz('term.plugins') }}
</div>
</template>
<div class="settings-tab-content">
<plugins-github/>
<plugins-github />
</div>
</b-tab>
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/mic.svg" classes="svg-md" name="settings-lyrics"/>
<svg-icon url="./assets/feather/mic.svg" classes="svg-md" name="settings-lyrics" />
</div>
<div>
{{ $root.getLz('settings.header.lyrics') }}
@ -740,7 +741,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.lyrics.enable_mxm" switch/>
<input type="checkbox" v-model="app.cfg.lyrics.enable_mxm" switch />
</label>
</div>
</div>
@ -750,7 +751,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.lyrics.mxm_karaoke" switch/>
<input type="checkbox" v-model="app.cfg.lyrics.mxm_karaoke" switch />
</label>
</div>
</div>
@ -844,7 +845,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.lyrics.enable_yt" switch/>
<input type="checkbox" v-model="app.cfg.lyrics.enable_yt" switch />
</label>
</div>
</div>
@ -854,7 +855,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.lyrics.enable_qq" switch/>
<input type="checkbox" v-model="app.cfg.lyrics.enable_qq" switch />
</label>
</div>
</div>
@ -865,7 +866,7 @@
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/radio.svg" classes="svg-md" name="settings-connectivity"/>
<svg-icon url="./assets/feather/radio.svg" classes="svg-md" name="settings-connectivity" />
</div>
<div>
{{ $root.getLz('settings.header.connectivity') }}
@ -886,7 +887,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.general.playbackNotifications"
switch/>
switch />
</label>
</div>
</div>
@ -899,7 +900,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.connectivity.discord_rpc.enabled"
switch/>
switch />
</label>
</div>
</div>
@ -926,7 +927,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox"
v-model="app.cfg.connectivity.discord_rpc.clear_on_pause" switch/>
v-model="app.cfg.connectivity.discord_rpc.clear_on_pause" switch />
</label>
</div>
</div>
@ -938,7 +939,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox"
v-model="app.cfg.connectivity.discord_rpc.hide_buttons" switch/>
v-model="app.cfg.connectivity.discord_rpc.hide_buttons" switch />
</label>
</div>
</div>
@ -950,14 +951,14 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox"
v-model="app.cfg.connectivity.discord_rpc.hide_timestamp" switch/>
v-model="app.cfg.connectivity.discord_rpc.hide_timestamp" switch />
</label>
</div>
</div>
<div class="md-option-line" v-show="app.cfg.connectivity.discord_rpc.enabled != false">
<div class="md-option-segment">
{{$root.getLz('settings.option.connectivity.discordRPC.detailsFormat')}}<br/>
{{$root.getLz('settings.option.connectivity.discordRPC.detailsFormat')}}<br />
<small>{{$root.getLz('term.variables')}}: {artist}, {composer}, {title},
{album},
{trackNumber}</small>
@ -965,7 +966,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="text"
v-model="app.cfg.connectivity.discord_rpc.details_format"/>
v-model="app.cfg.connectivity.discord_rpc.details_format" />
</label>
</div>
</div>
@ -979,7 +980,8 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="text" v-model="app.cfg.connectivity.discord_rpc.state_format"/>
<input type="text"
v-model="app.cfg.connectivity.discord_rpc.state_format" />
</label>
</div>
</div>
@ -1018,9 +1020,9 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<form @submit.prevent="submitToken">
<input type="text" autofocus id="lfmToken"/>
<input type="text" autofocus id="lfmToken" />
<input type="submit" class="md-btn"
@value="$root.getLz('action.submit')"/>
@value="$root.getLz('action.submit')" />
</form>
</label>
</div>
@ -1032,7 +1034,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="number" min="50" max="100"
v-model="app.cfg.connectivity.lastfm.scrobble_after"/>
v-model="app.cfg.connectivity.lastfm.scrobble_after" />
</label>
</div>
</div>
@ -1044,7 +1046,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.connectivity.lastfm.filter_loop"
switch/>
switch />
</label>
</div>
</div>
@ -1055,7 +1057,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.connectivity.lastfm.remove_featured"
switch/>
switch />
</label>
</div>
</div>
@ -1077,7 +1079,7 @@
<b-tab>
<template #title>
<div>
<svg-icon url="./assets/feather/hard-drive.svg" classes="svg-md" name="settings-advanced"/>
<svg-icon url="./assets/feather/hard-drive.svg" classes="svg-md" name="settings-advanced" />
</div>
<div>
{{$root.getLz('settings.header.advanced')}}
@ -1118,7 +1120,7 @@
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.advanced.disableLogging" switch/>
<input type="checkbox" v-model="app.cfg.advanced.disableLogging" switch />
</label>
</div>
</div>
@ -1161,7 +1163,7 @@
<input type="checkbox"
v-model="app.cfg.advanced.experiments.includes('immersive-preview')"
@click="app.cfg.advanced.experiments.includes('immersive-preview') ? removeExperiment('immersive-preview') : addExperiment('immersive-preview')"
switch/>
switch />
</label>
</div>
</div>
@ -1177,7 +1179,7 @@
<input type="checkbox"
v-model="app.cfg.advanced.experiments.includes('unknown-sources')"
@click="app.cfg.advanced.experiments.includes('unknown-sources') ? removeExperiment('unknown-sources') : addExperiment('unknown-sources')"
switch/>
switch />
</label>
</div>
</div>
@ -1192,7 +1194,7 @@
<input type="checkbox"
v-model="app.cfg.advanced.experiments.includes('ampv3')"
@click="app.cfg.advanced.experiments.includes('ampv3') ? removeExperiment('ampv3') : addExperiment('ampv3')"
switch/>
switch />
</label>
</div>
</div>
@ -1206,7 +1208,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.advanced.playlistTrackMapping"
switch/>
switch />
</label>
</div>
</div>
@ -1221,7 +1223,7 @@
<input type="checkbox"
v-model="app.cfg.advanced.experiments.includes('compactui')"
@click="app.cfg.advanced.experiments.includes('compactui') ? removeExperiment('compactui') : addExperiment('compactui')"
switch :disabled="!!app.getThemeDirective('forceUI')"/>
switch :disabled="!!app.getThemeDirective('forceUI')" />
</label>
</div>
</div>
@ -1235,7 +1237,7 @@
<input type="checkbox" disabled
v-model="app.cfg.advanced.experiments.includes('inline-playlists')"
@click="app.cfg.advanced.experiments.includes('inline-playlists') ? removeExperiment('inline-playlists') : addExperiment('inline-playlists')"
switch/>
switch />
</label>
</div>
</div>
@ -1248,7 +1250,7 @@
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.visual.transparent" switch
@change="promptForRelaunch()"/>
@change="promptForRelaunch()" />
</label>
</div>
</div>
@ -1280,14 +1282,14 @@
<!--keybinds Settings -->
<b-tab id="hid">
<template>
<keybinds-settings/>
<keybinds-settings />
</template>
</b-tab>
<!--keybinds-settings -->
<!--Github-theme-settings -->
<b-tab id="hid">
<template>
<themes-github/>
<themes-github />
</template>
</b-tab>
<!--Github-theme-settings -->
@ -1383,164 +1385,164 @@
</div>
</script>
<script>
Vue.component("settings-window", {
template: "#settings-window",
data: function () {
return {
app: this.$root,
themes: ipcRenderer.sendSync("get-themes"),
tabIndex: 0,
canChangeHash: false,
lastfmConnecting: false
}
},
watch: {
tabIndex: function (val) {
if (this.canChangeHash) {
// window.location.hash = `#settings/${val}`
}
}
},
methods: {
sidebarVis() {
const tabIndex = app.$store.state.pageState['settings'].currentTabIndex
if (tabIndex == 3 || tabIndex == 5 || tabIndex == 10) {
return true;
}
return false
},
close() {
this.$root.modals.settings = false
},
windowBgStyleChange() {
this.$root.getNowPlayingArtworkBG(undefined, true)
if (this.$root.cfg.visual.window_background_style === "mica") {
this.$root.spawnMica()
}
},
reinstallWidevineCDM() {
app.confirm(app.getLz("settings.option.experimental.reinstallwidevine.confirm"), (ok) => {
if (ok) {
ipcRenderer.invoke("reinstall-widevine-cdm");
}
})
},
gitHubExplore() {
app.openSettingsPage("github-themes")
},
copyLogs() {
ipcRenderer.send('fetch-log')
notyf.success(app.getLz('term.share.success'));
},
openAppData() {
ipcRenderer.send('open-appdata')
},
getLanguages: function () {
let langs = this.$root.lzListing
let categories = {
"main": [],
"fun": [],
"unsorted": []
}
// sort by category if category is undefined or empty put it in "unsorted"
for (let i = 0; i < langs.length; i++) {
if (langs[i].category === undefined || langs[i].category === "") {
categories.unsorted.push(langs[i])
} else {
categories[langs[i].category].push(langs[i])
}
}
// return
return categories
},
addExperiment(flag) {
app.cfg.advanced.experiments.push(flag);
},
removeExperiment(flag) {
app.cfg.advanced.experiments.splice(app.cfg.advanced.experiments.indexOf(flag), 1);
},
toggleNormalization: function () {
if (app.cfg.audio.normalization) {
CiderAudio.normalizerOn()
} else {
CiderAudio.normalizerOff()
}
},
changeAudioQuality: function () {
app.mk.bitrate = MusicKit.PlaybackBitrate[app.cfg.audio.quality];
},
toggleUserInfo: function () {
app.chrome.hideUserInfo = !app.cfg.visual.showuserinfo
},
sendDataToMTT: function () {
ipcRenderer.invoke('setStoreValue', 'general.close_behavior', app.cfg.general.close_behavior);
// setStoreValue does not change plugin store values somehow
ipcRenderer.invoke('update-store-mtt', app.cfg.general.close_behavior);
},
checkIfUpdateDisabled() {
if (app.cfg.main.UPDATABLE) return;
let updateFields = document.getElementsByClassName('update-check');
for (let i = 0; i < updateFields.length; i++) {
updateFields[i].style = "opacity: 0.5; pointer-events: none;";
updateFields[i].title = "Not available on this type of build";
}
},
promptForRelaunch() {
app.confirm(app.getLz('action.relaunch.confirm'), function (result) {
if (result) {
ipcRenderer.send('relaunchApp', '');
}
});
},
authCC() {
ipcRenderer.send('cc-auth')
},
logoutCC() {
ipcRenderer.send('cc-logout')
},
reloadDiscordRPC() {
ipcRenderer.send('reloadRPC')
},
lfmDisconnect() {
this.$root.cfg.connectivity.lastfm.enabled = false;
this.$root.cfg.connectivity.lastfm.secrets.username = "";
this.$root.cfg.connectivity.lastfm.secrets.key = "";
ipcRenderer.send('lastfm:disconnect');
},
async lfmAuthorize() {
this.lastfmConnecting = true;
window.open(await ipcRenderer.invoke('lastfm:url'));
app.notyf.success(app.getLz('settings.notyf.connectivity.lastfmScrobble.connecting'));
/* Just a timeout for the button */
setTimeout(() => {
if (!this.$root.cfg.connectivity.lastfm.enabled) {
app.notyf.error(app.getLz('settings.notyf.connectivity.lastfmScrobble.connectError'));
console.warn('[lastfm:authorize] Last.fm authorization timed out.');
this.lastfmConnecting = false;
}
}, 20000);
ipcRenderer.once('lastfm:authenticated', (_e, session) => {
this.$root.cfg.connectivity.lastfm.secrets.username = session.username
this.$root.cfg.connectivity.lastfm.secrets.key = session.key
this.$root.cfg.connectivity.lastfm.enabled = true
this.lastfmConnecting = false;
app.notyf.success(app.getLz('settings.notyf.connectivity.lastfmScrobble.connectSuccess'));
})
},
filterChange(e) {
this.$root.cfg.connectivity.lastfm.filter_types[e.target.value] = e.target.checked;
},
submitToken() {
const token = document.getElementById('lfmToken').value;
ipcRenderer.send('lastfm:auth', token);
},
openLocalSongsPathMenu() {
app.modals.pathMenu = true
}
Vue.component("settings-window", {
template: "#settings-window",
data: function() {
return {
app: this.$root,
themes: ipcRenderer.sendSync("get-themes"),
tabIndex: 0,
canChangeHash: false,
lastfmConnecting: false
}
},
watch: {
tabIndex: function(val) {
if (this.canChangeHash) {
// window.location.hash = `#settings/${val}`
}
})
</script>
}
},
methods: {
sidebarVis() {
const tabIndex = app.$store.state.pageState['settings'].currentTabIndex
if (tabIndex == 3 || tabIndex == 5 || tabIndex == 10) {
return true;
}
return false
},
close() {
this.$root.modals.settings = false
},
windowBgStyleChange() {
this.$root.getNowPlayingArtworkBG(undefined, true)
if (this.$root.cfg.visual.window_background_style === "mica") {
this.$root.spawnMica()
}
},
reinstallWidevineCDM() {
app.confirm(app.getLz("settings.option.experimental.reinstallwidevine.confirm"), (ok) => {
if (ok) {
ipcRenderer.invoke("reinstall-widevine-cdm");
}
})
},
gitHubExplore() {
app.openSettingsPage("github-themes")
},
copyLogs() {
ipcRenderer.send('fetch-log')
notyf.success(app.getLz('term.share.success'));
},
openAppData() {
ipcRenderer.send('open-appdata')
},
getLanguages: function() {
let langs = this.$root.lzListing
let categories = {
"main": [],
"fun": [],
"unsorted": []
}
// sort by category if category is undefined or empty put it in "unsorted"
for (let i = 0; i < langs.length; i++) {
if (langs[i].category === undefined || langs[i].category === "") {
categories.unsorted.push(langs[i])
} else {
categories[langs[i].category].push(langs[i])
}
}
// return
return categories
},
addExperiment(flag) {
app.cfg.advanced.experiments.push(flag);
},
removeExperiment(flag) {
app.cfg.advanced.experiments.splice(app.cfg.advanced.experiments.indexOf(flag), 1);
},
toggleNormalization: function() {
if (app.cfg.audio.normalization) {
CiderAudio.normalizerOn()
} else {
CiderAudio.normalizerOff()
}
},
changeAudioQuality: function() {
app.mk.bitrate = MusicKit.PlaybackBitrate[app.cfg.audio.quality];
},
toggleUserInfo: function() {
app.chrome.hideUserInfo = !app.cfg.visual.showuserinfo
},
sendDataToMTT: function() {
ipcRenderer.invoke('setStoreValue', 'general.close_behavior', app.cfg.general.close_behavior);
// setStoreValue does not change plugin store values somehow
ipcRenderer.invoke('update-store-mtt', app.cfg.general.close_behavior);
},
checkIfUpdateDisabled() {
if (app.cfg.main.UPDATABLE) return;
let updateFields = document.getElementsByClassName('update-check');
for (let i = 0; i < updateFields.length; i++) {
updateFields[i].style = "opacity: 0.5; pointer-events: none;";
updateFields[i].title = "Not available on this type of build";
}
},
promptForRelaunch() {
app.confirm(app.getLz('action.relaunch.confirm'), function(result) {
if (result) {
ipcRenderer.send('relaunchApp', '');
}
});
},
authCC() {
ipcRenderer.send('cc-auth')
},
logoutCC() {
ipcRenderer.send('cc-logout')
},
reloadDiscordRPC() {
ipcRenderer.send('reloadRPC')
},
lfmDisconnect() {
this.$root.cfg.connectivity.lastfm.enabled = false;
this.$root.cfg.connectivity.lastfm.secrets.username = "";
this.$root.cfg.connectivity.lastfm.secrets.key = "";
ipcRenderer.send('lastfm:disconnect');
},
async lfmAuthorize() {
this.lastfmConnecting = true;
window.open(await ipcRenderer.invoke('lastfm:url'));
app.notyf.success(app.getLz('settings.notyf.connectivity.lastfmScrobble.connecting'));
/* Just a timeout for the button */
setTimeout(() => {
if (!this.$root.cfg.connectivity.lastfm.enabled) {
app.notyf.error(app.getLz('settings.notyf.connectivity.lastfmScrobble.connectError'));
console.warn('[lastfm:authorize] Last.fm authorization timed out.');
this.lastfmConnecting = false;
}
}, 20000);
ipcRenderer.once('lastfm:authenticated', (_e, session) => {
this.$root.cfg.connectivity.lastfm.secrets.username = session.username
this.$root.cfg.connectivity.lastfm.secrets.key = session.key
this.$root.cfg.connectivity.lastfm.enabled = true
this.lastfmConnecting = false;
app.notyf.success(app.getLz('settings.notyf.connectivity.lastfmScrobble.connectSuccess'));
})
},
filterChange(e) {
this.$root.cfg.connectivity.lastfm.filter_types[e.target.value] = e.target.checked;
},
submitToken() {
const token = document.getElementById('lfmToken').value;
ipcRenderer.send('lastfm:auth', token);
},
openLocalSongsPathMenu() {
app.modals.pathMenu = true
}
}
})
</script>

View file

@ -1,10 +1,12 @@
<script type="text/x-template" id="add-to-playlist">
<template>
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()" @contextmenu.self="app.resetState()">
<div class="modal-fullscreen addtoplaylist-panel" @click.self="app.resetState()"
@contextmenu.self="app.resetState()">
<div class="modal-window">
<div class="modal-header">
<div class="modal-title">{{app.getLz('action.addToPlaylist')}}</div>
<button class="close-btn" @click="app.resetState()" :aria-label="app.getLz('action.close')"></button>
<button class="close-btn" @click="app.resetState()"
:aria-label="app.getLz('action.close')"></button>
</div>
<div class="modal-content">
<button class="playlist-item"
@ -12,7 +14,8 @@
<div class="icon"><%- include("../svg/plus.svg") %></div>
<div class="name">{{app.getLz('action.createPlaylist')}}</div>
</button>
<sidebar-playlist :playlist-select="playlistSelect" v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" :item="item">
<sidebar-playlist :playlist-select="playlistSelect"
v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" :item="item">
</sidebar-playlist>
</div>
<div class="modal-search">
@ -34,55 +37,55 @@
</script>
<script>
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function () {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
})
},
methods: {
playlistSelect(playlist) {
if (playlist.type != "library-playlist-folders") {
this.addToPlaylist(playlist.id)
}
},
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
Vue.component('add-to-playlist', {
template: '#add-to-playlist',
data: function() {
return {
playlistSorted: [],
searchQuery: "",
focused: "",
app: this.$root,
}
},
props: {
playlists: {
type: Array,
required: true
}
},
mounted() {
this.search()
this.$refs.searchInput.focus()
this.$refs.searchInput.addEventListener('keydown', (e) => {
if (e.keyCode == 13) {
if (this.focused != "") {
this.addToPlaylist(this.focused)
}
}
});
</script>
})
},
methods: {
playlistSelect(playlist) {
if (playlist.type != "library-playlist-folders") {
this.addToPlaylist(playlist.id)
}
},
addToPlaylist(id) {
app.addSelectedToPlaylist(id)
},
search() {
this.focused = ""
if (this.searchQuery == "") {
this.playlistSorted = this.playlists
} else {
this.playlistSorted = this.playlists.filter(playlist => {
return playlist.attributes.name.toLowerCase().indexOf(this.searchQuery.toLowerCase()) > -1
})
if (this.playlistSorted.length == 1) {
this.focused = this.playlistSorted[0].id
}
}
},
}
});
</script>

View file

@ -1,6 +1,6 @@
<script type="text/x-template" id="sidebar-playlist">
<div class="sidebar-playlist" :key="item.id">
<button class="app-sidebar-item app-sidebar-item-playlist" :key="item.id"
<button class="app-sidebar-item app-sidebar-item-playlist" :key="item.id"
:class="item.type != 'library-playlist-folders' ? {'active': $root.page.includes(item.id)} : ['playlist-folder', {'folder-button-active': folderOpened}, isPlaylistSelected]"
@contextmenu="playlistContextMenu($event, item.id)"
@dragstart="startDrag($event, item)"
@ -9,14 +9,17 @@
:href="item.href"
@click='clickEvent()'>
<template v-if="!renaming">
<svg-icon :url="icon" name="sidebar-playlist"/> {{ item.attributes.name }}
<svg-icon :url="icon" name="sidebar-playlist" />
{{ item.attributes.name }}
<small class="presentNotice" v-if="hasRelatedMediaItems">(Track present)</small>
</template>
<input type="text" v-model="item.attributes.name" class="pl-rename-field" @blur="rename()" @keydown.enter="rename()" v-else>
<input type="text" v-model="item.attributes.name" class="pl-rename-field" @blur="rename()"
@keydown.enter="rename()" v-else>
</button>
<div class="folder-body" v-if="item.type === 'library-playlist-folders' && folderOpened">
<template v-if="children.length != 0">
<sidebar-playlist v-for="item in children" :relate-media-items="relateMediaItems" :playlist-select="playlistSelect" :item="item" :key="item.id"></sidebar-playlist>
<sidebar-playlist v-for="item in children" :relate-media-items="relateMediaItems"
:playlist-select="playlistSelect" :item="item" :key="item.id"></sidebar-playlist>
</template>
<template v-else>
<div class="spinner"></div>
@ -26,263 +29,265 @@
</script>
<script>
Vue.component('sidebar-playlist', {
template: '#sidebar-playlist',
props: {
item: {
type: Object,
required: true
},
playlistSelect: {
type: Function,
required: false
},
relateMediaItems: {
type: Array,
required: false,
default() {
return []
}
}
},
data: function () {
return {
folderOpened: false,
children: [],
playlistRoot: "p.playlistsroot",
renaming: false,
icon: "",
hasRelatedMediaItems: false
}
},
async mounted() {
if (this.item.type !== "library-playlist-folders") {
this.icon = ("./assets/feather/list.svg")
} else {
this.icon = ("./assets/feather/folder.svg")
}
let playlistMap = this.$root.playlists.trackMapping
if (this.relateMediaItems.length != 0) {
if (playlistMap[this.relateMediaItems[0]]) {
if (playlistMap[this.relateMediaItems[0]].includes(this.item.id)) {
this.hasRelatedMediaItems = true
}
}
}
},
methods: {
clickEvent() {
if (this.item.type != "library-playlist-folders") {
if (this.playlistSelect) {
this.playlistSelect(this.item)
} else {
this.openPlaylist(this.item)
}
} else {
this.getPlaylistChildren(this.item)
}
},
rename() {
this.renaming = false
if (this.item.type === "library-playlist-folders") {
this.$root.editPlaylistFolder(this.item.id, this.item.attributes.name)
} else {
this.$root.editPlaylist(this.item.id, this.item.attributes.name)
}
},
async getChildren() {
let self = this
this.children = []
this.children = this.$root.playlists.listing.filter(child => {
if (child.parent == self.item.id) {
return child
}
})
},
async move(item, sendTo) {
let self = this
console.log(sendTo)
let type = item.type.replace("library-", "")
let typeTo = sendTo.type
this.$root.mk.api.v3.music(`/v1/me/library/${type}/${item.id}/parent`, {}, {
fetchOptions: {
method: "PUT",
body: JSON.stringify({
data: [{
id: sendTo.id,
type: typeTo
}]
})
}
})
// find the item in this.$root.playlists.listing and store it in a variable
this.$root.playlists.listing.filter(playlist => {
if (playlist.id == item.id) {
console.log(playlist)
playlist.parent = sendTo.id
}
})
if (typeof this.$root.getChildren == "function") {
this.$root.getChildren()
console.log(this.$root.children)
}
await this.getChildren()
this.$root.sortPlaylists()
// await this.$root.refreshPlaylists()
},
playlistContextMenu(event, playlist_id) {
let menu = {
items: {
"moveToParent": {
name: this.$root.getLz('action.moveToTop'),
action: () => {
let self = this
this.move(this.item, {
id: this.playlistRoot,
type: "library-playlist-folders"
})
setTimeout(() => { self.getChildren() }, 2000)
}
},
"rename": {
name: this.$root.getLz('action.rename'),
action: () => {
this.renaming = true
setTimeout(() => {
document.querySelector(".pl-rename-field").focus()
document.querySelector(".pl-rename-field").select()
}, 100)
}
},
"deleteFromPlaylist": {
name: this.$root.getLz('action.removeFromLibrary'),
action: () => {
this.$root.deletePlaylist(playlist_id)
}
},
"addToFavorites": {
name: this.$root.getLz('action.addToFavorites'),
disabled: true,
hidden: true,
action: () => {
this.addFavorite(playlist_id, "library-playlists")
}
}
}
}
if (this.item.type === "library-playlist-folders") {
menu.items.addToFavorites.disabled = true
}
app.showMenuPanel(menu, event)
},
dragOver(evt) {
evt.preventDefault();
evt.dataTransfer.dropEffect = "move";
},
onDrop(evt) {
let data = JSON.parse(evt.dataTransfer.getData("text/plain"))
evt.preventDefault();
if (data.id == this.item.id) {
return;
}
console.log(data)
if (data) {
if (this.item.type == "library-playlists" || this.item.type == "library-playlist-folders") {
if (data.type == "library-playlists" && this.item.type == "library-playlists") {
return
}
this.move(data, this.item)
}
}
},
startDrag(evt) {
evt.dataTransfer.dropEffect = 'move'
evt.dataTransfer.effectAllowed = 'move'
evt.dataTransfer.setData('text/plain', JSON.stringify(this.item))
},
openPlaylist(item) {
this.$root.appRoute(`playlist_` + item.id);
this.$root.showingPlaylist = [];
if (item.id == 'ciderlocal') {
this.$root.showingPlaylist = {
"id": "ciderlocal",
"type": "library-playlists",
"href": "",
"attributes": {
"artwork": {
"width": null,
"height": null,
"url": "",
"hasP3": false
},
"dateAdded": "2021-02-16T03:39:47Z",
"name": "Local Songs",
"canDelete": true,
"hasCatalog": true,
"canEdit": true,
"playParams": {
"id": "ciderlocal",
"kind": "playlist",
"isLibrary": true,
},
"isPublic": true,
"description": {
"standard": ""
}
},
"relationships": {
"tracks": {
"href": "",
"data": this.$root.library.localsongs
}
}
}
this.$root.playlists.loadingState = 1;
} else {
this.$root.getPlaylistFromID(this.$root.page.substring(9), true)
}
},
getPlaylistChildren(item) {
let self = this
this.children = []
this.getChildren()
this.toggleFolder()
this.$root.mk.api.v3.music(`v1/me/library/playlist-folders/${item.id}/children`).then(data => {
let children = data.data.data;
children.forEach(child => {
if (!self.$root.playlists.listing.find(listing => listing.id == child.id)) {
child.parent = self.item.id
self.$root.playlists.listing.push(child)
}
})
self.$root.playlists.listing.sort((a, b) => {
if (a.type === 'library-playlist-folders' && b.type !== 'library-playlist-folders') {
return -1
} else if (a.type !== 'library-playlist-folders' && b.type === 'library-playlist-folders') {
return 1
} else {
return 0
}
})
self.getChildren()
})
},
isPlaylistSelected(item) {
if (this.$root.showingPlaylist.id == item.id) {
return ["active"]
} else {
return []
}
},
toggleFolder() {
this.folderOpened = !this.folderOpened;
}
Vue.component('sidebar-playlist', {
template: '#sidebar-playlist',
props: {
item: {
type: Object,
required: true
},
playlistSelect: {
type: Function,
required: false
},
relateMediaItems: {
type: Array,
required: false,
default() {
return []
}
});
</script>
}
},
data: function() {
return {
folderOpened: false,
children: [],
playlistRoot: "p.playlistsroot",
renaming: false,
icon: "",
hasRelatedMediaItems: false
}
},
async mounted() {
if (this.item.type !== "library-playlist-folders") {
this.icon = ("./assets/feather/list.svg")
} else {
this.icon = ("./assets/feather/folder.svg")
}
let playlistMap = this.$root.playlists.trackMapping
if (this.relateMediaItems.length != 0) {
if (playlistMap[this.relateMediaItems[0]]) {
if (playlistMap[this.relateMediaItems[0]].includes(this.item.id)) {
this.hasRelatedMediaItems = true
}
}
}
},
methods: {
clickEvent() {
if (this.item.type != "library-playlist-folders") {
if (this.playlistSelect) {
this.playlistSelect(this.item)
} else {
this.openPlaylist(this.item)
}
} else {
this.getPlaylistChildren(this.item)
}
},
rename() {
this.renaming = false
if (this.item.type === "library-playlist-folders") {
this.$root.editPlaylistFolder(this.item.id, this.item.attributes.name)
} else {
this.$root.editPlaylist(this.item.id, this.item.attributes.name)
}
},
async getChildren() {
let self = this
this.children = []
this.children = this.$root.playlists.listing.filter(child => {
if (child.parent == self.item.id) {
return child
}
})
},
async move(item, sendTo) {
let self = this
console.log(sendTo)
let type = item.type.replace("library-", "")
let typeTo = sendTo.type
this.$root.mk.api.v3.music(`/v1/me/library/${type}/${item.id}/parent`, {}, {
fetchOptions: {
method: "PUT",
body: JSON.stringify({
data: [{
id: sendTo.id,
type: typeTo
}]
})
}
})
// find the item in this.$root.playlists.listing and store it in a variable
this.$root.playlists.listing.filter(playlist => {
if (playlist.id == item.id) {
console.log(playlist)
playlist.parent = sendTo.id
}
})
if (typeof this.$root.getChildren == "function") {
this.$root.getChildren()
console.log(this.$root.children)
}
await this.getChildren()
this.$root.sortPlaylists()
// await this.$root.refreshPlaylists()
},
playlistContextMenu(event, playlist_id) {
let menu = {
items: {
"moveToParent": {
name: this.$root.getLz('action.moveToTop'),
action: () => {
let self = this
this.move(this.item, {
id: this.playlistRoot,
type: "library-playlist-folders"
})
setTimeout(() => {
self.getChildren()
}, 2000)
}
},
"rename": {
name: this.$root.getLz('action.rename'),
action: () => {
this.renaming = true
setTimeout(() => {
document.querySelector(".pl-rename-field").focus()
document.querySelector(".pl-rename-field").select()
}, 100)
}
},
"deleteFromPlaylist": {
name: this.$root.getLz('action.removeFromLibrary'),
action: () => {
this.$root.deletePlaylist(playlist_id)
}
},
"addToFavorites": {
name: this.$root.getLz('action.addToFavorites'),
disabled: true,
hidden: true,
action: () => {
this.addFavorite(playlist_id, "library-playlists")
}
}
}
}
if (this.item.type === "library-playlist-folders") {
menu.items.addToFavorites.disabled = true
}
app.showMenuPanel(menu, event)
},
dragOver(evt) {
evt.preventDefault();
evt.dataTransfer.dropEffect = "move";
},
onDrop(evt) {
let data = JSON.parse(evt.dataTransfer.getData("text/plain"))
evt.preventDefault();
if (data.id == this.item.id) {
return;
}
console.log(data)
if (data) {
if (this.item.type == "library-playlists" || this.item.type == "library-playlist-folders") {
if (data.type == "library-playlists" && this.item.type == "library-playlists") {
return
}
this.move(data, this.item)
}
}
},
startDrag(evt) {
evt.dataTransfer.dropEffect = 'move'
evt.dataTransfer.effectAllowed = 'move'
evt.dataTransfer.setData('text/plain', JSON.stringify(this.item))
},
openPlaylist(item) {
this.$root.appRoute(`playlist_` + item.id);
this.$root.showingPlaylist = [];
if (item.id == 'ciderlocal') {
this.$root.showingPlaylist = {
"id": "ciderlocal",
"type": "library-playlists",
"href": "",
"attributes": {
"artwork": {
"width": null,
"height": null,
"url": "",
"hasP3": false
},
"dateAdded": "2021-02-16T03:39:47Z",
"name": "Local Songs",
"canDelete": true,
"hasCatalog": true,
"canEdit": true,
"playParams": {
"id": "ciderlocal",
"kind": "playlist",
"isLibrary": true,
},
"isPublic": true,
"description": {
"standard": ""
}
},
"relationships": {
"tracks": {
"href": "",
"data": this.$root.library.localsongs
}
}
}
this.$root.playlists.loadingState = 1;
} else {
this.$root.getPlaylistFromID(this.$root.page.substring(9), true)
}
},
getPlaylistChildren(item) {
let self = this
this.children = []
this.getChildren()
this.toggleFolder()
this.$root.mk.api.v3.music(`v1/me/library/playlist-folders/${item.id}/children`).then(data => {
let children = data.data.data;
children.forEach(child => {
if (!self.$root.playlists.listing.find(listing => listing.id == child.id)) {
child.parent = self.item.id
self.$root.playlists.listing.push(child)
}
})
self.$root.playlists.listing.sort((a, b) => {
if (a.type === 'library-playlist-folders' && b.type !== 'library-playlist-folders') {
return -1
} else if (a.type !== 'library-playlist-folders' && b.type === 'library-playlist-folders') {
return 1
} else {
return 0
}
})
self.getChildren()
})
},
isPlaylistSelected(item) {
if (this.$root.showingPlaylist.id == item.id) {
return ["active"]
} else {
return []
}
},
toggleFolder() {
this.folderOpened = !this.folderOpened;
}
}
});
</script>

View file

@ -140,12 +140,12 @@
</template>
<template v-if="$root.cfg.libraryPrefs.localPaths.length != 0">
<div class="app-sidebar-header-text"
@click="$root.cfg.general.sidebarCollapsed.localLibrary = !$root.cfg.general.sidebarCollapsed.localLibrary"
:class="{collapsed: $root.cfg.general.sidebarCollapsed.localLibrary}">
Local Library
@click="$root.cfg.general.sidebarCollapsed.localLibrary = !$root.cfg.general.sidebarCollapsed.localLibrary"
:class="{collapsed: $root.cfg.general.sidebarCollapsed.localLibrary}">
Local Library
</div>
<template v-if="!$root.cfg.general.sidebarCollapsed.localLibrary">
<sidebar-playlist :item="{attributes: { name:'Songs'} , id:'ciderlocal'}"></sidebar-playlist>
<sidebar-playlist :item="{attributes: { name:'Songs'} , id:'ciderlocal'}"></sidebar-playlist>
</template>
</template>
<template v-if="$root.getPlaylistFolderChildren('p.applemusic').length != 0">
@ -310,7 +310,7 @@
</script>
<script>
Vue.component("cider-app-sidebar", {
template: "#cider-app-sidebar"
})
</script>
Vue.component("cider-app-sidebar", {
template: "#cider-app-sidebar"
})
</script>

View file

@ -15,123 +15,123 @@
<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="<%- (env.dev ? " stylesheet" : "stylesheet/less" ) %>" type="text/css" href="style.<%- (env.dev ? "css"
: "less" ) %>"/>
<!-- <link rel="stylesheet/less" type="text/css" id="userTheme" href="themes/default.less"/>-->
<script src="./lib/less.js"></script>
<script src="<%- (env.dev ? " ./lib/vue.js" : "./lib/vue.dev.js" ) %>"></script>
<script src="./lib/smoothscroll.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>
<script src="./lib/jquery-3.2.1.slim.min.js"></script>
<script src="./lib/popper.min.js"></script>
<script src="./lib/bootstrap.min.js"></script>
<script src="./lib/bootbox.min.js"></script>
<script src="./lib/notyf.min.js"></script>
<script src="./lib/marked.js"></script>
<script src="./lib/velocity.min.js"></script>
<script src="./lib/fast-plural-rules.js"></script>
<script src="./lib/resonance-audio.min.js"></script>
<script src="./lib/stackblur.min.js"></script>
<link rel="<%- (env.dev ? " stylesheet" : "stylesheet/less") %>" type="text/css" href="style.<%- (env.dev ? "css"
: "less") %>" />
<!-- <link rel="stylesheet/less" type="text/css" id="userTheme" href="themes/default.less"/>-->
<script src="./lib/less.js"></script>
<script src="<%- (env.dev ? " ./lib/vue.js" : "./lib/vue.dev.js"); %>"></script>
<script src="./lib/smoothscroll.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>
<script src="./lib/jquery-3.2.1.slim.min.js"></script>
<script src="./lib/popper.min.js"></script>
<script src="./lib/bootstrap.min.js"></script>
<script src="./lib/bootbox.min.js"></script>
<script src="./lib/notyf.min.js"></script>
<script src="./lib/marked.js"></script>
<script src="./lib/velocity.min.js"></script>
<script src="./lib/fast-plural-rules.js"></script>
<script src="./lib/resonance-audio.min.js"></script>
<script src="./lib/stackblur.min.js"></script>
<style>
#LOADER {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #1E1E1E;
z-index: 99999;
display: flex;
justify-content: center;
align-items: center;
}
<style>
#LOADER {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #1E1E1E;
z-index: 99999;
display: flex;
justify-content: center;
align-items: center;
}
#LOADER>svg {
width: 128px;
}
#LOADER > svg {
width: 128px;
}
@media (prefers-color-scheme: light) {
#LOADER {
background-color: #eee;
}
}
</style>
@media (prefers-color-scheme: light) {
#LOADER {
background-color: #eee;
}
}
</style>
</head>
<body class="notransparency" oncontextmenu="return false;" loading="1" os-release="<%= parseInt(env.osRelease) %>"
platform="<%= env.platform %>">
<script src="<%- (env.dev ? " ./lib/vue.js" : "./lib/vue.dev.js" ) %>"></script>
<script src="./lib/vue-horizontal.js"></script>
<script src="./lib/bootstrap-vue.min.js"></script>
<script src="./lib/vuex.min.js"></script>
<script src="./lib/sortable.min.js"></script>
<script src="./lib/vue-observe-visibility.min.js"></script>
<script src="./lib/vuedraggable.umd.min.js"></script>
<script>
window.quasarConfig = {
brand: {
primary: '#fc3c44'
},
config: {
dark: true
},
loadingBar: { skipHijack: true }
}
</script>
<!-- <script src="./lib/quasar/quasar.umd.min.js"></script> -->
<script type="module" src="./main/app.js"></script>
<body class="notransparency" oncontextmenu="return false;" loading="1" os-release="<%= parseInt(env.osRelease) ;%>"
platform="<%= env.platform ;%>">
<script src="<%- (env.dev ? " ./lib/vue.js" : "./lib/vue.dev.js"); %>"></script>
<script src="./lib/vue-horizontal.js"></script>
<script src="./lib/bootstrap-vue.min.js"></script>
<script src="./lib/vuex.min.js"></script>
<script src="./lib/sortable.min.js"></script>
<script src="./lib/vue-observe-visibility.min.js"></script>
<script src="./lib/vuedraggable.umd.min.js"></script>
<script>
window.quasarConfig = {
brand: {
primary: '#fc3c44'
},
config: {
dark: true
},
loadingBar: { skipHijack: true }
}
</script>
<!-- <script src="./lib/quasar/quasar.umd.min.js"></script> -->
<script type="module" src="./main/app.js"></script>
<div id="LOADER">
<%- include("../assets/cider-round.svg") %>
</div>
<div id="app" :class="getAppClasses()" v-if="appVisible" :window-state="chrome.windowState" :style="getAppStyle()"
:library-visible="(chrome.sidebarCollapsed ? 0 : 1)" :window-style="cfg.visual.directives.windowLayout">
<transition name="fsModeSwitch">
<div id="app-main" v-show="appMode == 'player'">
<%- include('app/chrome-top'); %>
<%- include('app/app-navigation'); %>
<%- include('app/chrome-bottom'); %>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'fullscreen'">
<fullscreen-view :image="currentArtUrlRaw" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics"
:richlyrics="richlyrics"></fullscreen-view>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'mini'">
<mini-view :image="currentArtUrlRaw" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics"
:richlyrics="richlyrics">
</mini-view>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container oobe" v-if="appMode == 'oobe'">
<cider-oobe></cider-oobe>
</div>
</transition>
<%- include('app/panels'); %>
<div class="cursor" v-if="chrome.showCursor"></div>
</div>
<div id="LOADER">
<%- include("../assets/cider-round.svg") %>
</div>
<div id="app" :class="getAppClasses()" v-if="appVisible" :window-state="chrome.windowState" :style="getAppStyle()"
:library-visible="(chrome.sidebarCollapsed ? 0 : 1)" :window-style="cfg.visual.directives.windowLayout">
<transition name="fsModeSwitch">
<div id="app-main" v-show="appMode == 'player'">
<%- include('app/chrome-top'); %>
<%- include('app/app-navigation'); %>
<%- include('app/chrome-bottom'); %>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'fullscreen'">
<fullscreen-view :image="currentArtUrlRaw" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics"
:richlyrics="richlyrics"></fullscreen-view>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'mini'">
<mini-view :image="currentArtUrlRaw" :time="mk.currentPlaybackTime - lyricOffset" :lyrics="lyrics"
:richlyrics="richlyrics">
</mini-view>
</div>
</transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container oobe" v-if="appMode == 'oobe'">
<cider-oobe></cider-oobe>
</div>
</transition>
<%- include('app/panels'); %>
<div class="cursor" v-if="chrome.showCursor"></div>
</div>
<% for(var i=0; i < Object.keys(env.components).length ; i++) { %>
<%- include(env.components[i]); %>
<% } %>
<% for(var i = 0; i < Object.keys(env.components).length ; i++) { %>
<%- include(env.components[i]); %>
<% } %>
<script async
src="<%- (env.useV3 ? 'https://js-cdn.music.apple.com/musickit/v3/amp/musickit.js' : 'https://api.cider.sh/musickit.js') %>"
data-web-components>
</script>
<script src="index.js?v=1"></script>
<script async
src="<%- (env.useV3 ? "https://js-cdn.music.apple.com/musickit/v3/amp/musickit.js" : "https://api.cider.sh/musickit.js"); %>"
data-web-components>
</script>
<script src="index.js?v=1"></script>
<script type="text/x-template" id="am-musiccovershelf">
<script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
</body>
</html>
</html>

View file

@ -6,19 +6,33 @@
<p style="text-align: center" id="version">{{ $root.getLz("term.version") }} {{ $root.version }}</p>
<p style="text-align: center"> {{$root.getLz('about.thanks')}} </p>
<p style="text-align: center">"{{$root.getLz('term.appleMusic')}}" - {{$root.getLz('term.copyright')}} © 2022 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">{{$root.getLz('term.appleInc')}}</a>
{{$root.getLz('term.rightsReserved')}}</p>
<p style="text-align: center">"{{$root.getLz('term.appleMusic')}}" - {{$root.getLz('term.copyright')}} ©
2022 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">{{$root.getLz('term.appleInc')}}</a>
{{$root.getLz('term.rightsReserved')}}</p>
<hr>
<h3>{{$root.getLz('term.sponsor')}}</h3>
<button onclick="window.open('https://github.com/sponsors/ciderapp')" class="md-btn sponsorBtn githubBtn"><img src="./assets/github.svg"/>GitHub Sponsors</button>
<button onclick="window.open('https://ko-fi.com/cryptofyre')" class="md-btn sponsorBtn kofiBtn"><img src="./assets/ko_fi.svg"/>Ko-fi</button>
<button onclick="window.open('https://opencollective.com/ciderapp')" class="md-btn sponsorBtn opencollectiveBtn"><img src="./assets/open_collective.svg"/>Open Collective</button>
<button onclick="window.open('https://github.com/sponsors/ciderapp')"
class="md-btn sponsorBtn githubBtn"><img src="./assets/github.svg" />GitHub Sponsors
</button>
<button onclick="window.open('https://ko-fi.com/cryptofyre')" class="md-btn sponsorBtn kofiBtn"><img
src="./assets/ko_fi.svg" />Ko-fi
</button>
<button onclick="window.open('https://opencollective.com/ciderapp')"
class="md-btn sponsorBtn opencollectiveBtn"><img src="./assets/open_collective.svg" />Open
Collective
</button>
<h3>{{$root.getLz('term.socials')}}</h3>
<button onclick="window.open('https://github.com/ciderapp/Cider')" class="md-btn sponsorBtn githubBtn"><img src="./assets/github.svg"/>{{$root.getLz('term.github')}}</button>
<button onclick="window.open('https://discord.gg/applemusic')" class="md-btn sponsorBtn discordBtn"><img style="height: 26px;" src="./assets/discord.svg"/>{{$root.getLz('term.discord')}}</button>
<button onclick="window.open('https://twitter.com/UseCider')" class="md-btn sponsorBtn twitterBtn"><img src="./assets/twitter.svg"/>Twitter</button>
<button onclick="window.open('https://github.com/ciderapp/Cider')" class="md-btn sponsorBtn githubBtn">
<img src="./assets/github.svg" />{{$root.getLz('term.github')}}
</button>
<button onclick="window.open('https://discord.gg/applemusic')" class="md-btn sponsorBtn discordBtn"><img
style="height: 26px;" src="./assets/discord.svg" />{{$root.getLz('term.discord')}}
</button>
<button onclick="window.open('https://twitter.com/UseCider')" class="md-btn sponsorBtn twitterBtn"><img
src="./assets/twitter.svg" />Twitter
</button>
</div>
<div class="col">
@ -26,14 +40,18 @@
<div class="col">
<h3>{{$root.getLz('term.ciderTeam')}}</h3>
<div class="md-btn teamBtn" v-for="member in team" @click="window.open(member.link)">
<img :src="member.avatar"/>
<img :src="member.avatar" />
<div class="row" style="width:100%;">
<div class="col" style="text-align: left;">
{{ member.name }}
</div>
<div class="col" style="text-align: right">
<button @click.stop="window.open(member.twitter)" class="social-btn" v-if="member.twitter">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="white" style=""><path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"></path></svg>
<button @click.stop="window.open(member.twitter)" class="social-btn"
v-if="member.twitter">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="white" style="">
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"></path>
</svg>
</button>
</div>
<div class="col-auto">
@ -51,105 +69,103 @@
<img class="md-contributors"
style="cursor:pointer;width:100%;"
onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
src="https://contrib.rocks/image?repo=ciderapp/Cider&columns=25"/>
src="https://contrib.rocks/image?repo=ciderapp/Cider&columns=25" />
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('about-page', {
template: '#about-page',
data: function () {
return {
window: window,
version: app.version,
team: [
{
name: 'cryptofyre',
link: 'https://github.com/cryptofyre',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/33162551?v=4',
twitter: 'https://twitter.com/cryptofyre'
},
{
name: 'Core',
link: 'https://github.com/coredev-uk',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/64542347?v=4',
twitter: 'https://twitter.com/core_hdd'
},
{
name: 'Quacksire',
link: 'https://github.com/quacksire',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/19170969?v=4',
twitter: 'https://twitter.com/duckdoquack'
},
{
name: 'booploops',
link: 'https://github.com/booploops',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/49113086?v=4',
twitter: 'https://twitter.com/boopl00ps'
},
{
name: 'vapormusic',
link: 'https://github.com/vapormusic',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/27716185?v=4'
},
{
name: 'crypticplank',
link: 'https://github.com/crypticplank',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/52553007?v=4',
twitter: 'https://twitter.com/crypticplank'
},
{
name: 'Maikiwi',
link: 'https://github.com/maikirakiwi',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/74925636?v=4',
twitter: 'https://twitter.com/notmaikiwi'
},
{
name: 'yazninja',
link: 'https://github.com/yazninja',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/71800112?v=4',
twitter: 'https://twitter.com/YazNinjaa'
},
{
name: 'GamingLiamStudios',
link: 'https://github.com/GamingLiamStudios',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/58615717?v=4',
twitter: 'https://twitter.com/GLStudios_'
},
{
name: 'Amaru',
link: 'https://github.com/Amaru8',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/52407090?v=4'
},
{
name: 'Void',
link: 'https://twitter.com/MoonyVoid',
role: app.getLz('term.socialTeam'),
avatar: 'https://pbs.twimg.com/profile_images/1226463559472816129/8LScNYED_400x400.jpg'
},
{
name: 'NoseySG',
link: 'https://twitter.com/noah_grose',
role: app.getLz('term.socialTeam'),
avatar: 'https://pbs.twimg.com/profile_images/1496944907260420099/D5gl6H4J_400x400.jpg'
}
]
}
},
methods: {
}
});
</script>
Vue.component('about-page', {
template: '#about-page',
data: function() {
return {
window: window,
version: app.version,
team: [
{
name: 'cryptofyre',
link: 'https://github.com/cryptofyre',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/33162551?v=4',
twitter: 'https://twitter.com/cryptofyre'
},
{
name: 'Core',
link: 'https://github.com/coredev-uk',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/64542347?v=4',
twitter: 'https://twitter.com/core_hdd'
},
{
name: 'Quacksire',
link: 'https://github.com/quacksire',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/19170969?v=4',
twitter: 'https://twitter.com/duckdoquack'
},
{
name: 'booploops',
link: 'https://github.com/booploops',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/49113086?v=4',
twitter: 'https://twitter.com/boopl00ps'
},
{
name: 'vapormusic',
link: 'https://github.com/vapormusic',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/27716185?v=4'
},
{
name: 'crypticplank',
link: 'https://github.com/crypticplank',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/52553007?v=4',
twitter: 'https://twitter.com/crypticplank'
},
{
name: 'Maikiwi',
link: 'https://github.com/maikirakiwi',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/74925636?v=4',
twitter: 'https://twitter.com/notmaikiwi'
},
{
name: 'yazninja',
link: 'https://github.com/yazninja',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/71800112?v=4',
twitter: 'https://twitter.com/YazNinjaa'
},
{
name: 'GamingLiamStudios',
link: 'https://github.com/GamingLiamStudios',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/58615717?v=4',
twitter: 'https://twitter.com/GLStudios_'
},
{
name: 'Amaru',
link: 'https://github.com/Amaru8',
role: app.getLz('term.developer'),
avatar: 'https://avatars.githubusercontent.com/u/52407090?v=4'
},
{
name: 'Void',
link: 'https://twitter.com/MoonyVoid',
role: app.getLz('term.socialTeam'),
avatar: 'https://pbs.twimg.com/profile_images/1226463559472816129/8LScNYED_400x400.jpg'
},
{
name: 'NoseySG',
link: 'https://twitter.com/noah_grose',
role: app.getLz('term.socialTeam'),
avatar: 'https://pbs.twimg.com/profile_images/1496944907260420099/D5gl6H4J_400x400.jpg'
}
]
}
},
methods: {}
});
</script>

View file

@ -1,18 +1,18 @@
<script type="text/x-template" id="apple-account-settings">
<div style="display:flex;width:100%;height:100%;padding-top: var(--navigationBarHeight);position:absolute;top:0;left:0;">
<webview id="foo" src="https://music.apple.com/includes/commerce/account/settings?product=music&isFullscreen=true&isModal=false" style="display:inline-flex; width:100%;"></webview>
<webview id="foo"
src="https://music.apple.com/includes/commerce/account/settings?product=music&isFullscreen=true&isModal=false"
style="display:inline-flex; width:100%;"></webview>
</div>
</script>
<script>
Vue.component('apple-account-settings', {
template: '#apple-account-settings',
mounted() {
document.querySelector("#foo").addEventListener("dom-ready", ()=>{
// document.querySelector("#foo").executeJavaScript(`document.body.innerHTML += ("<style>.header {display: none!important;} </style>")`)
})
},
methods: {
}
});
</script>
Vue.component('apple-account-settings', {
template: '#apple-account-settings',
mounted() {
document.querySelector("#foo").addEventListener("dom-ready", () => {
// document.querySelector("#foo").executeJavaScript(`document.body.innerHTML += ("<style>.header {display: none!important;} </style>")`)
})
},
methods: {}
});
</script>

View file

@ -2,41 +2,47 @@
<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 cider-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')" >{{app.getLz('term.seeAll')}}</button>
</div>
<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>
<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>
<div class="col-auto cider-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')">
{{app.getLz('term.seeAll')}}
</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>
</template>
</div>
</script>
<script>
Vue.component('cider-applecurator', {
template: "#cider-applecurator",
props: ["data"],
data: function() {
return {
app: this.$root
}
}
})
</script>
Vue.component('cider-applecurator', {
template: "#cider-applecurator",
props: ["data"],
data: function() {
return {
app: this.$root
}
}
})
</script>

View file

@ -8,7 +8,9 @@
<h3>{{app.getLz('home.followedArtists')}}</h3>
</div>
<div class="col-auto nopadding cider-flex-center">
<button class="cd-btn-seeall" @click="syncFavorites()" v-if="!syncingFavs">{{app.getLz('home.syncFavorites')}}</button>
<button class="cd-btn-seeall" @click="syncFavorites()" v-if="!syncingFavs">
{{app.getLz('home.syncFavorites')}}
</button>
<div class="spinner" style="height: 26px;" v-else></div>
</div>
</div>
@ -17,8 +19,10 @@
<mediaitem-square :item="artist" kind="small"></mediaitem-square>
<button @click="unfollow(artist.id)" class="md-btn md-btn-glyph" style="display:flex;">
<div class="sidebar-icon">
<div class="svg-icon" :style="{'--url': 'url(./assets/feather/x-circle.svg)'}"></div>
</div> {{app.getLz('action.removeFavorite')}}
<div class="svg-icon"
:style="{'--url': 'url(./assets/feather/x-circle.svg)'}"></div>
</div>
{{app.getLz('action.removeFavorite')}}
</button>
</div>
</vue-horizontal>
@ -50,80 +54,81 @@
</script>
<script>
Vue.component('cider-artist-feed', {
template: '#cider-artist-feed',
data: function () {
return {
app: this.$root,
followedArtists: this.$root.cfg.home.followedArtists,
artistFeed: [],
artists: [],
syncingFavs: false
}
},
async mounted() {
let self = this
await this.getArtistFeed()
},
methods: {
async syncFavorites() {
this.syncingFavs = true
await app.syncFavorites()
await this.getArtistFeed()
this.syncingFavs = false
},
async unfollow(id) {
let index = this.followedArtists.indexOf(id)
if (index > -1) {
this.followedArtists.splice(index, 1)
}
let artist = this.artists.find(a => a.id == id)
let index2 = this.artists.indexOf(artist)
if (index2 > -1) {
this.artists.splice(index2, 1)
}
await app.mk.api.v3.music(`/v1/me/favorites`, {
"art[url]": "f",
"ids[artists]": id,
"l": app.mklang,
"platform": "web"
}, {
fetchOptions: {
method: "DELETE"
}
})
this.getArtistFeed()
},
async getArtistFeed() {
let artists = this.followedArtists
let self = this
this.artists = []
this.artistFeed = []
// Apple limits the number of IDs we can provide in a single API call to 50.
// Divide it into groups of 50 and send parallel requests
let chunks = []
for (let artistIdx = 0; artistIdx < artists.length; artistIdx += 50) {
chunks.push(artists.slice(artistIdx, artistIdx + 50))
}
try {
const chunkArtistData = await Promise.all(chunks.map(chunk =>
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${chunk.toString()}&views=latest-release&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=2&art[url]=f`)))
chunkArtistData.forEach(chunkResult =>
chunkResult.data.data.forEach(item => {
self.artists.push(item)
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])
}
}))
// sort artistFeed by attributes.releaseDate descending, date is formatted as "YYYY-MM-DD"
this.artistFeed.sort((a, b) => {
let dateA = new Date(a.attributes.releaseDate)
let dateB = new Date(b.attributes.releaseDate)
return dateB - dateA
})
} catch (err) { }
}
Vue.component('cider-artist-feed', {
template: '#cider-artist-feed',
data: function() {
return {
app: this.$root,
followedArtists: this.$root.cfg.home.followedArtists,
artistFeed: [],
artists: [],
syncingFavs: false
}
},
async mounted() {
let self = this
await this.getArtistFeed()
},
methods: {
async syncFavorites() {
this.syncingFavs = true
await app.syncFavorites()
await this.getArtistFeed()
this.syncingFavs = false
},
async unfollow(id) {
let index = this.followedArtists.indexOf(id)
if (index > -1) {
this.followedArtists.splice(index, 1)
}
});
</script>
let artist = this.artists.find(a => a.id == id)
let index2 = this.artists.indexOf(artist)
if (index2 > -1) {
this.artists.splice(index2, 1)
}
await app.mk.api.v3.music(`/v1/me/favorites`, {
"art[url]": "f",
"ids[artists]": id,
"l": app.mklang,
"platform": "web"
}, {
fetchOptions: {
method: "DELETE"
}
})
this.getArtistFeed()
},
async getArtistFeed() {
let artists = this.followedArtists
let self = this
this.artists = []
this.artistFeed = []
// Apple limits the number of IDs we can provide in a single API call to 50.
// Divide it into groups of 50 and send parallel requests
let chunks = []
for (let artistIdx = 0; artistIdx < artists.length; artistIdx += 50) {
chunks.push(artists.slice(artistIdx, artistIdx + 50))
}
try {
const chunkArtistData = await Promise.all(chunks.map(chunk =>
this.app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists?ids=${chunk.toString()}&views=latest-release&include[songs]=albums&fields[albums]=artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialVideo,name,playParams,releaseDate,url,trackCount&limit[artists:top-songs]=2&art[url]=f`)))
chunkArtistData.forEach(chunkResult =>
chunkResult.data.data.forEach(item => {
self.artists.push(item)
if (item.views["latest-release"].data.length != 0) {
self.artistFeed.push(item.views["latest-release"].data[0])
}
}))
// sort artistFeed by attributes.releaseDate descending, date is formatted as "YYYY-MM-DD"
this.artistFeed.sort((a, b) => {
let dateA = new Date(a.attributes.releaseDate)
let dateB = new Date(b.attributes.releaseDate)
return dateB - dateA
})
} catch (err) {
}
}
}
});
</script>

View file

@ -1,7 +1,8 @@
<script type="text/x-template" id="cider-artist">
<div class="content-inner artist-page"
:class="[(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9) || hasHero()) ? 'animated' : '']">
<div :class="['artist-header', { 'artist-header-compact': app.cfg.visual.compactArtistHeader }]" :key="data.id" v-observe-visibility="{callback: isHeaderVisible}">
<div :class="['artist-header', { 'artist-header-compact': app.cfg.visual.compactArtistHeader }]" :key="data.id"
v-observe-visibility="{callback: isHeaderVisible}">
<animatedartwork-view
:priority="true"
v-if="hasAnimated()"
@ -26,7 +27,7 @@
<div class="col cider-flex-center artist-title"
:class="{'artist-animation-on': (data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) || hasHero() }"
:style="{ 'color': '#' +hasHeroObject()?.textColor1 ?? ''}"
>
>
<button class="artist-play" @click="app.mk.setStationQueue({artist:'a-'+data.id}).then(()=>{
app.mk.play()
})" :aria-label="app.getLz('term.play')"><%- include("../svg/play.svg") %></button>
@ -42,10 +43,10 @@
<div class="svg-icon"></div>
</button>
</div>
<div class="artworkContainer"
<div class="artworkContainer"
v-if="!(data.attributes.editorialVideo && (data.attributes.editorialVideo.motionArtistWide16x9 || data.attributes.editorialVideo.motionArtistFullscreen16x9)) && !hasHero()">
<artwork-material :url="data.attributes.artwork.url" size="190" images="1"></artwork-material>
</div>
<artwork-material :url="data.attributes.artwork.url" size="190" images="1"></artwork-material>
</div>
<div class="artist-hero" v-if="hasHero() && !hasAnimated()">
<mediaitem-artwork shadow="none" :url="hasHero()" size="2048" />
</div>
@ -157,133 +158,133 @@
</div>
</script>
<script>
Vue.component('cider-artist', {
template: "#cider-artist",
props: ['data'],
data: function () {
return {
topSongsExpanded: false,
app: this.$root,
headerVisible: true
}
},
methods: {
hasAnimated() {
if(this.data.attributes?.editorialVideo && (this.data.attributes?.editorialVideo?.motionArtistWide16x9 || this.data.attributes?.editorialVideo?.motionArtistFullscreen16x9)) {
return true;
}
return false;
},
hasHero() {
if(this.data.attributes?.editorialArtwork?.centeredFullscreenBackground){
return this.data.attributes?.editorialArtwork?.centeredFullscreenBackground.url
} else if(this.data.attributes?.editorialArtwork?.bannerUber) {
return this.data.attributes?.editorialArtwork?.bannerUber.url
} else if(this.data.attributes?.editorialArtwork?.subscriptionHero){
return this.data.attributes?.editorialArtwork?.subscriptionHero.url
}
return false;
},
hasHeroObject() {
if(this.data.attributes?.editorialArtwork?.centeredFullscreenBackground){
return this.data.attributes?.editorialArtwork?.centeredFullscreenBackground
} else if(this.data.attributes?.editorialArtwork?.bannerUber) {
return this.data.attributes?.editorialArtwork?.bannerUber
} else if(this.data.attributes?.editorialArtwork?.subscriptionHero){
return this.data.attributes?.editorialArtwork?.subscriptionHero
}
return [];
},
isHeaderVisible(visible) {
this.headerVisible = visible
},
async artistMenu(event) {
let self = this
let followAction = "follow"
let followActions = {
follow: {
icon: "./assets/feather/plus-circle.svg",
name: app.getLz('action.follow'),
action: () => {
self.app.cfg.home.followedArtists.push(self.data.id)
}
},
unfollow: {
icon: "./assets/feather/x-circle.svg",
name: app.getLz('action.unfollow'),
action: () => {
let index = self.app.cfg.home.followedArtists.indexOf(self.data.id)
if (index > -1) {
self.app.cfg.home.followedArtists.splice(index, 1)
}
}
}
}
let favoriteActions = {
favorite: {
icon: "./assets/star.svg",
name: app.getLz('action.favorite'),
action: () => {
app.setArtistFavorite(app.artistPage.data.id, true)
}
},
removeFavorite: {
icon: "./assets/star.svg",
name: app.getLz('action.removeFavorite'),
action: () => {
app.setArtistFavorite(app.artistPage.data.id, false)
}
}
}
if (this.app.cfg.home.followedArtists.includes(self.data.id)) {
followAction = "unfollow"
}
const inFavorites = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${app.artistPage.data.id}`, {
"fields[artists]": "inFavorites"
})).data.data[0].attributes?.inFavorites
app.showMenuPanel({
items: [
{
icon: "./assets/feather/play.svg",
name: app.getLz('action.startRadio'),
action: () => {
app.mk.setStationQueue({artist: self.data.id}).then(() => {
app.mk.play()
})
}
},
favoriteActions[inFavorites ? "removeFavorite" : "favorite"],
// followActions[followAction],
{
icon: "./assets/feather/share.svg",
name: app.getLz('term.share'),
action: () => {
self.app.copyToClipboard(self.data.attributes.url)
}
}
]
}, event)
},
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;
}
}
Vue.component('cider-artist', {
template: "#cider-artist",
props: ['data'],
data: function() {
return {
topSongsExpanded: false,
app: this.$root,
headerVisible: true
}
},
methods: {
hasAnimated() {
if (this.data.attributes?.editorialVideo && (this.data.attributes?.editorialVideo?.motionArtistWide16x9 || this.data.attributes?.editorialVideo?.motionArtistFullscreen16x9)) {
return true;
}
})
</script>
return false;
},
hasHero() {
if (this.data.attributes?.editorialArtwork?.centeredFullscreenBackground) {
return this.data.attributes?.editorialArtwork?.centeredFullscreenBackground.url
} else if (this.data.attributes?.editorialArtwork?.bannerUber) {
return this.data.attributes?.editorialArtwork?.bannerUber.url
} else if (this.data.attributes?.editorialArtwork?.subscriptionHero) {
return this.data.attributes?.editorialArtwork?.subscriptionHero.url
}
return false;
},
hasHeroObject() {
if (this.data.attributes?.editorialArtwork?.centeredFullscreenBackground) {
return this.data.attributes?.editorialArtwork?.centeredFullscreenBackground
} else if (this.data.attributes?.editorialArtwork?.bannerUber) {
return this.data.attributes?.editorialArtwork?.bannerUber
} else if (this.data.attributes?.editorialArtwork?.subscriptionHero) {
return this.data.attributes?.editorialArtwork?.subscriptionHero
}
return [];
},
isHeaderVisible(visible) {
this.headerVisible = visible
},
async artistMenu(event) {
let self = this
let followAction = "follow"
let followActions = {
follow: {
icon: "./assets/feather/plus-circle.svg",
name: app.getLz('action.follow'),
action: () => {
self.app.cfg.home.followedArtists.push(self.data.id)
}
},
unfollow: {
icon: "./assets/feather/x-circle.svg",
name: app.getLz('action.unfollow'),
action: () => {
let index = self.app.cfg.home.followedArtists.indexOf(self.data.id)
if (index > -1) {
self.app.cfg.home.followedArtists.splice(index, 1)
}
}
}
}
let favoriteActions = {
favorite: {
icon: "./assets/star.svg",
name: app.getLz('action.favorite'),
action: () => {
app.setArtistFavorite(app.artistPage.data.id, true)
}
},
removeFavorite: {
icon: "./assets/star.svg",
name: app.getLz('action.removeFavorite'),
action: () => {
app.setArtistFavorite(app.artistPage.data.id, false)
}
}
}
if (this.app.cfg.home.followedArtists.includes(self.data.id)) {
followAction = "unfollow"
}
const inFavorites = (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${app.artistPage.data.id}`, {
"fields[artists]": "inFavorites"
})).data.data[0].attributes?.inFavorites
app.showMenuPanel({
items: [
{
icon: "./assets/feather/play.svg",
name: app.getLz('action.startRadio'),
action: () => {
app.mk.setStationQueue({ artist: self.data.id }).then(() => {
app.mk.play()
})
}
},
favoriteActions[inFavorites ? "removeFavorite" : "favorite"],
// followActions[followAction],
{
icon: "./assets/feather/share.svg",
name: app.getLz('term.share'),
action: () => {
self.app.copyToClipboard(self.data.attributes.url)
}
}
]
}, event)
},
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>

Some files were not shown because too many files have changed in this diff Show more