Merge branch 'main' into main-ts

This commit is contained in:
Core 2022-01-15 13:54:29 +00:00
commit bc56aee137
No known key found for this signature in database
GPG key ID: 1B77805746C47C28
31 changed files with 1832 additions and 734 deletions

View file

@ -26,7 +26,6 @@
"@sentry/electron": "^2.5.4", "@sentry/electron": "^2.5.4",
"discord-rpc": "^4.0.1", "discord-rpc": "^4.0.1",
"ejs": "^3.1.6", "ejs": "^3.1.6",
"electron-acrylic-window": "^0.5.11",
"electron-fetch": "^1.7.4", "electron-fetch": "^1.7.4",
"electron-log": "^4.4.4", "electron-log": "^4.4.4",
"electron-store": "^8.0.1", "electron-store": "^8.0.1",
@ -37,6 +36,7 @@
"lastfmapi": "^0.1.1", "lastfmapi": "^0.1.1",
"mpris-service": "^2.1.2", "mpris-service": "^2.1.2",
"music-metadata": "^7.11.4", "music-metadata": "^7.11.4",
"qrcode-terminal": "^0.12.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",

View file

@ -23260,9 +23260,36 @@
} }
if (null != (l = "string" != typeof (h = g.pathwayID) ? im("invalid steering manifest PATHWAY-PRIORITY list item data type") : /^[\w\-\.]+$/.test(h) ? void 0 : im("steering manifest contains invalid pathway ID: " + h))) if (null != (l = "string" != typeof (h = g.pathwayID) ? im("invalid steering manifest PATHWAY-PRIORITY list item data type") : /^[\w\-\.]+$/.test(h) ? void 0 : im("steering manifest contains invalid pathway ID: " + h)))
break; break;
if (g.hdcpLevel === "NONE"){ let cpc = g.allowedCPCMap ? JSON.stringify(g.allowedCPCMap) : "null";
n.push(g)} if (!cpc.includes("WIDEVINE_HARDWARE") && !g.url.includes('trickPlay') && !g.videoCodec.includes("hvc1"))
n.push(g)
} }
try{
// console.log(n, window.screen.width)
let ok = (n.map( function(item){return{height : item.height, content: item}}));
let screenHeight = (app.cfg.visual.videoRes ?? window.screen.height) ;
ok.sort(function (a, b) {
return a.height - b.height;
});
for (var i = 0; i < ok.length; i++){
if (ok[i].height > screenHeight){
if (i == 0){n.splice(0,n.length);n.push(ok[i].content)}
else{n.splice(0,n.length);n.push(ok[i-1].content)}
console.log('selected' , n[0].height)
break;
}
}
if (n.length > 1){
n.splice(0,n.length - 1);
}
// console.log(n)
// console.log(ok)
} catch (e){ console.log(e)}
return { return {
variantMediaOptions: n, variantMediaOptions: n,
contentSteeringOption: u, contentSteeringOption: u,

View file

@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256px" height="256px" viewBox="0 0 256 256" id="Flat">
<path d="M236,56A108.12186,108.12186,0,0,1,128,164H60.9707l27.51465,27.51465a12.0001,12.0001,0,0,1-16.9707,16.9707l-48-48c-.02954-.02978-.054-.0625-.08325-.09228-.24683-.252-.48536-.51172-.70948-.78467-.12378-.15039-.23169-.30908-.34692-.46387-.118-.15869-.24109-.31348-.35132-.478-.1206-.18017-.22534-.3667-.33508-.55127-.09021-.15185-.18567-.2998-.26917-.45605-.10009-.18652-.18493-.37842-.27429-.56885-.07727-.16455-.15954-.32617-.22961-.49463-.07678-.18554-.139-.375-.20618-.56347-.06482-.18164-.1344-.35987-.19067-.54541-.05725-.189-.09961-.38135-.14734-.57325-.04712-.188-.10022-.374-.13819-.56543-.04406-.22168-.07055-.44482-.102-.668-.02307-.165-.05468-.32666-.07116-.49366a12.08048,12.08048,0,0,1,0-2.373c.01648-.167.04809-.32862.07116-.49366.0315-.22314.058-.44628.102-.668.038-.19141.09107-.37745.13819-.56543.04773-.1919.09009-.38428.14734-.57373.05627-.18506.12585-.36329.19055-.54493.06714-.18847.12939-.37793.2063-.56347.07007-.16846.15234-.33008.22973-.49463.08936-.19043.1742-.38233.27417-.56885.0835-.15625.179-.3042.26917-.45605.10974-.18457.21448-.3711.33508-.55127.11023-.16455.23328-.31934.35132-.478.11523-.15479.22314-.31348.34692-.46387.22412-.273.46265-.53271.70948-.78467.02929-.02978.05371-.0625.08325-.09228l48-48a12.0001,12.0001,0,0,1,16.9707,16.9707L60.9707,140H128a84.09562,84.09562,0,0,0,84-84,12,12,0,0,1,24,0Z"/>
</svg>
<!-- MIT License
Copyright (c) 2020-2021 Phosphor Icons
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. -->

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,25 @@
<svg width="256px" height="256px" viewBox="0 0 256 256" id="Flat" xmlns="http://www.w3.org/2000/svg">
<path d="M236,200a12,12,0,0,1-24,0,84.09562,84.09562,0,0,0-84-84H60.9707l27.51465,27.51465a12.0001,12.0001,0,0,1-16.9707,16.9707l-48-48c-.02954-.02954-.054-.06225-.08325-.092-.24683-.25195-.48536-.51221-.70948-.78491-.12378-.15064-.23169-.30908-.34692-.46411-.118-.15869-.24109-.31324-.35132-.47779-.1206-.17993-.22534-.3667-.33508-.55151-.09021-.15161-.18567-.29956-.26917-.45557-.10009-.18676-.18493-.37866-.27429-.56933-.07727-.16455-.15954-.32593-.22961-.49463-.07678-.1853-.139-.37476-.20618-.56323-.06482-.1814-.1344-.35987-.19067-.54517-.05725-.18945-.09961-.38184-.14734-.57324-.04712-.18848-.10022-.374-.13819-.56592-.04406-.22144-.07055-.44482-.102-.66772-.02307-.165-.05468-.32691-.07116-.49366a12.08048,12.08048,0,0,1,0-2.373c.01648-.16675.04809-.32862.07116-.49366.0315-.2229.058-.44628.102-.66772.038-.1919.09107-.37744.13819-.56592.04773-.1914.09009-.38379.14734-.57324.05627-.1853.12585-.36377.19055-.54492.06714-.18848.12939-.37818.2063-.56372.07007-.16846.15234-.33008.22973-.49463.08936-.19068.1742-.38233.27417-.56909.0835-.156.179-.304.26917-.45557.10974-.18481.21448-.37158.33508-.55151.11023-.16455.23328-.3191.35132-.47779.11523-.155.22314-.31347.34692-.46411.22412-.2727.46265-.533.70948-.78491.02929-.02979.05371-.0625.08325-.092l48-48a12.0001,12.0001,0,0,1,16.9707,16.9707L60.9707,92H128A108.12186,108.12186,0,0,1,236,200Z"/>
</svg>
<!-- MIT License
Copyright (c) 2020-2021 Phosphor Icons
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. -->

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -0,0 +1 @@
<svg fill="none" height="391" viewBox="0 0 391 391" width="391" xmlns="http://www.w3.org/2000/svg"><path d="m52 102h296v199h-296z" fill="#fff"/><path d="m174 1.09995c-79.2999 8.9-145.5999 65.29995-166.59995 141.70005-15.2 55.6-5.8 114 26.10005 161.7 61.7 92.3 189.4999 114.2 278.7999 47.8 74.8-55.8 99.8-156.6 59.7-241-9.7-20.3001-20.4-35.6001-36.4-52.2001-27.2-27.9-61.3-46.7-99.6-54.99995-18.3-3.900005-43.3-5.1-62-3zm117.8 102.90005c25.2 7 43.6 24.6 50.2 48 7.8 28.1-.1 55.6-21.5 73.9-11.5 9.8-32.5 17.1-49.6 17.1h-7.6l-.8 7.2c-1.9 19.4-11.4 32.5-27 37-2.9.9-25.2 1.2-80 1.2l-75.9999.1-6-2.8c-9.6-4.5-16.9-12.7-19.7-22.2-.4-1.6-.8-36.6-.8-77.8 0-80.7 0-80.6 5.2-82.7 1.4-.5 48.5999-.9 114.2999-.9 109.5-.1 112.2-.1 119.3 1.9z" fill="#579fbf"/><path d="m264 173v34h8.8c15.5 0 25-5.3 31.2-17.4 3.3-6.6 3.5-7.4 3.5-17.5 0-9.6-.3-11.1-2.8-16.3-3-6.2-8.6-11.6-15.2-14.8-3.3-1.6-6.2-2-14.8-2h-10.7z" fill="#579fbf"/><path d="m177.885 147.41c-7.3 2.4-9.6 3.7-15.3 8.4l-4.7 3.9-4.3-3.5c-14.2-11.6-35-12.9-46.7-2.9-5.8 5-8.1996 10.3-8.7996 19.4-.5 8.1 1.2 15.8 4.9996 23.2 1.1 2.2 5.9 8 10.6 13 14.7 15.3 42.4 40.7 44.4 40.7 1.2 0 12.1-10.1 27.3-25.3 22.9-22.8 25.7-25.9 28.6-32.1 4.1-8.7 5-18.8 2.5-26.4-4.9-14.6-23.4-23.4-38.6-18.4z" fill="#ff5f5f"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="m34.6194245 8.17880011c2.1314244 3.07072649 3.3805755 6.80008579 3.3805755 10.82119989s-1.2491511 7.7504734-3.3805755 10.8211999l-4.9217466-4.9217466c.9664963-1.748848 1.5166078-3.7599079 1.5166078-5.8994533s-.5501115-4.1506053-1.5166078-5.8994533zm-4.7982246-4.79822459-4.9217466 4.92174663c-1.748848-.96649637-3.7599079-1.51660786-5.8994533-1.51660786-6.7457637 0-12.21428571 5.46852201-12.21428571 12.21428571s5.46852201 12.2142857 12.21428571 12.2142857c2.1395454 0 4.1506053-.5501115 5.8994533-1.5166078l4.9217466 4.9217466c-3.0707265 2.1314244-6.8000858 3.3805755-10.8211999 3.3805755-10.49341025 0-19-8.5065898-19-19 0-10.49341025 8.50658975-19 19-19 4.0211141 0 7.7504734 1.24915112 10.8211999 3.38057552z" fill="#7fadf2"/><path d="m34.6194245 8.17880011c2.1314244 3.07072649 3.3805755 6.80008579 3.3805755 10.82119989s-1.2491511 7.7504734-3.3805755 10.8211999l-4.9217466-4.9217466c.9664963-1.748848 1.5166078-3.7599079 1.5166078-5.8994533s-.5501115-4.1506053-1.5166078-5.8994533z" fill="#b8d3f4"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -52,7 +52,7 @@ var CiderContextMenu = {
} }
item.tabIndex = 0 item.tabIndex = 0
item.classList.add("context-menu-item") item.classList.add("context-menu-item")
if(menudata.items[i]["icon"]) { if (menudata.items[i]["icon"]) {
item.innerHTML += `<div class="sidebar-icon">${await app.getSvgIcon(menudata.items[i]["icon"])}</div>` item.innerHTML += `<div class="sidebar-icon">${await app.getSvgIcon(menudata.items[i]["icon"])}</div>`
} }
item.innerHTML += menudata.items[i].name item.innerHTML += menudata.items[i].name
@ -290,7 +290,18 @@ const app = new Vue({
}, },
socialBadges: { socialBadges: {
badgeMap: {}, badgeMap: {},
version: "" version: "",
mediaItems: [],
mediaItemDLState: 0 // 0 = not started, 1 = in progress, 2 = complete
},
menuPanel: {
visible: false,
event: null,
content: {
name: "",
items: {},
headerItems: {}
}
} }
}, },
watch: { watch: {
@ -315,6 +326,53 @@ const app = new Vue({
}, },
}, },
methods: { methods: {
async showSocialListeningTo() {
let contentIds = Object.keys(app.socialBadges.badgeMap)
app.showCollection({data: this.socialBadges.mediaItems}, "Friends Listening To", "albums")
if(this.socialBadges.mediaItemDLState == 1 || this.socialBadges.mediaItemDLState == 2) {
return
}
this.socialBadges.mediaItemDLState = 2
await asyncForEach(contentIds, async (item)=>{
try {
let type = "albums"
if(item.includes("pl.")) {
type = "playlists"
}
if(item.includes("ra.")) {
type = "stations"
}
let found = await app.mk.api.v3.music(`/v1/catalog/us/${type}/${item}`)
this.socialBadges.mediaItems.push(found.data.data[0])
}catch(e){
}
})
},
async openAppleMusicURL(url) {
let properties = MusicKit.formattedMediaURL(url)
let item = {
id: properties.contentId,
attributes: {
playParams: {
id: properties.contentId,
kind: properties.kind,
}
},
type: properties.kind,
kind: properties.kind
}
app.routeView(item)
},
async showMenuPanel(data, event) {
app.menuPanel.visible = true;
app.menuPanel.content.name = data.name ?? "";
app.menuPanel.content.items = data.items ?? {};
app.menuPanel.content.headerItems = data.headerItems ?? {};
if(event) {
app.menuPanel.event = event;
}
},
async getSvgIcon(url) { async getSvgIcon(url) {
let response = await fetch(url); let response = await fetch(url);
let data = await response.text(); let data = await response.text();
@ -353,22 +411,11 @@ const app = new Vue({
history.forward() history.forward()
}, },
getHTMLStyle() { getHTMLStyle() {
switch (this.cfg.visual.window_transparency) {
case "acrylic":
default:
document.querySelector("html").style.background = "";
document.querySelector("body").style.background = "";
document.querySelector("body").classList.remove("notransparency")
break;
case "disabled":
document.querySelector("html").style.background = "#222"; document.querySelector("html").style.background = "#222";
document.querySelector("body").classList.add("notransparency") document.querySelector("body").classList.add("notransparency")
// document.querySelector("body").style.background = "#222";
break;
}
}, },
resetState() { resetState() {
this.menuPanel.visible = false;
app.selectedMediaItems = []; app.selectedMediaItems = [];
for (let key in app.modals) { for (let key in app.modals) {
app.modals[key] = false; app.modals[key] = false;
@ -387,10 +434,12 @@ const app = new Vue({
id: self.selectedMediaItems[i].id, id: self.selectedMediaItems[i].id,
type: self.selectedMediaItems[i].kind type: self.selectedMediaItems[i].kind
}) })
} else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true ) { } else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true) {
self.selectedMediaItems[i].kind = "albums" self.selectedMediaItems[i].kind = "albums"
let res = await self.mk.api.albumRelationship(self.selectedMediaItems[i].id,"tracks"); let res = await self.mk.api.albumRelationship(self.selectedMediaItems[i].id, "tracks");
let ids = res.map(function(i) {return {id:i.id, type: i.type}}) let ids = res.map(function (i) {
return {id: i.id, type: i.type}
})
pl_items = pl_items.concat(ids) pl_items = pl_items.concat(ids)
} else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") { } else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") {
self.selectedMediaItems[i].kind = "library-songs" self.selectedMediaItems[i].kind = "library-songs"
@ -398,10 +447,12 @@ const app = new Vue({
id: self.selectedMediaItems[i].id, id: self.selectedMediaItems[i].id,
type: self.selectedMediaItems[i].kind type: self.selectedMediaItems[i].kind
}) })
} else if ((self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums") || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true )) { } else if ((self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums") || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true)) {
self.selectedMediaItems[i].kind = "library-albums" self.selectedMediaItems[i].kind = "library-albums"
let res = await self.mk.api.library.albumRelationship(self.selectedMediaItems[i].id,"tracks"); let res = await self.mk.api.library.albumRelationship(self.selectedMediaItems[i].id, "tracks");
let ids = res.map(function(i) {return {id:i.id, type: i.type}}) let ids = res.map(function (i) {
return {id: i.id, type: i.type}
})
pl_items = pl_items.concat(ids) pl_items = pl_items.concat(ids)
} else { } else {
pl_items.push({ pl_items.push({
@ -635,6 +686,11 @@ const app = new Vue({
this.$forceUpdate() this.$forceUpdate()
}, 500) }, 500)
}, },
getAppClasses() {
if (this.cfg.advanced.experiments.includes('compactui')) {
return {compact: true}
}
},
invokeDrawer(panel) { invokeDrawer(panel) {
if (this.drawer.panel == panel && this.drawer.open) { if (this.drawer.panel == panel && this.drawer.open) {
if (panel == "lyrics") { if (panel == "lyrics") {
@ -721,7 +777,7 @@ const app = new Vue({
} }
] ]
} }
CiderContextMenu.Create(event, menu) this.showMenuPanel(menu, event)
}, },
async editPlaylistFolder(id, name = "New Playlist") { async editPlaylistFolder(id, name = "New Playlist") {
let self = this let self = this
@ -886,7 +942,9 @@ const app = new Vue({
"fields[catalog]": "artistUrl,albumUrl", "fields[catalog]": "artistUrl,albumUrl",
"fields[songs]": "artistUrl,albumUrl" "fields[songs]": "artistUrl,albumUrl"
} }
if (!transient) {this.playlists.loadingState = 0;} if (!transient) {
this.playlists.loadingState = 0;
}
let playlistId = '' let playlistId = ''
try { try {
@ -991,6 +1049,7 @@ const app = new Vue({
return hash; return hash;
}, },
appRoute(route) { appRoute(route) {
console.log(route)
if (route == "" || route == "#" || route == "/") { if (route == "" || route == "#" || route == "/") {
return; return;
} }
@ -1004,12 +1063,13 @@ const app = new Vue({
let hash = route.split("/") let hash = route.split("/")
let page = hash[0] let page = hash[0]
let id = hash[1] let id = hash[1]
console.log(`page: ${page} id: ${id}`) let isLibrary = hash[2] ?? false
console.log(`page: ${page} id: ${id} isLibrary: ${isLibrary}`)
this.routeView({ this.routeView({
kind: page, kind: page,
id: id, id: id,
attributes: { attributes: {
playParams: {kind: page, id: id} playParams: {kind: page, id: id, isLibrary: isLibrary}
} }
}) })
}, },
@ -1036,7 +1096,7 @@ const app = new Vue({
document.querySelector("#app-content").scrollTop = 0 document.querySelector("#app-content").scrollTop = 0
} else if (kind.toString().includes("artist")) { } else if (kind.toString().includes("artist")) {
app.getArtistInfo(id, isLibrary) app.getArtistInfo(id, isLibrary)
window.location.hash = `${kind}/${id}` window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
document.querySelector("#app-content").scrollTop = 0 document.querySelector("#app-content").scrollTop = 0
} else if (kind.toString().includes("record-label") || kind.toString().includes("curator")) { } else if (kind.toString().includes("record-label") || kind.toString().includes("curator")) {
@ -1057,7 +1117,7 @@ const app = new Vue({
let params = {extend: "editorialVideo"} let params = {extend: "editorialVideo"}
app.page = (kind) + "_" + (id); app.page = (kind) + "_" + (id);
app.getTypeFromID((kind), (id), (isLibrary), params); app.getTypeFromID((kind), (id), (isLibrary), params);
window.location.hash = `${kind}/${id}` window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
document.querySelector("#app-content").scrollTop = 0 document.querySelector("#app-content").scrollTop = 0
} else { } else {
app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '') app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
@ -1306,13 +1366,14 @@ const app = new Vue({
}, },
searchLibrarySongs() { searchLibrarySongs() {
let self = this let self = this
let prefs = this.cfg.libraryPrefs.songs
function sortSongs() { function sortSongs() {
// sort this.library.songs.displayListing by song.attributes[self.library.songs.sorting] in descending or ascending order based on alphabetical order and numeric order // sort this.library.songs.displayListing by song.attributes[self.library.songs.sorting] in descending or ascending order based on alphabetical order and numeric order
// check if song.attributes[self.library.songs.sorting] is a number and if so, sort by number if not, sort by alphabetical order ignoring case // check if song.attributes[self.library.songs.sorting] is a number and if so, sort by number if not, sort by alphabetical order ignoring case
self.library.songs.displayListing.sort((a, b) => { self.library.songs.displayListing.sort((a, b) => {
let aa = a.attributes[self.library.songs.sorting] let aa = a.attributes[prefs.sort]
let bb = b.attributes[self.library.songs.sorting] let bb = b.attributes[prefs.sort]
if (self.library.songs.sorting == "genre") { if (self.library.songs.sorting == "genre") {
aa = a.attributes.genreNames[0] aa = a.attributes.genreNames[0]
bb = b.attributes.genreNames[0] bb = b.attributes.genreNames[0]
@ -1323,13 +1384,13 @@ const app = new Vue({
if (bb == null) { if (bb == null) {
bb = "" bb = ""
} }
if (self.library.songs.sortOrder == "asc") { if (prefs.sortOrder == "asc") {
if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) { if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
return aa - bb return aa - bb
} else { } else {
return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase()) return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase())
} }
} else if (self.library.songs.sortOrder == "desc") { } else if (prefs.sortOrder == "desc") {
if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) { if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
return bb - aa return bb - aa
} else { } else {
@ -1572,7 +1633,8 @@ const app = new Vue({
if (downloaded.next != null && typeof downloaded.next === "function") { if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", params, {includeResponseMeta: !0}).then((response) => { downloaded.next("", params, {includeResponseMeta: !0}).then((response) => {
processChunk(response) processChunk(response)
}) } else { })
} else {
console.log("Download next", downloaded.next) console.log("Download next", downloaded.next)
} }
} }
@ -1651,7 +1713,8 @@ const app = new Vue({
if (downloaded.next != null && typeof downloaded.next === "function") { if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", params, {includeResponseMeta: !0}).then((response) => { downloaded.next("", params, {includeResponseMeta: !0}).then((response) => {
processChunk(response) processChunk(response)
}) } else { })
} else {
console.log("Download next", downloaded.next) console.log("Download next", downloaded.next)
} }
} }
@ -1731,7 +1794,8 @@ const app = new Vue({
if (downloaded.next != null && typeof downloaded.next === "function") { if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", "artists", {includeResponseMeta: !0}).then((response) => { downloaded.next("", "artists", {includeResponseMeta: !0}).then((response) => {
processChunk(response) processChunk(response)
}) } else { })
} else {
console.log("Download next", downloaded.next) console.log("Download next", downloaded.next)
} }
@ -1780,7 +1844,7 @@ const app = new Vue({
let hours = Math.floor(time / 3600) let hours = Math.floor(time / 3600)
let mins = Math.floor(time / 60) % 60 let mins = Math.floor(time / 60) % 60
let secs = time % 60 let secs = time % 60
return app.showingPlaylist.relationships.tracks.data.length + " tracks, " + ((hours > 0) ? (hours + (" hour" + ((hours > 1) ? "s, " : ", "))) : "") + ((mins > 0) ? (mins + (" minute" + ((mins > 1) ? "s, " : ", "))) : "") + secs + (" second" + ((secs > 1) ? "s." : ".")); return app.showingPlaylist.relationships.tracks.data.length + " track" + (app.showingPlaylist.relationships.tracks.data.length > 1 ? "s" : "") + ", " + ((hours > 0) ? (hours + (" hour" + ((hours > 1) ? "s, " : ", "))) : "") + ((mins > 0) ? (mins + (" minute" + ((mins > 1) ? "s, " : ", "))) : "") + secs + (" second" + ((secs > 1) ? "s." : "."));
} else return "" } else return ""
} catch (err) { } catch (err) {
return "" return ""
@ -2354,25 +2418,27 @@ const app = new Vue({
array[j] = temp; array[j] = temp;
} }
} }
let kind = parent.substring(0, parent.indexOf(":")) let kind = parent.substring(0, parent.indexOf(":"))
let id = parent.substring(parent.indexOf(":") + 1, parent.length) let id = parent.substring(parent.indexOf(":") + 1, parent.length)
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
console.log(truekind, id) console.log(truekind, id)
try { try {
if (app.library.songs.listing.length > childIndex && parent == "librarysongs") { if (app.library.songs.displayListing.length > childIndex && parent == "librarysongs") {
console.log(item) console.log(item)
if (item && ((app.library.songs.listing[childIndex].id != item.id))) { if (item && ((app.library.songs.displayListing[childIndex].id != item.id))) {
childIndex = app.library.songs.listing.indexOf(item) childIndex = app.library.songs.displayListing.indexOf(item)
} }
let query = app.library.songs.listing.map(item => new MusicKit.MediaItem(item)); let query = app.library.songs.displayListing.map(item => new MusicKit.MediaItem(item));
try {
app.mk.stop()
} catch (e) { app.mk.stop().then(() => {
}
this.mk.clearQueue().then(function (_) { this.mk.clearQueue().then(function (_) {
if (app.mk.shuffleMode == 1){ shuffleArray(query)} if (app.mk.shuffleMode == 1) {
shuffleArray(query)
}
app.mk.queue.append(query) app.mk.queue.append(query)
if (childIndex != -1) { if (childIndex != -1) {
app.mk.changeToMediaAtIndex(childIndex) app.mk.changeToMediaAtIndex(childIndex)
@ -2385,29 +2451,33 @@ const app = new Vue({
app.mk.play() app.mk.play()
} }
}) })
})
} else { } else {
app.mk.stop().then(() => { app.mk.stop().then(() => {
if (truekind == "playlists" && (id.startsWith("p.") || id.startsWith("pl.u"))){ if (truekind == "playlists" && (id.startsWith("p.") || id.startsWith("pl.u"))) {
app.mk.setQueue({[item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id}).then(function () { app.mk.setQueue({[item.attributes.playParams.kind ?? item.type]: item.attributes.playParams.id ?? item.id}).then(function () {
app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1).then(function(){ app.mk.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1).then(function () {
if ((app.showingPlaylist && app.showingPlaylist.id == id)) { if ((app.showingPlaylist && app.showingPlaylist.id == id)) {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item)); let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (query.length > 100) { let u = query;
let u = query.slice(100); if (app.mk.shuffleMode == 1) { shuffleArray(u) } if (app.mk.shuffleMode == 1) {
app.mk.queue.append(u)} shuffleArray(u)
}
app.mk.queue.append(u)
} else { } else {
app.getPlaylistFromID(id, true).then(function () { app.getPlaylistFromID(id, true).then(function () {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item)); let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (query.length > 100) { let u = query;
let u = query.slice(100); if (app.mk.shuffleMode == 1) { shuffleArray(u) } if (app.mk.shuffleMode == 1) {
app.mk.queue.append(u)} shuffleArray(u)
}
app.mk.queue.append(u)
}) })
} }
}) })
}) })
} } else {
else{
this.mk.setQueue({[truekind]: [id]}).then(function (queue) { this.mk.setQueue({[truekind]: [id]}).then(function (queue) {
if (item && ((queue._itemIDs[childIndex] != item.id))) { if (item && ((queue._itemIDs[childIndex] != item.id))) {
childIndex = queue._itemIDs.indexOf(item.id) childIndex = queue._itemIDs.indexOf(item.id)
@ -2422,8 +2492,10 @@ const app = new Vue({
} else { } else {
app.mk.play() app.mk.play()
} }
})} })
})} }
})
}
} catch (err) { } catch (err) {
console.log(err) console.log(err)
try { try {
@ -2524,7 +2596,8 @@ const app = new Vue({
if (type.slice(-1) != "s") { if (type.slice(-1) != "s") {
type += "s" type += "s"
} }
let id = item.playParams.catalogId ? item.playParams.catalogId : item.id type = type.replace("library-", "")
let id = item.attributes.playParams.catalogId ?? item.id
let index = types.findIndex(function (type) { let index = types.findIndex(function (type) {
return type.type == this return type.type == this
@ -2535,7 +2608,11 @@ const app = new Vue({
types[index].id.push(id) types[index].id.push(id)
} }
} }
return await this.mk.api.catalogResources(types, {"omit[resource]": "autos", relate: "library", fields: "inLibrary"}) return await this.mk.api.catalogResources(types, {
"omit[resource]": "autos",
relate: "library",
fields: "inLibrary"
})
}, },
isInLibrary(playParams) { isInLibrary(playParams) {
let self = this let self = this
@ -2572,7 +2649,7 @@ const app = new Vue({
} }
}, },
getMediaItemArtwork(url, height = 64, width) { getMediaItemArtwork(url, height = 64, width) {
if(typeof url == "undefined" || url == "") { if (typeof url == "undefined" || url == "") {
return "https://beta.music.apple.com/assets/product/MissingArtworkMusic.svg" return "https://beta.music.apple.com/assets/product/MissingArtworkMusic.svg"
} }
let newurl = `${url.replace('{w}', width ?? height).replace('{h}', height).replace('{f}', "webp").replace('{c}', ((width === 900) ? "sr" : "cc"))}`; let newurl = `${url.replace('{w}', width ?? height).replace('{h}', height).replace('{f}', "webp").replace('{c}', ((width === 900) ? "sr" : "cc"))}`;
@ -2582,7 +2659,7 @@ const app = new Vue({
} }
return newurl return newurl
}, },
_rgbToRgb(rgb = [0,0,0]) { _rgbToRgb(rgb = [0, 0, 0]) {
// if rgb // if rgb
return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})` return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`
}, },
@ -2602,7 +2679,7 @@ const app = new Vue({
this.currentTrackID = this.mk.nowPlayingItem["id"]; this.currentTrackID = this.mk.nowPlayingItem["id"];
document.querySelector('.bg-artwork').src = ""; document.querySelector('.bg-artwork').src = "";
if (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"]) { if (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"]) {
getBase64FromUrl(this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"].replace('{w}', size).replace('{h}', size)).then(img =>{ getBase64FromUrl(this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"].replace('{w}', size).replace('{h}', size)).then(img => {
document.querySelectorAll('.bg-artwork').forEach(artwork => { document.querySelectorAll('.bg-artwork').forEach(artwork => {
artwork.src = img; artwork.src = img;
}) })
@ -2690,29 +2767,32 @@ const app = new Vue({
// }, // },
async getCurrentArtURL(){ async getCurrentArtURL() {
try{ try {
this.currentArtUrl = ''; this.currentArtUrl = '';
if (app.mk.nowPlayingItem != null && app.mk.nowPlayingItem.attributes != null && app.mk.nowPlayingItem.attributes.artwork != null && app.mk.nowPlayingItem.attributes.artwork.url != null && app.mk.nowPlayingItem.attributes.artwork.url!= '' ) if (app.mk.nowPlayingItem != null && app.mk.nowPlayingItem.attributes != null && app.mk.nowPlayingItem.attributes.artwork != null && app.mk.nowPlayingItem.attributes.artwork.url != null && app.mk.nowPlayingItem.attributes.artwork.url != '') {
{
this.currentArtUrl = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '').replace('{w}', 50).replace('{h}', 50); this.currentArtUrl = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? '').replace('{w}', 50).replace('{h}', 50);
try{ try {
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);} document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
catch (e) {} } catch (e) {
}
} else { } else {
let data = await this.mk.api.library.song(this.mk.nowPlayingItem.id); let data = await this.mk.api.library.song(this.mk.nowPlayingItem.id);
if (data != null && data !== "" && data.attributes != null && data.attributes.artwork != null) { if (data != null && data !== "" && data.attributes != null && data.attributes.artwork != null) {
this.currentArtUrl = (data["attributes"]["artwork"]["url"] ?? '').replace('{w}', 50).replace('{h}', 50); this.currentArtUrl = (data["attributes"]["artwork"]["url"] ?? '').replace('{w}', 50).replace('{h}', 50);
try{ try {
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);} document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
catch (e) {} } catch (e) {
} else {this.currentArtUrl = ''; }
try{ } else {
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);} this.currentArtUrl = '';
catch (e) {} try {
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
} catch (e) {
} }
} }
}catch(e){ }
} catch (e) {
} }
}, },
@ -2737,7 +2817,7 @@ const app = new Vue({
const data = await this.mk.api.library.song(this.mk.nowPlayingItem.id) const data = await this.mk.api.library.song(this.mk.nowPlayingItem.id)
if (data != null && data !== "") { if (data != null && data !== "") {
getBase64FromUrl((data["attributes"]["artwork"]["url"]).toString()).then(img =>{ getBase64FromUrl((data["attributes"]["artwork"]["url"]).toString()).then(img => {
document.querySelector('.bg-artwork').forEach(artwork => { document.querySelector('.bg-artwork').forEach(artwork => {
artwork.src = img; artwork.src = img;
}) })
@ -2763,16 +2843,16 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s" let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) { if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) { if (!type.startsWith("library-")) {
type = "library-" + type type = "library-" + type
} }
id = item.id id = item.id
} }
let response = await this.mk.api.v3.music(`/v1/me/ratings/${type}?platform=web&ids=${id}`) let response = await this.mk.api.v3.music(`/v1/me/ratings/${type}?platform=web&ids=${id}`)
if(response.data.data.length != 0) { if (response.data.data.length != 0) {
let value = response.data.data[0].attributes.value let value = response.data.data[0].attributes.value
return value return value
}else{ } else {
return 0 return 0
} }
}, },
@ -2780,7 +2860,7 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s" let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) { if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) { if (!type.startsWith("library-")) {
type = "library-" + type type = "library-" + type
} }
id = item.id id = item.id
@ -2804,7 +2884,7 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s" let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) { if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) { if (!type.startsWith("library-")) {
type = "library-" + type type = "library-" + type
} }
id = item.id id = item.id
@ -2828,7 +2908,7 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s" let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) { if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) { if (!type.startsWith("library-")) {
type = "library-" + type type = "library-" + type
} }
id = item.id id = item.id
@ -2842,16 +2922,20 @@ const app = new Vue({
}, },
volumeWheel(event) { volumeWheel(event) {
if (event.deltaY < 0) { if (event.deltaY < 0) {
if(this.mk.volume < 1){ if (this.mk.volume < 1) {
if (this.mk.volume <= 0.9) { if (this.mk.volume <= 0.9) {
this.mk.volume += 0.1 this.mk.volume += 0.1
} else { this.mk.volume = 1 } } else {
this.mk.volume = 1
}
} }
} else if (event.deltaY > 0) { } else if (event.deltaY > 0) {
if(this.mk.volume > 0){ if (this.mk.volume > 0) {
if (this.mk.volume >= 0.1){ if (this.mk.volume >= 0.1) {
this.mk.volume -= 0.1 this.mk.volume -= 0.1
} else {this.mk.volume = 0} } else {
this.mk.volume = 0
}
} }
} }
}, },
@ -2921,10 +3005,53 @@ const app = new Vue({
items: [] items: []
}, },
normal: { normal: {
headerItems: [
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"hidden": false,
"disabled": true,
"action": function () {
app.love(app.mk.nowPlayingItem)
}
},
{
"icon": "./assets/feather/heart.svg",
"id": "unlove",
"active": true,
"name": "Unlove",
"hidden": true,
"action": function () {
app.unlove(app.mk.nowPlayingItem)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"hidden": false,
"disabled": true,
"action": function () {
app.dislike(app.mk.nowPlayingItem)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"active": true,
"hidden": true,
"action": function () {
app.unlove(app.mk.nowPlayingItem)
}
},
],
items: [ items: [
{ {
"icon": "./assets/feather/list.svg", "icon": "./assets/feather/list.svg",
"name": "Add to Playlist...", "name": "Add to Playlist...",
"hidden": true,
"action": function () { "action": function () {
app.promptAddToPlaylist() app.promptAddToPlaylist()
} }
@ -2935,51 +3062,15 @@ const app = new Vue({
"name": "Add to Library...", "name": "Add to Library...",
"disabled": false, "disabled": false,
"action": function () { "action": function () {
app.addToLibrary(item_id); app.addToLibrary(app.mk.nowPlayingItem.id);
// if (!isLibrary) {app.addToLibrary(item_id); this.mk.nowPlayingItem.attributes.playParams["isLibrary"] = true} else { app.removeFromLibrary(data_type,item_id); this.mk.nowPlayingItem.attributes.playParams["isLibrary"] = false}; // if (!isLibrary) {app.addToLibrary(item_id); this.mk.nowPlayingItem.attributes.playParams["isLibrary"] = true} else { app.removeFromLibrary(data_type,item_id); this.mk.nowPlayingItem.attributes.playParams["isLibrary"] = false};
} }
}, },
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"disabled": true,
"action": function () {
app.love(app.mk.nowPlayingItem)
}
},
{
"icon": "./assets/feather/unheart.svg",
"id": "unlove",
"name": "Unlove",
"disabled": true,
"action": function () {
app.unlove(app.mk.nowPlayingItem)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"disabled": true,
"action": function () {
app.dislike(app.mk.nowPlayingItem)
}
},
{
"icon": "./assets/feather/x-circle.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"disabled": true,
"action": function () {
app.unlove(app.mk.nowPlayingItem)
}
},
{ {
"icon": "./assets/feather/radio.svg", "icon": "./assets/feather/radio.svg",
"name": "Start Radio", "name": "Start Radio",
"action": function () { "action": function () {
app.mk.setStationQueue({song: item_id}).then(() => { app.mk.setStationQueue({song: app.mk.nowPlayingItem.id}).then(() => {
app.mk.play() app.mk.play()
app.selectedMediaItems = [] app.selectedMediaItems = []
}) })
@ -3004,17 +3095,23 @@ const app = new Vue({
// }else{ // }else{
// menus.normal.items.find(x => x.id == "addToLibrary").disabled = true // menus.normal.items.find(x => x.id == "addToLibrary").disabled = true
// } // }
this.showMenuPanel(menus[useMenu], event)
try{
let rating = await app.getRating(app.mk.nowPlayingItem) let rating = await app.getRating(app.mk.nowPlayingItem)
if(rating == 0) { if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false menus.normal.headerItems.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
}else if(rating == 1) { } else if (rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
}else if(rating == -1) { menus.normal.headerItems.find(x => x.id == 'love').hidden = true
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false } else if (rating == -1) {
menus.normal.headerItems.find(x => x.id == 'undo_dislike').hidden = false
menus.normal.headerItems.find(x => x.id == 'dislike').hidden = true
}
} catch(err) {
} }
CiderContextMenu.Create(event, menus[useMenu])
}, },
LastFMDeauthorize() { LastFMDeauthorize() {
ipcRenderer.invoke('setStoreValue', 'lastfm.enabled', false).catch((e) => console.error(e)); ipcRenderer.invoke('setStoreValue', 'lastfm.enabled', false).catch((e) => console.error(e));
@ -3061,10 +3158,14 @@ const app = new Vue({
peak: peak peak: peak
} }
}, },
fullscreen(flag){ fullscreen(flag) {
if (flag) { if (flag) {
ipcRenderer.send('setFullScreen', true); ipcRenderer.send('setFullScreen', true);
if (app.mk.nowPlayingItem.type && app.mk.nowPlayingItem.type.toLowerCase().includes("video")) {
document.querySelector('video#apple-music-video-player').requestFullscreen()
} else {
app.appMode = 'fullscreen'; app.appMode = 'fullscreen';
}
document.addEventListener('keydown', event => { document.addEventListener('keydown', event => {
if (event.key === 'Escape' && app.appMode === 'fullscreen') { if (event.key === 'Escape' && app.appMode === 'fullscreen') {
this.fullscreen(false); this.fullscreen(false);
@ -3079,6 +3180,41 @@ const app = new Vue({
} }
}) })
Vue.component('animated-number', {
template: "<div style='display: inline-block;'>{{ displayNumber }}</div>",
props: {'number': {default: 0}},
data() {
return {
displayNumber: 0,
interval: false
}
},
ready() {
this.displayNumber = this.number ? this.number : 0;
},
watch: {
number() {
clearInterval(this.interval);
if (this.number == this.displayNumber) {
return;
}
this.interval = window.setInterval(() => {
if (this.displayNumber != this.number) {
var change = (this.number - this.displayNumber) / 10;
change = change >= 0 ? Math.ceil(change) : Math.floor(change);
this.displayNumber = this.displayNumber + change;
}
}, 20);
}
}
})
Vue.component('sidebar-library-item', { Vue.component('sidebar-library-item', {
template: '#sidebar-library-item', template: '#sidebar-library-item',
props: { props: {
@ -3189,6 +3325,11 @@ document.addEventListener('musickitloaded', function () {
}; };
request.open("GET", "https://api.cider.sh/"); request.open("GET", "https://api.cider.sh/");
request.send(); request.send();
// check for widevine failure and reconfigure the instance.
window.addEventListener("drmUnsupported", function () {
initMusicKit()
});
}); });
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
@ -3196,7 +3337,7 @@ if ('serviceWorker' in navigator) {
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js?v=1'); navigator.serviceWorker.register('sw.js?v=1');
}); });
} }
const getBase64FromUrl = async (url) => { const getBase64FromUrl = async (url) => {
const data = await fetch(url); const data = await fetch(url);
@ -3269,6 +3410,12 @@ function xmlToJson(xml) {
return obj; return obj;
}; };
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
var checkIfScrollIsStatic = setInterval(() => { var checkIfScrollIsStatic = setInterval(() => {
try { try {
if (position === document.getElementsByClassName('lyric-body')[0].scrollTop) { if (position === document.getElementsByClassName('lyric-body')[0].scrollTop) {

View file

@ -0,0 +1,59 @@
#app.compact {
.content-inner {
zoom: 0.95;
}
.app-sidebar-content {
padding:0px;
.app-sidebar-header-text {
padding: 6px 10px;
margin: 0px;
}
.app-sidebar-item {
display: flex;
width: 100%;
padding: 8px 12px;
font-size: 13px;
margin: 0px;
border: 1px solid transparent;
border-radius: 0px;
transition: unset;
transform: unset;
&:active {
background: var(--selected-click);
}
&::after {
display: none;
}
&.active {
background: var(--keyColor-disabled);
}
}
.sidebar-icon {
width: 14px;
height: 16px;
margin-right: 8px;
}
.folder-body {
border-radius: 0px;
padding: 0px;
}
}
@media (max-width: 951px) {
.content-inner {
zoom: 0.8;
}
}
}
// if page width is less than 951px
@media (max-width: 951px) {
.content-inner {
zoom: 0.8;
}
}

View file

@ -45,6 +45,17 @@ body {
transition: opacity .10s var(--appleEase); transition: opacity .10s var(--appleEase);
} }
a:-webkit-any-link {
color: var(--keyColor);
}
hr {
appearance: none;
border: none;
height: 1px;
background-color: rgb(255 255 255 / 20%);
}
body[loading] { body[loading] {
opacity: 0.5; opacity: 0.5;
pointer-events: none; pointer-events: none;
@ -57,10 +68,10 @@ body[platform='linux'] {
body.notransparency::before { body.notransparency::before {
content: ""; content: "";
position: absolute; position: absolute;
top:0; top: 0;
left:0; left: 0;
right:0; right: 0;
bottom:0; bottom: 0;
opacity: 0.5; opacity: 0.5;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==);
} }
@ -134,7 +145,7 @@ body.notransparency::before {
} }
.bgGradientMaterial-base { .bgGradientMaterial-base {
position:relative; position: relative;
} }
.bgGradientMaterial-base::before { .bgGradientMaterial-base::before {
@ -224,13 +235,14 @@ input[type="text"], input[type="number"] {
z-index: -1; z-index: -1;
.bg-artwork.a { .bg-artwork.a {
top:0; top: 0;
left:0; left: 0;
//mix-blend-mode: luminosity; //mix-blend-mode: luminosity;
} }
.bg-artwork.b { .bg-artwork.b {
bottom:0; bottom: 0;
right:0; right: 0;
animation-direction: reverse; animation-direction: reverse;
animation-delay: 10s; animation-delay: 10s;
} }
@ -332,6 +344,8 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
left: 0; left: 0;
padding: 32px; padding: 32px;
width: 100%; width: 100%;
transition: zoom 1s;
zoom: 1;
} }
.content-inner.centered { .content-inner.centered {
@ -423,6 +437,18 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
.app-sidebar-footer { .app-sidebar-footer {
padding: 11px; padding: 11px;
.app-playback-controls {
.control-buttons {
display: flex;
justify-content: center;
align-content: center;
}
.volume {
display: flex;
}
}
} }
.app-sidebar-button { .app-sidebar-button {
@ -496,6 +522,21 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
background: rgb(0 0 0 / 15%); background: rgb(0 0 0 / 15%);
flex-direction: column; flex-direction: column;
padding: 20px 0px; padding: 20px 0px;
&.libraryNotification {
flex-direction: row;
padding: 0px;
.message {
flex-grow: 1;
}
.spinner {
width: 46px;
height: 30px;
margin-left: 1em;
}
}
} }
.app-sidebar-content { .app-sidebar-content {
@ -1558,7 +1599,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
overflow-x: hidden; overflow-x: hidden;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
font-family: 'Inter', 'Noto Sans JP','Source Han Sans SC', 'Source Han Sans HK','Noto Sans SC','Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif; font-family: 'Inter', 'Noto Sans JP', 'Source Han Sans SC', 'Source Han Sans HK', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif;
} }
.lyric-body .no-lyrics { .lyric-body .no-lyrics {
@ -1653,7 +1694,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyrics-translation { .lyrics-translation {
font-size: 1.6rem; font-size: 1.6rem;
font-weight: 450; font-weight: 450;
font-family: 'Inter', 'Noto Sans JP','Noto Sans SC','Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif; font-family: 'Inter', 'Noto Sans JP', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif;
filter: contrast(0.5); filter: contrast(0.5);
} }
@ -1802,6 +1843,38 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
/* Cider */ /* Cider */
.about-page {
.teamBtn {
display: flex;
align-items: center;
width: 100%;
font-size: 14px;
padding: 6px 16px;
margin: 4px;
> img {
width: 30px;
margin: 0px 16px 0px 0px;
pointer-events: none;
border-radius: 100%;
box-shadow: var(--mediaItemShadow);
image-rendering: -webkit-optimize-contrast;
}
}
.sponsorBtn {
display: inline-flex;
justify-content: center;
align-items: center;
> img {
width: 26px;
margin: 0px 16px 0px 0px;
pointer-events: none;
}
}
}
.md-close-btn { .md-close-btn {
-webkit-mask-image: url("ameres://icons/webui/close.svg"); -webkit-mask-image: url("ameres://icons/webui/close.svg");
-webkit-mask-repeat: no-repeat; -webkit-mask-repeat: no-repeat;
@ -1838,35 +1911,43 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&:active { &:active {
filter: brightness(75%); filter: brightness(75%);
transform: translateY(2px); transform: scale(0.98);
transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase);
} }
} }
.md-select { .md-select {
padding: 5px 10px; width: 100%;
font-size: 1em; padding: 6px;
border-radius: 6px;
border: 1px solid rgba(200, 200, 200, 0.1);
font-family: inherit; font-family: inherit;
border-radius: 4px; font-size: 14px;
border: 1px solid rgb(100 100 100 / 35%); background: rgba(100, 100, 100, 0.25);
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.3), 0px 1px 1px rgba(0, 0, 0, 0.4); color: #c8c8c8;
background: #363636; font-weight: 500;
color: #eee;
}
.md-select:focus { option {
outline: none;
}
.md-select > option {
font-size: 1em; font-size: 1em;
font-family: inherit; font-family: inherit;
padding: 8px 16px; padding: 8px 16px;
background: #404040;
}
optgroup {
background: #2c2c2c;
}
&:focus {
outline: solid 1px var(--selected);
}
} }
.sidebar-playlist { .sidebar-playlist {
.folder-button-active { .folder-button-active {
background: rgb(255 255 255 / 12%); background: rgb(255 255 255 / 12%);
} }
.folder-body { .folder-body {
background: #ffffff0a; background: #ffffff0a;
border-radius: 10px; border-radius: 10px;
@ -2238,6 +2319,28 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
// Library - Songs page
.library-page {
padding: 0px;
.library-header {
position: sticky;
top: 0;
left: 0;
border-bottom: 1px solid rgba(200, 200, 200, 0.05);
z-index: 6;
background: black;
padding: 0px 2em;
backdrop-filter: blur(32px);
background: rgba(24, 24, 24, 0.15);
top: var(--navigationBarHeight);
}
.well {
margin: 2em;
}
}
/* Album / Playlist Page */ /* Album / Playlist Page */
.playlist-page { .playlist-page {
--bgColor: transparent; --bgColor: transparent;
@ -2383,6 +2486,25 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
.playlist-more {
border-radius: 100%;
background: var(--keyColor);
box-shadow: var(--ciderShadow-Generic);
width: 36px;
height: 36px;
float: right;
border: 0px;
cursor: pointer;
z-index: 5;
&:hover {
background: var(--keyColor-rollover);
}
&:active {
background: var(--keyColor-pressed);
}
}
.playlist-time { .playlist-time {
font-size: 0.9em; font-size: 0.9em;
@ -2677,6 +2799,114 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
.menu-panel {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
.menu-header-body {
padding: 6px;
display: flex;
background: rgb(200 200 200 / 10%);
.menu-option-header {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--mediaItemRadius);
appearance: none;
border: 0;
background: transparent;
&.active {
.sidebar-icon>.svg-icon {
--color: var(--keyColor);
}
}
&:hover {
background: var(--selected);
}
&:active {
background: var(--selected-click);
}
}
}
.menu-panel-body {
display: flex;
flex-flow: column;
background: rgb(38 38 38);
position: relative;
min-width: 200px;
box-shadow: var(--ciderShadow-Generic);
border-radius: var(--mediaItemRadius);
overflow: hidden;
font-size: 14px;
.menu-option {
text-align: left;
display: flex;
width: 100%;
padding: 12px 16px;
appearance: none;
border: 0px;
font: inherit;
background: transparent;
color: inherit;
&:hover {
background: var(--selected);
}
&:active {
background: var(--selected-click);
}
}
}
.menu-header-text {
margin: 18px 6px;
.close-btn {
width: 50px;
height: 42px;
background-image: var(--gfx-closeBtn);
background-position: center;
background-repeat: no-repeat;
-webkit-app-region: no-drag;
appearance: none;
border: 0;
background-color: transparent;
position: absolute;
top: 0;
right: 0;
&:hover {
background-color: rgb(196, 43, 28)
}
}
}
.menu-body {
overflow: overlay;
height: 100%;
}
.menu-footer {
width: 100%;
padding: 12px;
}
}
.queue-panel { .queue-panel {
height: 100%; height: 100%;
width: 100%; width: 100%;
@ -2774,6 +3004,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&.hmedia-scroller-card { &.hmedia-scroller-card {
height: 370px; height: 370px;
.mediaitem-card { .mediaitem-card {
margin: 12px; margin: 12px;
} }
@ -2835,11 +3066,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
.info-rect { .info-rect {
width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
justify-content: center; justify-content: center;
flex-grow: 1;
} }
.title { .title {
@ -2848,7 +3079,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.subtitle { .subtitle {
width: 90%; width: 90%;
font-size: 12px; font-size: .8em;
opacity: 0.7; opacity: 0.7;
} }
@ -2870,21 +3101,19 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
justify-content: center; justify-content: center;
} }
.content-rating { .explicit-icon {
text-transform: uppercase; background-image: url("./assets/explicit.svg");
font-size: 10px; height: 12px;
border-radius: 3px; width: 36px;
background: rgb(200 200 200 / 15%); filter: contrast(0);
width: 60px; background-repeat: no-repeat;
text-align: center;
padding: 5px;
margin-right: 12px;
flex: 0 0 auto;
font-weight: 500;
color: #ccc;
} }
.isLibrary { .isLibrary {
flex: 0 0 auto;
width: 40px;
text-align: center;
button { button {
appearance: none; appearance: none;
border: 0px; border: 0px;
@ -2897,6 +3126,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&:hover { &:hover {
background: rgb(200 200 200 / 10%); background: rgb(200 200 200 / 10%);
box-shadow: var(--mediaItemShadow); box-shadow: var(--mediaItemShadow);
.overlay-play { .overlay-play {
opacity: 1; opacity: 1;
} }
@ -2913,6 +3143,20 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
box-shadow: var(--mediaItemShadow); box-shadow: var(--mediaItemShadow);
color: #eee; color: #eee;
} }
// list item compact
&.compact {
height: 40px;
font-size: 13px;
.artwork {
display: none;
}
.info-rect {
padding-left: 1em;
}
}
} }
/* mediaitem-hrect */ /* mediaitem-hrect */
@ -3261,7 +3505,8 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background: rgba(50, 50, 50, 0.7); background: rgba(50, 50, 50, 0.7);
cursor: pointer; cursor: pointer;
transition: opacity 0.1s var(--appleEase); transition: opacity 0.1s var(--appleEase);
:hover{
:hover {
border-radius: 50%; border-radius: 50%;
background: rgba(250, 0, 0, 0.7); background: rgba(250, 0, 0, 0.7);
} }
@ -3365,6 +3610,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
overflow: hidden; overflow: hidden;
border-radius: 0px; border-radius: 0px;
margin: 0; margin: 0;
.mediaitem-artwork { .mediaitem-artwork {
border-radius: 0px; border-radius: 0px;
@ -3382,15 +3628,15 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&::before { &::before {
background: var(--bgartwork); background: var(--bgartwork);
content: ""; content: "";
top:0; top: 0;
left:0; left: 0;
bottom:0; bottom: 0;
right:0; right: 0;
position:absolute; position: absolute;
background-size: cover; background-size: cover;
background-position: bottom; background-position: bottom;
z-index: 0; z-index: 0;
opacity:1; opacity: 1;
filter: brightness(0.5) blur(50px) saturate(180%); filter: brightness(0.5) blur(50px) saturate(180%);
} }
} }
@ -3578,11 +3824,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background-color: rgba(200, 200, 200, 0.7); background-color: rgba(200, 200, 200, 0.7);
} }
.playback-button--small{ .playback-button--small {
opacity: 0.7; opacity: 0.7;
} }
.right-col{ .right-col {
height: 50vh; height: 50vh;
} }
@ -3591,6 +3837,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
display: flex !important; display: flex !important;
} }
} }
.display--large { .display--large {
display: flex; display: flex;
@ -3632,7 +3879,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
.background{ .background {
position: absolute; position: absolute;
background-size: cover; background-size: cover;
width: 100%; width: 100%;
@ -3643,23 +3890,22 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
width: 100%; width: 100%;
height: 100%; height: 100%;
.bg-artwork-container{ .bg-artwork-container {
z-index: unset; z-index: unset;
} }
.bg-artwork-container .bg-artwork { .bg-artwork-container .bg-artwork {
filter: brightness(80%) saturate(69%) blur(180px) contrast(0.8) opacity(0.8); filter: brightness(85%) saturate(95%) blur(180px) contrast(0.9) opacity(0.9);
} }
.no-animation{ .no-animation {
animation : unset; animation: unset;
} }
} }
} }
.lyrics-col {
.lyrics-col{
height: 62vh; height: 62vh;
display: flex; display: flex;
@ -3675,25 +3921,25 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
box-shadow: inset 0px 0px 10px 10px rgb(200 200 200 / 50%); box-shadow: inset 0px 0px 10px 10px rgb(200 200 200 / 50%);
} }
.no-lyrics{ .no-lyrics {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.lyric-line{ .lyric-line {
font-size: 35px; font-size: 35px;
} }
} }
.queue-col{ .queue-col {
width: 60vh; width: 60vh;
height: 50vh; height: 50vh;
.queue-title{ .queue-title {
opacity: 0.6; opacity: 0.6;
} }
@ -3710,7 +3956,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
.tab-toggles{ .tab-toggles {
display: flex; display: flex;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@ -3720,7 +3966,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
justify-content: space-evenly; justify-content: space-evenly;
.queue { .queue {
background-image: url("./assets/list.svg") ; background-image: url("./assets/list.svg");
padding: 0.5vh; padding: 0.5vh;
width: 2.5vh; width: 2.5vh;
height: 2.5vh; height: 2.5vh;
@ -3729,7 +3975,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
.lyrics { .lyrics {
background-image: url("./assets/quote-right.svg") ; background-image: url("./assets/quote-right.svg");
padding: 0.5vh; padding: 0.5vh;
width: 2.5vh; width: 2.5vh;
height: 2.5vh; height: 2.5vh;
@ -3754,12 +4000,12 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
height: 50vh; height: 50vh;
} }
.controls-parents{ .controls-parents {
width: 50vh; width: 50vh;
} }
.app-playback-controls { .app-playback-controls {
.song-artist , .song-name { .song-artist, .song-name {
font-weight: 600; font-weight: 600;
text-align: center; text-align: center;
font-size: 0.9em; font-size: 0.9em;
@ -3779,9 +4025,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
} }
} }
.song-artist { .song-artist {
font-size: 0.875em; font-size: 0.875em;
} }
.song-name { .song-name {
width: unset !important; width: unset !important;
margin-top: 0.15vh; margin-top: 0.15vh;
@ -3804,7 +4052,8 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
input[type="range"] { input[type="range"] {
width: 100%; width: 100%;
} }
>div {
> div {
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
@ -3882,12 +4131,28 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
/* Cider */ /* Cider */
// sidebar icon // sidebar icon
.svg-icon {
--color: #aaa;
--url: url("./assets/feather/share.svg");
-webkit-mask-image: var(--url);
-webkit-mask-size: cover;
height: 18px;
width: 18px;
background: var(--color);
}
.sidebar-icon { .sidebar-icon {
width: 18px; width: 18px;
height: 18px; height: 18px;
margin-right: 8px; margin-right: 8px;
>svg { > .svg-icon {
width: 100%;
height: 100%;
--color: #aaa;
}
> svg {
width: 100%; width: 100%;
height: 100%; height: 100%;
color: #aaa; color: #aaa;
@ -3917,6 +4182,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.bgArtworkMaterial { .bgArtworkMaterial {
display: block; display: block;
&::before { &::before {
top: -50%; top: -50%;
left: -20%; left: -20%;
@ -3939,8 +4205,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
transform-origin: center; transform-origin: center;
animation: fsLyricIn var(--appleEase) .2s; animation: fsLyricIn var(--appleEase) .2s;
opacity: 0.9; opacity: 0.9;
text-shadow: text-shadow: -1px -1px 0 #000,
-1px -1px 0 #000,
0 -1px 0 #000, 0 -1px 0 #000,
1px -1px 0 #000, 1px -1px 0 #000,
1px 0 0 #000, 1px 0 0 #000,
@ -4162,7 +4427,7 @@ div#captions {
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.6);
color: yellow; color: yellow;
white-space: pre-line; white-space: pre-line;
font-family: 'Inter', 'Noto Sans JP','Source Han Sans SC', 'Source Han Sans HK','Source Han Sans SC', 'Source Han Sans HK','Noto Sans SC','Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif; font-family: 'Inter', 'Noto Sans JP', 'Source Han Sans SC', 'Source Han Sans HK', 'Source Han Sans SC', 'Source Han Sans HK', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif;
} }
[v-cloak] { [v-cloak] {
@ -4276,6 +4541,7 @@ body.no-gpu {
.bg-artwork-container { .bg-artwork-container {
display: none; display: none;
} }
#navigation-bar { #navigation-bar {
backdrop-filter: unset; backdrop-filter: unset;
mix-blend-mode: unset; mix-blend-mode: unset;
@ -4315,3 +4581,5 @@ body.no-gpu {
right: -300px; right: -300px;
} }
} }
@import url("less/compact.less");

View file

@ -15,7 +15,7 @@
</button> </button>
</div> </div>
<div class="modal-search"> <div class="modal-search">
<div class="search-input-container" style="width:100%;margin: 16px 0px;"> <div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" <input type="search"
ref="searchInput" ref="searchInput"

View file

@ -7,14 +7,19 @@
:data-index="index" :data-index="index"
:data-guid="guid" :data-guid="guid"
:data-islibrary="this.item.attributes.playParams.isLibrary ?? false" :data-islibrary="this.item.attributes.playParams.isLibrary ?? false"
:key="item.attributes.playParams.id ?? item.id"
class="cd-mediaitem-list-item" class="cd-mediaitem-list-item"
:class="{'mediaitem-selected': app.select_hasMediaItem(guid)}"> @mouseenter="checkLibrary"
@mouseover="showInLibrary = true"
@mouseleave="showInLibrary = false"
:class="[{'mediaitem-selected': app.select_hasMediaItem(guid)}, addClasses]">
<template v-if="isVisible"> <template v-if="isVisible">
<div class="isLibrary" v-if="showLibraryStatus == true"> <div class="isLibrary" :style="{opacity: (showInLibrary ? 1 : 0)}" v-if="showLibraryStatus == true">
<button @click="addToLibrary()" <button @click="addToLibrary()"
v-if="!app.isInLibrary(item.attributes.playParams) && !addedToLibrary">🖤 v-if="!addedToLibrary">
<div class="svg-icon" :style="{'--color': 'var(--keyColor)', '--url': 'url(./assets/feather/plus.svg)'}"></div>
</button> </button>
<button v-else @click="removeFromLibrary()">❤️</button> <button v-else style="opacity:0;">❤️</button>
</div> </div>
<div class="artwork" v-if="showArtwork == true"> <div class="artwork" v-if="showArtwork == true">
<mediaitem-artwork <mediaitem-artwork
@ -24,7 +29,7 @@
<button class="overlay-play" @click="playTrack()"><%- include("../svg/play.svg") %></button> <button class="overlay-play" @click="playTrack()"><%- include("../svg/play.svg") %></button>
</div> </div>
<div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}" <div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}"
@dblclick="app.routeView(item)"> @dblclick="route()">
<div class="title text-overflow-elipsis"> <div class="title text-overflow-elipsis">
{{ item.attributes.name }} {{ item.attributes.name }}
</div> </div>
@ -44,10 +49,8 @@
</template> </template>
</div> </div>
</div> </div>
<div class="content-rating" v-if="item.attributes.contentRating" @dblclick="app.routeView(item)"> <div class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div>
{{ item.attributes.contentRating }} <template v-if="showMetaData == true" @dblclick="route()">
</div>
<template v-if="showMetaData == true" @dblclick="app.routeView(item)">
<div class="metainfo"> <div class="metainfo">
{{ item.attributes.releaseDate ? new Date(item.attributes.releaseDate).toLocaleDateString() {{ item.attributes.releaseDate ? new Date(item.attributes.releaseDate).toLocaleDateString()
: "" }} : "" }}
@ -56,7 +59,7 @@
{{ item.attributes.genreNames[0] ?? "" }} {{ item.attributes.genreNames[0] ?? "" }}
</div> </div>
</template> </template>
<div class="duration" v-if="displayDuration" @dblclick="app.routeView(item)"> <div class="duration" v-if="displayDuration" @dblclick="route()">
{{ msToMinSec(item.attributes.durationInMillis ?? 0) }} {{ msToMinSec(item.attributes.durationInMillis ?? 0) }}
</div> </div>
</template> </template>
@ -68,11 +71,13 @@
template: '#mediaitem-list-item', template: '#mediaitem-list-item',
data: function () { data: function () {
return { return {
showInLibrary: false,
isVisible: false, isVisible: false,
addedToLibrary: false, addedToLibrary: false,
guid: this.uuidv4(), guid: this.uuidv4(),
app: this.$root, app: this.$root,
displayDuration: true displayDuration: true,
addClasses: {}
} }
}, },
props: { props: {
@ -84,14 +89,36 @@
'show-meta-data': {type: Boolean, default: false}, 'show-meta-data': {type: Boolean, default: false},
'show-duration': {type: Boolean, default: true}, 'show-duration': {type: Boolean, default: true},
'contextExt': {type: Object, required: false}, 'contextExt': {type: Object, required: false},
'class-list': {type: String, required: false, default: ""},
}, },
mounted() { mounted() {
let duration = this.item.attributes.durationInMillis ?? 0 let duration = this.item.attributes.durationInMillis ?? 0
if (duration == 0 || !this.showDuration) { if (duration == 0 || !this.showDuration) {
this.displayDuration = false this.displayDuration = false
} }
this.getClasses()
}, },
methods: { methods: {
async checkLibrary() {
if(this.addedToLibrary) {return this.addedToLibrary}
if(this.item.type.includes("library-playlists") || this.item.type.includes("station")) {
this.addedToLibrary = true
return
}
this.$root.inLibrary([this.item]).then(res => {
this.addedToLibrary = res[0].attributes.inLibrary
})
return this.addedToLibrary
},
getClasses() {
if(this.classList) {
this.addClasses = {}
let classList = this.classList.split(' ')
for(let i = 0; i < classList.length; i++) {
this.addClasses[classList[i]] = true
}
}
},
dragStart(evt) { dragStart(evt) {
evt.dataTransfer.setData('text/plain', JSON.stringify({ evt.dataTransfer.setData('text/plain', JSON.stringify({
type: this.item.attributes.playParams.kind ?? this.item.type, type: this.item.attributes.playParams.kind ?? this.item.type,
@ -197,12 +224,14 @@
items: [ items: [
{ {
"name": "Add to Playlist...", "name": "Add to Playlist...",
"icon": "./assets/feather/plus.svg",
"action": function () { "action": function () {
app.promptAddToPlaylist() app.promptAddToPlaylist()
} }
}, },
{ {
name: `Play ${app.selectedMediaItems.length} tracks next`, name: `Play ${app.selectedMediaItems.length} tracks next`,
"icon": "./assets/arrow-bend-up.svg",
action: () => { action: () => {
let itemsToPlay = {} let itemsToPlay = {}
app.selectedMediaItems.forEach(item => { app.selectedMediaItems.forEach(item => {
@ -224,6 +253,7 @@
}, },
{ {
name: `Play ${app.selectedMediaItems.length} tracks later`, name: `Play ${app.selectedMediaItems.length} tracks later`,
"icon": "./assets/arrow-bend-down.svg",
action: () => { action: () => {
let itemsToPlay = {} let itemsToPlay = {}
app.selectedMediaItems.forEach(item => { app.selectedMediaItems.forEach(item => {
@ -245,7 +275,68 @@
] ]
}, },
normal: { normal: {
headerItems: [
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"hidden": false,
"disabled": true,
"action": function () {
app.love(self.item)
}
},
{
"icon": "./assets/feather/heart.svg",
"id": "unlove",
"active": true,
"name": "Unlove",
"hidden": true,
"action": function () {
app.unlove(self.item)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"hidden": false,
"disabled": true,
"action": function () {
app.dislike(self.item)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"active": true,
"hidden": true,
"action": function () {
app.unlove(self.item)
}
},
],
items: [ items: [
{
"id": "addToLibrary",
"icon": "./assets/feather/plus.svg",
"name": "Add to library",
"hidden": false,
"disabled": true,
"action": function () {
self.addToLibrary()
}
},
{
"id": "removeFromLibrary",
"icon": "./assets/feather/x-circle.svg",
"name": "Remove from library",
"hidden": true,
"action": function () {
self.removeFromLibrary()
}
},
{ {
"icon": "./assets/feather/list.svg", "icon": "./assets/feather/list.svg",
"name": "Add to Playlist...", "name": "Add to Playlist...",
@ -255,6 +346,7 @@
}, },
{ {
"name": "Play Next", "name": "Play Next",
"icon": "./assets/arrow-bend-up.svg",
"action": function () { "action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id}) app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex() app.mk.queue._reindex()
@ -263,6 +355,7 @@
}, },
{ {
"name": "Play Later", "name": "Play Later",
"icon": "./assets/arrow-bend-down.svg",
"action": function () { "action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id}) app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex() app.mk.queue._reindex()
@ -279,42 +372,6 @@
}) })
} }
}, },
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"disabled": true,
"action": function () {
app.love(self.item)
}
},
{
"icon": "./assets/feather/unheart.svg",
"id": "unlove",
"name": "Unlove",
"disabled": true,
"action": function () {
app.unlove(self.item)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"disabled": true,
"action": function () {
app.dislike(self.item)
}
},
{
"icon": "./assets/feather/x-circle.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"disabled": true,
"action": function () {
app.unlove(self.item)
}
},
{ {
"icon": "./assets/feather/user.svg", "icon": "./assets/feather/user.svg",
"name": "Go to Artist", "name": "Go to Artist",
@ -352,21 +409,38 @@
if (this.contextExt.multiple) { if (this.contextExt.multiple) {
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple) menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
} }
}
app.showMenuPanel(menus[useMenu], event)
try {
await this.checkLibrary().then(res => {
console.log(res)
if(res) {
menus.normal.items.find(x => x.id == 'addToLibrary').hidden = true
menus.normal.items.find(x => x.id == 'removeFromLibrary').hidden = false
}else{
menus.normal.items.find(x => x.id == 'addToLibrary').disabled = false
}
})
}catch(e) {
} }
try{ try{
let rating = await app.getRating(self.item) let rating = await app.getRating(self.item)
if (rating == 0) { if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false menus.normal.headerItems.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
} else if (rating == 1) { } else if (rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
menus.normal.headerItems.find(x => x.id == 'love').hidden = true
} else if (rating == -1) { } else if (rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false menus.normal.headerItems.find(x => x.id == 'undo_dislike').hidden = false
menus.normal.headerItems.find(x => x.id == 'dislike').hidden = true
} }
} catch(err) { } catch(err) {
} }
CiderContextMenu.Create(event, menus[useMenu])
}, },
visibilityChanged: function (isVisible, entry) { visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible this.isVisible = isVisible
@ -474,6 +548,14 @@
else { else {
app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url) app.playMediaItemById(item.attributes.playParams.id ?? item.id, item.attributes.playParams.kind ?? item.type, item.attributes.playParams.isLibrary ?? false, item.attributes.url)
}}) }})
},
route(){
let kind = (this.item.attributes.playParams ? (this.item.attributes.playParams.kind ?? (this.item.type ?? '')) : (this.item.type ?? ''));
if (kind.toLowerCase().includes('album') || kind.toLowerCase().includes('playlist')){
app.routeView(this.item)
} else {
this.playTrack()
}
} }
} }
}); });

View file

@ -76,7 +76,7 @@
}, },
methods: { methods: {
getBgColor() { getBgColor() {
let color = `#${(this.item.attributes.artwork.bgColor != null) ? (this.item.attributes.artwork.bgColor) : `333333`}` let color = `#${(this.item.attributes.artwork != null && this.item.attributes.artwork.bgColor != null) ? (this.item.attributes.artwork.bgColor) : `333333`}`
let c = color.substring(1); // strip # let c = color.substring(1); // strip #
var rgb = parseInt(c, 16); // convert rrggbb to decimal var rgb = parseInt(c, 16); // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff; // extract red var r = (rgb >> 16) & 0xff; // extract red
@ -85,8 +85,6 @@
var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709 var luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709
console.log(color)
console.log(luma)
if (luma > 140) { if (luma > 140) {
return "#aaaaaa" return "#aaaaaa"
}else{ }else{
@ -142,9 +140,10 @@
} }
}, },
async getBadges() { async getBadges() {
let self = this const self = this
if (this.badges[this.item.attributes.playParams.id ?? this.item.id]) { const id = (this.item.attributes.playParams ? this.item.attributes.playParams.id : null) || this.item.id
let friends = this.badges[this.item.attributes.playParams.id ?? this.item.id] if (id && this.badges[id]) {
let friends = this.badges[id]
if (friends) { if (friends) {
friends.forEach(function (friend) { friends.forEach(function (friend) {
self.app.mk.api.socialProfile(friend).then(data => { self.app.mk.api.socialProfile(friend).then(data => {
@ -247,7 +246,6 @@
} else { } else {
console.log(event) console.log(event)
} }
await this.isInLibrary();
let self = this let self = this
let useMenu = "normal" let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) { if (app.selectedMediaItems.length <= 1) {
@ -261,6 +259,7 @@
items: [ items: [
{ {
name: `Play ${app.selectedMediaItems.length} tracks next`, name: `Play ${app.selectedMediaItems.length} tracks next`,
"icon": "./assets/arrow-bend-up.svg",
action: () => { action: () => {
let itemsToPlay = {} let itemsToPlay = {}
app.selectedMediaItems.forEach(item => { app.selectedMediaItems.forEach(item => {
@ -282,6 +281,7 @@
}, },
{ {
name: `Play ${app.selectedMediaItems.length} tracks later`, name: `Play ${app.selectedMediaItems.length} tracks later`,
"icon": "./assets/arrow-bend-down.svg",
action: () => { action: () => {
let itemsToPlay = {} let itemsToPlay = {}
app.selectedMediaItems.forEach(item => { app.selectedMediaItems.forEach(item => {
@ -303,6 +303,48 @@
] ]
}, },
normal: { normal: {
headerItems: [
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"hidden": false,
"disabled": true,
"action": function () {
app.love(self.item)
}
},
{
"icon": "./assets/feather/heart.svg",
"id": "unlove",
"active": true,
"name": "Unlove",
"hidden": true,
"action": function () {
app.unlove(self.item)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"hidden": false,
"disabled": true,
"action": function () {
app.dislike(self.item)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"active": true,
"hidden": true,
"action": function () {
app.unlove(self.item)
}
},
],
items: [ items: [
{ {
"icon": "./assets/feather/list.svg", "icon": "./assets/feather/list.svg",
@ -312,8 +354,35 @@
app.promptAddToPlaylist() app.promptAddToPlaylist()
} }
}, },
{
"id": "addToLibrary",
"icon": "./assets/feather/plus.svg",
"name": "Add to library",
"hidden": false,
"disabled": true,
"action": function () {
let item_id = self.item.attributes.playParams.id ?? self.item.id;
let data_type = self.item.attributes.playParams.kind ?? self.item.type;
app.addToLibrary(item_id);
self.addedToLibrary = true;
}
},
{
"id": "removeFromLibrary",
"icon": "./assets/feather/x-circle.svg",
"name": "Remove from library",
"hidden": true,
"action": async function () {
console.log("remove");
let item_id = self.item.attributes.playParams.id ?? self.item.id;
let data_type = self.item.attributes.playParams.kind ?? self.item.type;
await self.removeFromLibrary(item_id);
self.addedToLibrary = false
}
},
{ {
"name": "Play Next", "name": "Play Next",
"icon": "./assets/arrow-bend-up.svg",
"action": function () { "action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id}) app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex() app.mk.queue._reindex()
@ -322,66 +391,13 @@
}, },
{ {
"name": "Play Later", "name": "Play Later",
"icon": "./assets/arrow-bend-down.svg",
"action": function () { "action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id}) app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex() app.mk.queue._reindex()
app.selectedMediaItems = [] app.selectedMediaItems = []
} }
}, },
{
"icon": "./assets/feather/plus.svg",
"name": (this.addedToLibrary) ? "Remove from Library..." : "Add to Library...",
"action": async function () {
let item_id = self.item.attributes.playParams.id ?? self.item.id;
let data_type = self.item.attributes.playParams.kind ?? self.item.type;
if (self.addedToLibrary != true) {
console.log("add");
app.addToLibrary(item_id);
self.addedToLibrary = true
} else {
console.log("remove");
await self.removeFromLibrary(item_id);
self.addedToLibrary = false
}
;
}
},
{
"icon": "./assets/feather/heart.svg",
"id": "love",
"name": "Love",
"disabled": true,
"action": function () {
app.love(self.item)
}
},
{
"icon": "./assets/feather/unheart.svg",
"id": "unlove",
"name": "Unlove",
"disabled": true,
"action": function () {
app.unlove(self.item)
}
},
{
"icon": "./assets/feather/thumbs-down.svg",
"id": "dislike",
"name": "Dislike",
"disabled": true,
"action": function () {
app.dislike(self.item)
}
},
{
"icon": "./assets/feather/x-circle.svg",
"id": "undo_dislike",
"name": "Undo dislike",
"disabled": true,
"action": function () {
app.unlove(self.item)
}
},
{ {
"icon": "./assets/feather/share.svg", "icon": "./assets/feather/share.svg",
"name": "Share", "name": "Share",
@ -398,14 +414,35 @@
return item.id != "addToPlaylist" return item.id != "addToPlaylist"
}) })
} }
app.showMenuPanel(menus[useMenu], event)
try {
await this.isInLibrary().then((_) => {
if(self.addedToLibrary) {
menus.normal.items.find(x => x.id == 'addToLibrary').hidden = true
menus.normal.items.find(x => x.id == 'removeFromLibrary').hidden = false
}else{
menus.normal.items.find(x => x.id == 'addToLibrary').disabled = false
}
})
}catch(e) {
}
try{
let rating = await app.getRating(self.item) let rating = await app.getRating(self.item)
if (rating == 0) { if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false menus.normal.headerItems.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
} else if (rating == 1) { } else if (rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
menus.normal.headerItems.find(x => x.id == 'love').hidden = true
} else if (rating == -1) { } else if (rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false menus.normal.headerItems.find(x => x.id == 'undo_dislike').hidden = false
menus.normal.headerItems.find(x => x.id == 'dislike').hidden = true
}
} catch(err) {
} }
if (this.contextExt) { if (this.contextExt) {
@ -416,7 +453,6 @@
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple) menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
} }
} }
CiderContextMenu.Create(event, menus[useMenu])
}, },
}, },
beforeDestroy: function () { beforeDestroy: function () {

View file

@ -0,0 +1,111 @@
<script type="text/x-template" id="cider-menu-panel">
<div class="menu-panel" @click.self="menuPanel.visible = false" @contextmenu.self="menuPanel.visible = false">
<div class="menu-panel-body" :style="getStyle()">
<div class="menu-header-text" v-if="content.name != ''">
<div class="row">
<div class="col">
<h3 class="queue-header-text">{{ content.name }}</h3>
</div>
</div>
</div>
<div class="menu-header-body" v-if="Object.keys(content.headerItems).length != 0">
<template v-for="item in content.headerItems">
<button class="menu-option-header" :class="getClasses(item)" :title="item.name"
v-if="canDisplay(item)" :style="getItemStyle(item)" @click="action(item)">
<div class="sidebar-icon" style="margin: 0;" v-if="item.icon">
<div class="svg-icon" :style="{'--url': 'url(' + item.icon + ')'}"></div>
</div>
</button>
</template>
</div>
<div class="menu-body">
<template v-for="item in content.items">
<button class="menu-option" v-if="canDisplay(item)" :style="getItemStyle(item)"
@click="action(item)">
<div class="sidebar-icon" v-if="item.icon">
<div class="svg-icon" :style="{'--url': 'url(' + item.icon + ')'}"></div>
</div>
{{ item.name }}
</button>
</template>
</div>
</div>
</div>
</script>
<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
}
},
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];
});
},
methods: {
getClasses(item) {
if (item["active"]) {
return "active";
}
},
getStyle() {
let style = {}
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"] = (window.innerWidth - this.size[0]) + "px";
}
if (this.event.clientY + this.size[1] > window.innerHeight) {
style["top"] = (window.innerHeight - this.size[1]) + "px";
}
}
return 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

@ -113,7 +113,7 @@
}] }]
} }
} }
CiderContextMenu.Create(event, menus[useMenu]); app.showMenuPanel(menus[useMenu], event);
}, },
playQueueItem(index) { playQueueItem(index) {
app.mk.changeToMediaAtIndex(index) app.mk.changeToMediaAtIndex(index)

View file

@ -134,6 +134,7 @@
"addToFavorites": { "addToFavorites": {
name: "Add to favorites", name: "Add to favorites",
disabled: true, disabled: true,
hidden: true,
action: () => { action: () => {
this.addFavorite(playlist_id, "library-playlists") this.addFavorite(playlist_id, "library-playlists")
} }
@ -143,7 +144,7 @@
if(this.item.type === "library-playlist-folders") { if(this.item.type === "library-playlist-folders") {
menu.items.addToFavorites.disabled = true menu.items.addToFavorites.disabled = true
} }
CiderContextMenu.Create(event, menu) app.showMenuPanel(menu, event)
}, },
dragOver(evt) { dragOver(evt) {
evt.preventDefault(); evt.preventDefault();

View file

@ -30,7 +30,7 @@
</head> </head>
<body oncontextmenu="return false;" loading="1" platform="<%= env.platform %>"> <body oncontextmenu="return false;" loading="1" platform="<%= env.platform %>">
<div id="app"> <div id="app" :class="getAppClasses()">
<transition name="fsModeSwitch"> <transition name="fsModeSwitch">
<div id="app-main" v-show="appMode == 'player'"> <div id="app-main" v-show="appMode == 'player'">
<div class="mv-chrome" v-if="chrome.topChromeVisible == false"></div> <div class="mv-chrome" v-if="chrome.topChromeVisible == false"></div>
@ -48,23 +48,23 @@
<div class="app-chrome-item full-height" v-else> <div class="app-chrome-item full-height" v-else>
<div class="app-title"></div> <div class="app-title"></div>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item display--large">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" <button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
@click="mk.shuffleMode = 1"></button> @click="mk.shuffleMode = 1"></button>
<button class="playback-button--small shuffle active" v-else <button class="playback-button--small shuffle active" v-else
@click="mk.shuffleMode = 0"></button> @click="mk.shuffleMode = 0"></button>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item display--large">
<button class="playback-button previous" @click="prevButton()"></button> <button class="playback-button previous" @click="prevButton()"></button>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item display--large">
<button class="playback-button pause" @click="mk.pause()" v-if="mk.isPlaying"></button> <button class="playback-button pause" @click="mk.pause()" v-if="mk.isPlaying"></button>
<button class="playback-button play" @click="mk.play()" v-else></button> <button class="playback-button play" @click="mk.play()" v-else></button>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item display--large">
<button class="playback-button next" @click="mk.skipToNextItem()"></button> <button class="playback-button next" @click="mk.skipToNextItem()"></button>
</div> </div>
<div class="app-chrome-item"> <div class="app-chrome-item display--large">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" <button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"></button> @click="mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2" <button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
@ -94,7 +94,8 @@
<div class="song-artist item-navigate" style="display: inline-block;" <div class="song-artist item-navigate" style="display: inline-block;"
@click="getNowPlayingItemDetailed('album')"> @click="getNowPlayingItemDetailed('album')">
<div class="separator" style="display: inline-block;">{{"-"}}</div> <div class="separator" style="display: inline-block;">{{"-"}}</div>
{{(mk.nowPlayingItem["attributes"]["albumName"]) ? (mk.nowPlayingItem["attributes"]["albumName"]) : "" }} {{(mk.nowPlayingItem["attributes"]["albumName"]) ?
(mk.nowPlayingItem["attributes"]["albumName"]) : "" }}
</div> </div>
</div> </div>
@ -127,7 +128,8 @@
<div class="app-chrome--right"> <div class="app-chrome--right">
<div class="app-chrome-item volume display--large"> <div class="app-chrome-item volume display--large">
<div class="app-chrome-item volume-icon"></div> <div class="app-chrome-item volume-icon"></div>
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1" v-model="mk.volume" <input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1"
v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'"> v-if="typeof mk.volume != 'undefined'">
</div> </div>
<div class="app-chrome-item generic" v-if="false"> <div class="app-chrome-item generic" v-if="false">
@ -181,21 +183,30 @@
<div class="app-sidebar-header-text"> <div class="app-sidebar-header-text">
Apple Music Apple Music
</div> </div>
<sidebar-library-item name="Home" svg-icon="./assets/feather/home.svg" page="home"></sidebar-library-item> <sidebar-library-item name="Home" svg-icon="./assets/feather/home.svg"
<sidebar-library-item name="Listen Now" svg-icon="./assets/feather/play-circle.svg" page="listen_now"></sidebar-library-item> page="home"></sidebar-library-item>
<sidebar-library-item name="Browse" svg-icon="./assets/feather/globe.svg" page="browse"></sidebar-library-item> <sidebar-library-item name="Listen Now" svg-icon="./assets/feather/play-circle.svg"
<sidebar-library-item name="Radio" svg-icon="./assets/feather/radio.svg" page="radio"></sidebar-library-item> page="listen_now"></sidebar-library-item>
<sidebar-library-item name="Browse" svg-icon="./assets/feather/globe.svg"
page="browse"></sidebar-library-item>
<sidebar-library-item name="Radio" svg-icon="./assets/feather/radio.svg"
page="radio"></sidebar-library-item>
<div class="app-sidebar-header-text"> <div class="app-sidebar-header-text">
Library Library
</div> </div>
<sidebar-library-item name="Recently Added" svg-icon="./assets/feather/plus-circle.svg" page="library-recentlyadded"></sidebar-library-item> <sidebar-library-item name="Recently Added" svg-icon="./assets/feather/plus-circle.svg"
<sidebar-library-item name="Songs" svg-icon="./assets/feather/music.svg" page="library-songs"></sidebar-library-item> page="library-recentlyadded"></sidebar-library-item>
<sidebar-library-item name="Albums" svg-icon="./assets/feather/disc.svg" page="library-albums"></sidebar-library-item> <sidebar-library-item name="Songs" svg-icon="./assets/feather/music.svg"
<sidebar-library-item name="Artists" svg-icon="./assets/feather/user.svg" page="library-artists"></sidebar-library-item> page="library-songs"></sidebar-library-item>
<sidebar-library-item name="Albums" svg-icon="./assets/feather/disc.svg"
page="library-albums"></sidebar-library-item>
<sidebar-library-item name="Artists" svg-icon="./assets/feather/user.svg"
page="library-artists"></sidebar-library-item>
<div class="app-sidebar-header-text" @contextmenu="playlistHeaderContextMenu"> <div class="app-sidebar-header-text" @contextmenu="playlistHeaderContextMenu">
Playlists Playlists
</div> </div>
<sidebar-playlist v-for="item in getPlaylistFolderChildren('p.playlistsroot')" :item="item"></sidebar-playlist> <sidebar-playlist v-for="item in getPlaylistFolderChildren('p.playlistsroot')"
:item="item"></sidebar-playlist>
</div> </div>
<transition name="wpfade"> <transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened"> <div class="usermenu-container" v-if="chrome.menuOpened">
@ -220,10 +231,14 @@
</div> </div>
</div> </div>
</button> </button>
<button class="usermenu-item" v-if="cfg.advanced.AudioContext && cfg.audio.spatial" @click="modals.spatialProperties = true"> <button class="usermenu-item" v-if="cfg.advanced.AudioContext && cfg.audio.spatial"
@click="modals.spatialProperties = true">
Spatialized Audio Settings Spatialized Audio Settings
</button> </button>
<button class="usermenu-item"> <button class="usermenu-item" @click="appRoute('apple-account-settings')">
Account Settings
</button>
<button class="usermenu-item" @click="appRoute('about')">
About About
</button> </button>
<button class="usermenu-item" @click="window.open('https://discord.gg/applemusic')"> <button class="usermenu-item" @click="window.open('https://discord.gg/applemusic')">
@ -239,9 +254,45 @@
</div> </div>
</transition> </transition>
<div class="app-sidebar-footer"> <div class="app-sidebar-footer">
<input type="range" class="web-slider display--small" step="0.01" min="0" max="1"
v-model="mk.volume" @wheel="volumeWheel" <div class="app-playback-controls display--small" v-if="mkReady()"
@contextmenu="nowPlayingContextMenu">
<div class="control-buttons">
<div class="app-chrome-item">
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0"
@click="mk.shuffleMode = 1"></button>
<button class="playback-button--small shuffle active" v-else
@click="mk.shuffleMode = 0"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button previous" @click="prevButton()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button pause" @click="mk.pause()"
v-if="mk.isPlaying"></button>
<button class="playback-button play" @click="mk.play()" v-else></button>
</div>
<div class="app-chrome-item">
<button class="playback-button next" @click="mk.skipToNextItem()"></button>
</div>
<div class="app-chrome-item">
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"></button>
<button class="playback-button--small repeat active" @click="mk.repeatMode = 2"
v-else-if="mk.repeatMode == 1"></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 0"
v-else-if="mk.repeatMode == 2"></button>
</div>
</div>
<div class="app-chrome-item volume">
<div class="app-chrome-item volume-icon"></div>
<div class="input-container">
<input type="range" class="" @wheel="volumeWheel" step="0.01" min="0" max="1"
v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'"> v-if="typeof mk.volume != 'undefined'">
</div>
</div>
</div>
<button class="app-sidebar-button" style="width:100%" <button class="app-sidebar-button" style="width:100%"
:class="{active: chrome.menuOpened}" :class="{active: chrome.menuOpened}"
@blur="setTimeout(()=>{chrome.menuOpened = false}, 100)" @blur="setTimeout(()=>{chrome.menuOpened = false}, 100)"
@ -269,13 +320,9 @@
</div> </div>
</button> </button>
</div> </div>
<div class="app-sidebar-notification" v-if="library.downloadNotification.show"> <div class="app-sidebar-notification libraryNotification" v-if="library.downloadNotification.show">
<div>{{ library.downloadNotification.message }}</div> <div class="message">{{ library.downloadNotification.message }} ({{
<div>{{ library.downloadNotification.progress }} / {{ library.downloadNotification.total }} library.downloadNotification.progress }} / {{ library.downloadNotification.total }})
</div>
<div style="width: 100%">
<progress style="width: 80%;" :value="library.downloadNotification.progress"
:max="library.downloadNotification.total"></progress>
</div> </div>
</div> </div>
</div> </div>
@ -285,6 +332,18 @@
<button class="nav-item" <button class="nav-item"
@click="navigateForward()"><%- include('svg/chevron-right.svg') %></button> @click="navigateForward()"><%- include('svg/chevron-right.svg') %></button>
</div> </div>
<!-- Apple Setings Page -->
<transition name="wpfade">
<template v-if="page == 'apple-account-settings'">
<apple-account-settings></apple-account-settings>
</template>
</transition>
<!-- About -->
<transition name="wpfade">
<template v-if="page == 'about'">
<about-page></about-page>
</template>
</transition>
<!-- Artist Page --> <!-- Artist Page -->
<transition name="wpfade"> <transition name="wpfade">
<template v-if="page == 'artist-page' && artistPage.data.attributes"> <template v-if="page == 'artist-page' && artistPage.data.attributes">
@ -427,7 +486,9 @@
</transition> </transition>
<!-- Library - Songs --> <!-- Library - Songs -->
<transition name="wpfade" v-on:enter="getLibrarySongsFull()"> <transition name="wpfade" v-on:enter="getLibrarySongsFull()">
<%- include('pages/library-songs') %> <template v-if="page == 'library-songs'">
<cider-library-songs :data="library.songs"></cider-library-songs>
</template>
</transition> </transition>
<!-- Library - Albums --> <!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getLibraryAlbumsFull(null, 1); searchLibraryAlbums(1);"> <transition name="wpfade" v-on:enter="getLibraryAlbumsFull(null, 1); searchLibraryAlbums(1);">
@ -456,7 +517,7 @@
</div> </div>
<transition name="drawertransition"> <transition name="drawertransition">
<div class="app-drawer" v-if="drawer.open"> <div class="app-drawer" v-if="drawer.open && drawer.panel == 'lyrics'">
<div class="bgArtworkMaterial"> <div class="bgArtworkMaterial">
<div class="bg-artwork-container"> <div class="bg-artwork-container">
<img class="bg-artwork a" :src="$store.state.artwork.playerLCD"> <img class="bg-artwork a" :src="$store.state.artwork.playerLCD">
@ -466,21 +527,32 @@
<lyrics-view v-if="drawer.panel == 'lyrics'" :time="lyriccurrenttime" :lyrics="lyrics" <lyrics-view v-if="drawer.panel == 'lyrics'" :time="lyriccurrenttime" :lyrics="lyrics"
:richlyrics="richlyrics"></lyrics-view> :richlyrics="richlyrics"></lyrics-view>
<div v-if="drawer.panel == 'lyrics'" class="lyric-footer"> <div v-if="drawer.panel == 'lyrics'" class="lyric-footer">
<button class="md-btn" @click="modularUITest(!fullscreenLyrics)">{{fullscreenLyrics ? "Default View":'Fullscreen View'}}</button> <button class="md-btn" @click="modularUITest(!fullscreenLyrics)">{{fullscreenLyrics ?
"Default View":'Fullscreen View'}}
</button>
</div> </div>
</div>
</transition>
<transition name="drawertransition">
<div class="app-drawer" v-if="drawer.open && drawer.panel == 'queue'">
<cider-queue ref="queue" v-if="drawer.panel == 'queue'"></cider-queue> <cider-queue ref="queue" v-if="drawer.panel == 'queue'"></cider-queue>
</div> </div>
</transition> </transition>
<cider-menu-panel v-if="menuPanel.visible">
</cider-menu-panel>
</div> </div>
</div> </div>
</transition> </transition>
<transition name="fsModeSwitch"> <transition name="fsModeSwitch">
<div class="fullscreen-view-container" v-if="appMode == 'fullscreen'"> <div class="fullscreen-view-container" v-if="appMode == 'fullscreen'">
<fullscreen-view :image="currentArtUrl.replace('50x50', '600x600')" :time="lyriccurrenttime" :lyrics="lyrics" :richlyrics="richlyrics"></fullscreen-view> <fullscreen-view :image="currentArtUrl.replace('50x50', '600x600')" :time="lyriccurrenttime"
:lyrics="lyrics" :richlyrics="richlyrics"></fullscreen-view>
</div> </div>
</transition> </transition>
<transition name="wpfade"> <transition name="wpfade">
<div class="bg-artwork-container" :class="{noanimation: (!cfg.visual.bg_artwork_rotation || !animateBackground)}"> <div class="bg-artwork-container"
:class="{noanimation: (!cfg.visual.bg_artwork_rotation || !animateBackground)}">
<img @load="chrome.artworkReady = true" class="bg-artwork a " <img @load="chrome.artworkReady = true" class="bg-artwork a "
> >
<img class="bg-artwork b" <img class="bg-artwork b"
@ -528,6 +600,11 @@
</div> </div>
</div> </div>
<!-- Apple Settings Page -->
<%- include('pages/apple-account-settings') %>
<!-- Library - Songs -->
<%- include('pages/library-songs') %>
<!-- Media Item Artwork--> <!-- Media Item Artwork-->
<%- include("components/mediaitem-artwork"); %> <%- include("components/mediaitem-artwork"); %>
<!-- Browse --> <!-- Browse -->
@ -563,6 +640,9 @@
<!-- Search --> <!-- Search -->
<%- include('pages/search') %> <%- include('pages/search') %>
<!-- About -->
<%- include('pages/about') %>
<script type="text/x-template" id="am-musiccovershelf"> <script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1> <h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script> </script>
@ -576,6 +656,8 @@
</button> </button>
</script> </script>
<!-- Menu Panel -->
<%- include('components/menu-panel') %>
<!-- Playlist Listing --> <!-- Playlist Listing -->
<%- include('components/sidebar-playlist') %> <%- include('components/sidebar-playlist') %>
<!-- Spatial Properties --> <!-- Spatial Properties -->

View file

@ -1,19 +1,107 @@
Major thanks to the Cider Development Team and all of our contributors. <script type="text/x-template" id="about-page">
<div class="content-inner about-page">
<div class="row">
<div class="col">
<img src="assets/banner.png" alt="Cider Logo" style="display:block;margin:0 auto;width: 500px;">
<p style="text-align: center">Major thanks to the Cider Collective Team and all of our contributors.</p>
<p>"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link" <p style="text-align: center">"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank" target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a> rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
All Rights All Rights
Reserved.</p> Reserved.</p>
<hr>
<h3>Sponsor this project</h3>
<button onclick="window.open('https://ko-fi.com/cryptofyre')" class="md-btn sponsorBtn"><img src="./assets/ko_fi.svg"/>Ko-fi</button>
<button onclick="window.open('https://opencollective.com/ciderapp')" class="md-btn sponsorBtn"><img src="./assets/open_collective.svg"/>Open Collective</button>
cryptofyre - Developer - https://github.com/cryptofyre </div>
Core - Developer - https://github.com/coredev-uk <div class="col">
Quacksire - Developer - https://github.com/child-duckling <div class="row">
booploops - Developer - https://github.com/booploops <div class="col">
vapormusic - Developer - https://github.com/vapormusic <h3>Cider Team</h3>
Void - Social Communications Team - https://twitter.com/MoonyVoid <button class="md-btn teamBtn" @click="window.open(member.link)" v-for="member in team">
NoseySG - Social Communications Team - https://twitter.com/noah_grose <img :src="member.avatar"/>
<div class="row" style="width:100%;">
<img class="md-contributors" <div class="col" style="text-align: left">
{{ member.name }}
</div>
<div class="col-auto">
<b>{{ member.role }}</b>
</div>
</div>
</button>
</div>
</div>
<div class="row">
<div class="col">
<h3></h3>
<h3>Contributors</h3>
<img class="md-contributors"
style="cursor:pointer;width:100%;"
onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')" onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
src="https://contrib.rocks/image?repo=ciderapp/Cider"/> src="https://contrib.rocks/image?repo=ciderapp/Cider"/>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('about-page', {
template: '#about-page',
data: function () {
return {
window: window,
team: [
{
name: 'cryptofyre',
link: 'https://github.com/cryptofyre',
role: 'Developer',
avatar: 'https://avatars.githubusercontent.com/u/33162551?v=4'
},
{
name: 'Core',
link: 'https://github.com/coredev-uk',
role: 'Developer',
avatar: 'https://avatars.githubusercontent.com/u/64542347?v=4'
},
{
name: 'Quacksire',
link: 'https://github.com/quacksire',
role: 'Developer',
avatar: 'https://avatars.githubusercontent.com/u/19170969?v=4'
},
{
name: 'booploops',
link: 'https://github.com/booploops',
role: 'Developer',
avatar: 'https://avatars.githubusercontent.com/u/49113086?v=4'
},
{
name: 'vapormusic',
link: 'https://github.com/vapormusic',
role: 'Developer',
avatar: 'https://avatars.githubusercontent.com/u/27716185?v=4'
},
{
name: 'Void',
link: 'https://twitter.com/MoonyVoid',
role: 'Social Team',
avatar: 'https://pbs.twimg.com/profile_images/1226463559472816129/8LScNYED_400x400.jpg'
},
{
name: 'NoseySG',
link: 'https://twitter.com/noah_grose',
role: 'Social Team',
avatar: 'https://pbs.twimg.com/profile_images/1422541289837535239/qg-aaoP9_400x400.jpg'
}
]
}
},
methods: {
}
});
</script>

View file

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

View file

@ -8,7 +8,7 @@
<h3>Your Artists Feed</h3> <h3>Your Artists Feed</h3>
</div> </div>
</div> </div>
<div class="well" style="margin-top:0px;"> <div class="well" style="margin-top:0;">
<template v-if="artistFeed.length > 0"> <template v-if="artistFeed.length > 0">
<mediaitem-list-item v-for="item in artistFeed" :item="item"></mediaitem-list-item> <mediaitem-list-item v-for="item in artistFeed" :item="item"></mediaitem-list-item>
</template> </template>

View file

@ -52,10 +52,10 @@
</div> </div>
<div class="col" v-if="data.views['top-songs']"> <div class="col" v-if="data.views['top-songs']">
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<h3>Top Songs</h3> <h3>Top Songs</h3>
</div> </div>
<div class="col-auto flex-center" v-if="data.views['top-songs'].data.length >= 10" style="padding:0px;"> <div class="col-auto flex-center" v-if="data.views['top-songs'].data.length >= 10" style="padding:0;">
<button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - Top Songs', 'top-songs')">See All</button> <button class="cd-btn-seeall" @click="app.showArtistView(data.id, data.attributes.name + ' - Top Songs', 'top-songs')">See All</button>
</div> </div>
</div> </div>
@ -138,12 +138,14 @@
let followAction = "follow" let followAction = "follow"
let followActions = { let followActions = {
follow: { follow: {
icon: "./assets/feather/plus-circle.svg",
name: "Follow Artist", name: "Follow Artist",
action: ()=>{ action: ()=>{
self.app.cfg.home.followedArtists.push(self.data.id) self.app.cfg.home.followedArtists.push(self.data.id)
} }
}, },
unfollow: { unfollow: {
icon: "./assets/feather/x-circle.svg",
name: "Unfollow Artist", name: "Unfollow Artist",
action: ()=>{ action: ()=>{
let index = self.app.cfg.home.followedArtists.indexOf(self.data.id) let index = self.app.cfg.home.followedArtists.indexOf(self.data.id)
@ -156,9 +158,10 @@
if(this.app.cfg.home.followedArtists.includes(self.data.id)) { if(this.app.cfg.home.followedArtists.includes(self.data.id)) {
followAction = "unfollow" followAction = "unfollow"
} }
CiderContextMenu.Create(event, { app.showMenuPanel({
items: [ items: [
{ {
icon: "./assets/feather/play.svg",
name: "Play Artist Radio", name: "Play Artist Radio",
action: ()=>{ action: ()=>{
app.mk.setStationQueue({artist:self.data.id}).then(()=>{ app.mk.setStationQueue({artist:self.data.id}).then(()=>{
@ -168,13 +171,14 @@
}, },
followActions[followAction], followActions[followAction],
{ {
icon: "./assets/feather/share.svg",
name: "Share", name: "Share",
action: ()=>{ action: ()=>{
self.app.copyToClipboard(self.data.attributes.url) self.app.copyToClipboard(self.data.attributes.url)
} }
} }
] ]
}) }, event)
}, },
getArtistPalette(artist) { getArtistPalette(artist) {
if (artist["attributes"]["artwork"]) { if (artist["attributes"]["artwork"]) {

View file

@ -16,7 +16,7 @@
<div style="width: 260px;height:260px;"> <div style="width: 260px;height:260px;">
<mediaitem-artwork <mediaitem-artwork
:video-priority="true" :video-priority="true"
:url="(data.attributes != null && data.attributes.artwork && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0) ? data.relationships.tracks.data[0].attributes.artwork.url ?? '':'')" :url="(data.attributes != null && data.attributes.artwork != null) ? data.attributes.artwork.url : ((data.relationships != null && data.relationships.tracks.data.length > 0 && data.relationships.tracks.data[0].attributes != null) ? ((data.relationships.tracks.data[0].attributes.artwork != null)? data.relationships.tracks.data[0].attributes.artwork.url : ''):'')"
:video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' " :video="(data.attributes != null && data.attributes.editorialVideo != null) ? (data.attributes.editorialVideo.motionDetailSquare ? data.attributes.editorialVideo.motionDetailSquare.video : (data.attributes.editorialVideo.motionSquareVideo1x1 ? data.attributes.editorialVideo.motionSquareVideo1x1.video : '')) : '' "
size="260" size="260"
></mediaitem-artwork> ></mediaitem-artwork>
@ -40,10 +40,11 @@
@click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''"> @click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''">
{{getArtistName(data)}} {{getArtistName(data)}}
</div> </div>
<div class="playlist-desc" v-if="getDescription(data) != ''"> <div class="playlist-desc" v-if="data.attributes.description && (data.attributes.description.standard || data.attributes.description.short)">
<div class="content" <div v-if="data.attributes.description.short" class="content" v-html="data.attributes.description.short"></div>
v-html="getDescription(data)"></div> <div v-else-if="data.attributes.description.standard" class="content" v-html="data.attributes.description.standard"></div>
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded"> <button v-if="data.attributes.description.short" class="more-btn"
@click="editorialNotesExpanded = !editorialNotesExpanded">
More More
</button> </button>
</div> </div>
@ -58,18 +59,30 @@
</div> </div>
</template> </template>
<div class="playlist-controls"> <div class="playlist-controls">
<button class="wr-btn" style="min-width: 120px;" <button class="md-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 0; play()"> @click="app.mk.shuffleMode = 0; play()">
Play Play
</button> </button>
<button class="wr-btn" style="min-width: 120px;" <button class="md-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 1;play()"> @click="app.mk.shuffleMode = 1;play()">
Shuffle Shuffle
</button> </button>
<button class="wr-btn" style="min-width: 120px;" v-if="inLibrary!=null" <button class="md-btn" style="min-width: 120px;" v-if="inLibrary!=null && confirm!=true"
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString())"> @click="confirmButton()">
{{ (!inLibrary) ? "Add to Library" : "Remove from Library" }} {{ (!inLibrary) ? "Add to Library" : "Remove from Library" }}
</button> </button>
<button class="md-btn" style="min-width: 120px;" v-if="confirm==true"
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString()) ">
Confirm?
</button>
<button class="playlist-more" @click="menu">
<div style=" margin-top: -1px;
margin-left: -5px;
width: 36px;
height: 36px;">
<%- include("../svg/more.svg") %>
</div>
</button>
</div> </div>
</div> </div>
</div> </div>
@ -88,7 +101,8 @@
<div class="friends-info" v-if="itemBadges.length != 0"> <div class="friends-info" v-if="itemBadges.length != 0">
<div class="well"> <div class="well">
<div class="badge-container"> <div class="badge-container">
<div class="socialBadge" :title="`${badge.attributes.name} - @${badge.attributes.handle}`" v-for="badge in itemBadges"> <div class="socialBadge" :title="`${badge.attributes.name} - @${badge.attributes.handle}`"
v-for="badge in itemBadges">
<mediaitem-artwork <mediaitem-artwork
:url="badge.attributes.artwork.url" :url="badge.attributes.artwork.url"
:size="60"></mediaitem-artwork> :size="60"></mediaitem-artwork>
@ -97,7 +111,7 @@
</div> </div>
</div> </div>
<div class="playlist-time"> <div class="playlist-time">
{{getFormattedDate(data.attributes.releaseDate)}} {{getFormattedDate()}}
</div> </div>
<div class="playlist-time total">{{app.getTotalTime()}}</div> <div class="playlist-time total">{{app.getTotalTime()}}</div>
<div class="playlist-time item-navigate" @click="app.searchAndNavigate(data,'recordLabel') " <div class="playlist-time item-navigate" @click="app.searchAndNavigate(data,'recordLabel') "
@ -109,16 +123,19 @@
</div> </div>
</script> </script>
<script> <script>
Vue.component('cider-playlist', { Vue.component('cider-playlist', {
template: "#cider-playlist", template: "#cider-playlist",
props: ["data"], props: ["data"],
data: function () { data: function () {
return { return {
editorialNotesExpanded: false, editorialNotesExpanded: false,
drag: false, drag: false,
nameEditing: false, nameEditing: false,
inLibrary: null, inLibrary: null,
confirm: false,
app: this.$root, app: this.$root,
itemBadges: [], itemBadges: [],
badgesRequested: false badgesRequested: false
@ -161,24 +178,14 @@
} }
}) })
}, },
getDescription(data) { confirmButton() {
console.log(data.attributes) // Return button to normal state after 3 seconds
if (data.attributes.editorialNotes) {
if (data.attributes.editorialNotes.hasOwnProperty('short')) { this.confirm = true
return data.attributes.editorialNotes.short setTimeout(() => this.confirm = false, 3000);
} else if (data.attributes.editorialNotes.hasOwnProperty('standard')) {
return data.attributes.editorialNotes.standard
}
} else if (data.attributes.description) {
if (data.attributes.description.hasOwnProperty('short')) {
return data.attributes.description.short
} else if (data.attributes.description.hasOwnProperty('standard')) {
return data.attributes.description.standard
}
}
return ''
}, },
getArtistName(data) { getArtistName(data) {
console.log(data.attributes)
if (data.attributes.artistName) { if (data.attributes.artistName) {
return data.attributes.artistName return data.attributes.artistName
} else if (data.attributes.artist) { } else if (data.attributes.artist) {
@ -192,8 +199,12 @@
async isInLibrary() { async isInLibrary() {
if (this.data.type && !this.data.type.includes("library")) { if (this.data.type && !this.data.type.includes("library")) {
// please keep using vars here // please keep using vars here
var params = { "fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" } const params = {
var res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params); "fields[playlists]": "inLibrary",
"fields[albums]": "inLibrary",
"relate": "library"
};
const res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params);
this.inLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false this.inLibrary = (res && res.attributes && res.attributes.inLibrary) ? res.attributes.inLibrary : false
console.log(res) console.log(res)
} else { } else {
@ -203,7 +214,7 @@
editPlaylist() { editPlaylist() {
this.app.editPlaylist(this.data.id, this.data.attributes.name); this.app.editPlaylist(this.data.id, this.data.attributes.name);
this.app.playlists.listing.forEach(playlist => { this.app.playlists.listing.forEach(playlist => {
if (playlist.id == this.data.id) { if (playlist.id === this.data.id) {
playlist.attributes.name = this.data.attributes.name playlist.attributes.name = this.data.attributes.name
} }
}) })
@ -212,21 +223,23 @@
addToLibrary(id) { addToLibrary(id) {
app.mk.addToLibrary(id) app.mk.addToLibrary(id)
this.inLibrary = true this.inLibrary = true
this.confirm = false
}, },
async removeFromLibrary(id) { async removeFromLibrary(id) {
var params = { "fields[somgs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" } const params = {"fields[somgs]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library"};
var id = this.data.id ?? this.data.attributes.playParams.id var id = this.data.id ?? this.data.attributes.playParams.id
var res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params); const res = await app.mkapi(this.data.attributes.playParams.kind ?? this.data.type, this.data.attributes.playParams.isLibrary ?? false, this.data.attributes.playParams.id ?? this.data.id, params);
if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) { if (res && res.relationships && res.relationships.library && res.relationships.library.data && res.relationships.library.data.length > 0) {
id = res.relationships.library.data[0].id id = res.relationships.library.data[0].id
} }
let kind = this.data.attributes.playParams.kind ?? this.data.type ?? ''; let kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; const truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({ [truekind]: id }) app.mk.api.library.remove({[truekind]: id})
this.inLibrary = false this.inLibrary = false
this.confirm = false
}, },
editPlaylistName() { editPlaylistName() {
if (this.data.attributes.canEdit && this.data.type == "library-playlists") { if (this.data.attributes.canEdit && this.data.type === "library-playlists") {
this.nameEditing = true this.nameEditing = true
setTimeout(() => { setTimeout(() => {
document.querySelector(".nameEdit").focus() document.querySelector(".nameEdit").focus()
@ -295,20 +308,69 @@
} }
}) })
}, },
menu(event) {
app.showMenuPanel({
items: {
"share": {
name: "Share",
icon: "./assets/feather/share.svg",
action: () => {
let route = ""
switch (this.data.type) {
case 'albums':
route = `/v1/catalog/${app.mk.storefrontId}/albums/${this.data.id}`
break;
case 'playlists':
route = `/v1/catalog/${app.mk.storefrontId}/playlists/${this.data.id}`
break;
case "library-playlists":
route = `/v1/me/library/playlists/${this.data.id}/catalog`
break
case "library-albums":
route = `/v1/me/library/albums/${this.data.id}/catalog`
break
}
if (route === '') {
return
}
app.mk.api.v3.music(route).then(res => {
console.log(res.data.data[0].attributes.url)
app.copyToClipboard(res.data.data[0].attributes.url)
})
}
}
}
}, event)
},
getItemParent: function (data) { getItemParent: function (data) {
kind = data.attributes.playParams.kind; kind = data.attributes.playParams.kind;
id = data.attributes.playParams.id; id = data.attributes.playParams.id;
return `${kind}:${id}` return `${kind}:${id}`
}, },
getFormattedDate: function (date) { getFormattedDate: function () {
let date = (this.data.attributes.releaseDate ?? (this.data.attributes.lastModifiedDate ?? (this.data.attributes.dateAdded ?? '')))
let prefix = '';
if (date == null || date === "") return ""; if (date == null || date === "") return "";
switch (date) {
case this.data.attributes.releaseDate:
prefix = 'Released '
break;
case this.data.attributes.lastModifiedDate:
prefix = 'Updated '
break;
case this.data.attributes.dateAdded:
prefix = 'Added '
break;
}
let month, year;
try { try {
var releaseDate = new Date(date); const releaseDate = new Date(date);
month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(releaseDate); console.log(date, releaseDate)
month = new Intl.DateTimeFormat('en-US', {month: 'long'}).format(releaseDate);
date = releaseDate.getDate(); date = releaseDate.getDate();
year = releaseDate.getFullYear(); year = releaseDate.getFullYear();
return date + " " + month + " " + year; return prefix + date + " " + month + " " + year;
} catch (e) { } catch (e) {
return "" return ""
} }
@ -322,27 +384,29 @@
array[j] = temp; array[j] = temp;
} }
} }
var id = this.data.attributes.playParams.id ?? this.data.id;
const id = this.data.attributes.playParams.id ?? this.data.id;
//console.log("1") //console.log("1")
var kind = this.data.attributes.playParams.kind ?? this.data.type ?? ''; const kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
//console.log("1") //console.log("1")
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind; const truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
let query = (this.data ?? app.showingPlaylist).relationships.tracks.data.map(item => new MusicKit.MediaItem(item)); let query = (this.data ?? app.showingPlaylist).relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
app.mk.stop().then(function () { app.mk.stop().then(function () {
app.mk.setQueue({[truekind]: [id]}).then(function () { app.mk.setQueue({[truekind]: [id]}).then(function () {
app.mk.play().then(function () { app.mk.play().then(function () {
if (query.length > 100) { if (query.length > 100) {
let u = query.slice(100); if (app.mk.shuffleMode == 1) { shuffleArray(u) } let u = query.slice(100);
app.mk.queue.append(u)} if (app.mk.shuffleMode == 1) {
shuffleArray(u)
}
app.mk.queue.append(u)
}
}) })
}) })
}) })
} }
} }

View file

@ -21,7 +21,7 @@
<button class="cd-btn-seeall" @click="app.appRoute('artist-feed')">See All</button> <button class="cd-btn-seeall" @click="app.appRoute('artist-feed')">See All</button>
</div> </div>
</div> </div>
<div class="well artistfeed-well" style="margin-top:0px;"> <div class="well artistfeed-well" style="margin-top:0;">
<template v-if="artistFeed.length > 0"> <template v-if="artistFeed.length > 0">
<mediaitem-list-item v-for="item in artistFeed.limit(6)" :item="item"></mediaitem-list-item> <mediaitem-list-item v-for="item in artistFeed.limit(6)" :item="item"></mediaitem-list-item>
</template> </template>
@ -55,7 +55,14 @@
</div> </div>
<div class="row" v-if="friendsListeningTo && friendsListeningTo.length > 0"> <div class="row" v-if="friendsListeningTo && friendsListeningTo.length > 0">
<div class="col"> <div class="col">
<div class="row">
<div class="col nopadding">
<h3>Friends Listening To</h3> <h3>Friends Listening To</h3>
</div>
<div class="col-auto nopadding flex-center">
<button class="cd-btn-seeall" @click="app.showSocialListeningTo()">See All</button>
</div>
</div>
<div class="well"> <div class="well">
<template v-if="isSectionReady('friendsListeningTo')"> <template v-if="isSectionReady('friendsListeningTo')">
<mediaitem-square kind="small" v-for="item in friendsListeningTo" <mediaitem-square kind="small" v-for="item in friendsListeningTo"

View file

@ -1,7 +1,7 @@
<template v-if="page == 'library-albums'"> <template v-if="page == 'library-albums'">
<div class="content-inner"> <div class="content-inner">
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<h1 class="header-text">Albums</h1> <h1 class="header-text">Albums</h1>
</div> </div>
<div class="col-auto"> <div class="col-auto">
@ -9,8 +9,8 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;"> <div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" <input type="search"
style="width:100%;" style="width:100%;"

View file

@ -1,13 +1,13 @@
<div class="content-inner"> <div class="content-inner">
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<h1 class="header-text">Artists</h1> <h1 class="header-text">Artists</h1>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;"> <div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" <input type="search"
style="width:100%;" style="width:100%;"

View file

@ -1,7 +1,7 @@
<template v-if="page == 'library-recentlyadded'"> <template v-if="page == 'library-recentlyadded'">
<div class="content-inner"> <div class="content-inner">
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<h1 class="header-text">Recently Added</h1> <h1 class="header-text">Recently Added</h1>
</div> </div>
<div class="col-auto"> <div class="col-auto">
@ -10,8 +10,8 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;"> <div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" <input type="search"
style="width:100%;" style="width:100%;"

View file

@ -1,46 +1,80 @@
<template v-if="page == 'library-songs'">
<div class="content-inner">
<script type="text/x-template" id="cider-library-songs">
<div class="content-inner library-page">
<div class="library-header">
<div class="row"> <div class="row">
<div class="col" style="padding:0px;"> <div class="col" style="padding:0;">
<h1 class="header-text">Songs</h1> <div class="search-input-container" style="width:100%;margin: 16px 0;">
</div>
<div class="col-auto">
<button v-if="library.songs.downloadState == 2" @click="getLibrarySongsFull(true)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" <input type="search"
style="width:100%;" style="width:100%;"
spellcheck="false" spellcheck="false"
placeholder="Search..." placeholder="Search..."
@input="searchLibrarySongs" @input="$root.searchLibrarySongs"
v-model="library.songs.search" class="search-input"> v-model="library.songs.search" class="search-input">
</div> </div>
</div> </div>
<div class="col-auto flex-center"> <div class="col-auto flex-center">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<select class="md-select" v-model="library.songs.sorting" @change="searchLibrarySongs()"> <select class="md-select" v-model="prefs.sort" @change="$root.searchLibrarySongs()">
<optgroup label="Sort By"> <optgroup label="Sort By">
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option> <option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option>
</optgroup> </optgroup>
</select> </select>
</div> </div>
<div class="col"> <div class="col">
<select class="md-select" v-model="library.songs.sortOrder" @change="searchLibrarySongs()"> <select class="md-select" v-model="prefs.sortOrder" @change="$root.searchLibrarySongs()">
<optgroup label="Sort Order"> <optgroup label="Sort Order">
<option value="asc">Ascending</option> <option value="asc">Ascending</option>
<option value="desc">Descending</option> <option value="desc">Descending</option>
</optgroup> </optgroup>
</select> </select>
</div> </div>
<div class="col">
<select class="md-select" v-model="prefs.size" @change="$root.searchLibrarySongs()">
<optgroup label="Size">
<option value="normal">Normal</option>
<option value="compact">Compact</option>
</optgroup>
</select>
</div> </div>
</div> </div>
</div> </div>
<div class="col-auto flex-center">
<button v-if="library.songs.downloadState == 2" @click="$root.getLibrarySongsFull(true)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
<button v-else class="reload-btn" style="opacity: 0.8;pointer-events: none">
<div class="spinner"></div>
</button>
</div>
</div>
</div>
<div v-if="library.songs.downloadState == 3">Library contains no songs.</div> <div v-if="library.songs.downloadState == 3">Library contains no songs.</div>
<div class="well" :key="1" v-if="prefs.size == 'compact'">
<mediaitem-list-item class-list="compact" :item="item" :parent="'librarysongs'" :index="index" :show-meta-data="true" :show-library-status="false" v-for="(item, index) in library.songs.displayListing"></mediaitem-list-item>
</div>
<div class="well" :key="2" v-else>
<mediaitem-list-item :item="item" :parent="'librarysongs'" :index="index" :show-meta-data="true" :show-library-status="false" v-for="(item, index) in library.songs.displayListing"></mediaitem-list-item> <mediaitem-list-item :item="item" :parent="'librarysongs'" :index="index" :show-meta-data="true" :show-library-status="false" v-for="(item, index) in library.songs.displayListing"></mediaitem-list-item>
</div> </div>
</template> </div>
</script>
<script>
Vue.component('cider-library-songs', {
template: '#cider-library-songs',
data: function () {
return {
library: this.$root.library,
mediaItemSize: "compact",
prefs: this.$root.cfg.libraryPrefs.songs
}
},
methods: {
sayHello: function () {
alert('Hello world!');
}
}
});
</script>

View file

@ -1,6 +1,6 @@
<div class="content-inner"> <div class="content-inner">
<div class="row"> <div class="row">
<div class="col" style="padding:0px"> <div class="col" style="padding:0">
<h1 class="header-text">Made For You</h1> <h1 class="header-text">Made For You</h1>
</div> </div>
</div> </div>

View file

@ -90,18 +90,6 @@
<input type="checkbox" switch v-model="app.cfg.visual.bg_artwork_rotation"/> <input type="checkbox" switch v-model="app.cfg.visual.bg_artwork_rotation"/>
</div> </div>
</div> </div>
<div class="md-option-line">
<div class="md-option-segment">
Window Transparency
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select" style="width:180px;" v-model="app.cfg.visual.window_transparency" @change="app.getHTMLStyle()">
<option value="default">Default</option>
<option value="acrylic" v-if="app.platform == 'win32'">Acrylic</option>
<option value="disabled">Disabled</option>
</select>
</div>
</div>
<div class="md-option-line"> <div class="md-option-line">
<div class="md-option-segment"> <div class="md-option-segment">
Hardware Acceleration<br> Hardware Acceleration<br>
@ -447,7 +435,7 @@
<div class="md-option-segment"> <div class="md-option-segment">
LastFM Scrobbling LastFM Scrobbling
</div> </div>
<div class="md-option-segment md-option-segment_auto"> <div class="md-option-segment md-btn md-option-segment_auto">
<label class="list-button list-element" id="lfmConnect" ref="lfmConnect" <label class="list-button list-element" id="lfmConnect" ref="lfmConnect"
onclick="app.LastFMAuthenticate()">Connect</label> onclick="app.LastFMAuthenticate()">Connect</label>
</div> </div>
@ -476,6 +464,20 @@
<input type="checkbox" v-model="app.cfg.lastfm.enabledRemoveFeaturingArtists" switch/> <input type="checkbox" v-model="app.cfg.lastfm.enabledRemoveFeaturingArtists" switch/>
</div> </div>
</div> </div>
<div class="md-option-header">
<span>Experimental</span>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Compact UI
</div>
<div class="md-option-segment md-option-segment_auto" >
<button class="md-btn" :class="{'md-btn-primary': app.cfg.advanced.experiments.includes('compactui')}" @click="app.cfg.advanced.experiments.includes('compactui') ? removeExperiment('compactui') : addExperiment('compactui')">
{{app.cfg.advanced.experiments.includes('compactui') ? 'Enabled' : 'Disabled'}}
</button>
</div>
</div>
<div style="opacity: 0.5; pointer-events: none">
<div class="md-option-header"> <div class="md-option-header">
<span>Unfinished / Non Functional</span> <span>Unfinished / Non Functional</span>
</div> </div>
@ -552,6 +554,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</script> </script>
<script> <script>
@ -573,6 +576,12 @@
} }
}, },
methods: { methods: {
addExperiment(flag) {
app.cfg.advanced.experiments.push(flag);
},
removeExperiment(flag) {
app.cfg.advanced.experiments.splice(app.cfg.advanced.experiments.indexOf(flag), 1);
},
toggleAudioContext: function(){ toggleAudioContext: function(){
if (app.cfg.advanced.AudioContext){ if (app.cfg.advanced.AudioContext){
CiderAudio.init(); CiderAudio.init();

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 214 214" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-79.4309,9.53659)">
<path d="M250.163,76.146C250.163,52.589 231.037,33.463 207.48,33.463L122.114,33.463C98.556,33.463 79.431,52.589 79.431,76.146L79.431,161.512C79.431,185.07 98.556,204.195 122.114,204.195L207.48,204.195C231.037,204.195 250.163,185.07 250.163,161.512L250.163,76.146ZM235.163,83.646C235.163,64.228 219.398,48.463 199.98,48.463L129.614,48.463C110.196,48.463 94.431,64.228 94.431,83.646L94.431,154.012C94.431,173.43 110.196,189.195 129.614,189.195L199.98,189.195C219.398,189.195 235.163,173.43 235.163,154.012L235.163,83.646Z" style="fill:currentColor;"/>
</g>
<g transform="matrix(0.906286,0,0,0.906286,-20.9871,-22.3274)">
<path d="M250.163,76.146C250.163,52.589 231.037,33.463 207.48,33.463L122.114,33.463C98.556,33.463 79.431,52.589 79.431,76.146L79.431,161.512C79.431,185.07 98.556,204.195 122.114,204.195L207.48,204.195C231.037,204.195 250.163,185.07 250.163,161.512L250.163,76.146Z" style="fill:white;"/>
</g>
<g transform="matrix(0.333526,0,0,0.333526,43.0167,0)">
<path d="M511.8,130.7C511.8,115 510.5,99.3 506.7,84C500,56 484,34.7 460.2,19C448,11 434.5,6.1 420.1,3.5C409.1,1.5 397.9,0.6 386.8,0.3L384.1,0L127.6,0C124.4,0.3 121.1,0.4 117.9,0.6C102,1.5 86.2,3.2 71.1,9.2C42.7,20.4 22.1,40.1 10.1,68.4C5.9,78 3.8,88.2 2.3,98.5C1.1,106.8 0.4,115.3 0.1,123.7L-0.1,125.7L-0.1,386.4C0.1,389.4 0.2,392.4 0.4,395.4C1.5,412.8 3.7,430.1 11.1,446.1C24.9,476.4 48.2,496.3 80.1,505.8C89,508.6 98.3,509.8 107.7,510.6C119.5,511.8 131.4,511.9 143.2,511.9L378.5,511.9C389.7,511.9 400.8,511.2 412,509.7C429.6,507.5 446.1,502.3 461,492.5C478.9,480.7 492.4,465 501.1,445.4C505.1,436.4 507.3,426.8 509,417.2C511.4,402.8 511.9,388.2 511.9,373.6C511.8,292.6 511.9,211.6 511.8,130.6L511.8,130.7ZM374.8,93.4L374.8,337.6C374.8,346.5 373.6,355.3 369.6,363.3C363.4,375.9 353.4,383.8 340,387.6C332.6,389.8 324.9,390.9 317.2,391.3C297,392.3 279.4,378.6 275.8,358.6C272.7,342.1 280.6,323.9 298,315.4C304.8,312.1 312.2,310.1 319.7,308.6C327.8,306.9 335.9,305.3 343.9,303.4C349.8,302.1 353.6,298.5 354.8,292.4L355.2,288.3L355.2,172.1L354.6,168.2C353.8,165 351.4,163 348.1,163.2C344.7,163.4 341.4,163.9 338,164.6C321.7,167.8 305.5,171 289.3,174.3L210.4,190.2L209.3,190.5C203.4,192.2 201.3,194.8 201,201L201,203.7C200.9,259.2 201,314.7 200.9,370.2C200.9,379.2 199.9,388 196.3,396.4C190.4,410.1 179.9,418.7 165.7,422.7C158.2,424.9 150.5,426.1 142.7,426.4C122.3,427.2 105.3,413.6 101.8,393.5C98.8,376.2 106.7,357.5 126.4,349.2C134.1,346 142,344.3 150.1,342.6C156.2,341.4 162.4,340.1 168.4,338.9C176.6,337.2 180.8,332 181.2,323.7L181.2,131C181.2,128.3 181.5,125.7 182.1,123.1C183.6,117 187.9,113.5 193.8,112.1C199.2,110.7 204.8,109.7 210.3,108.5C226,105.3 241.5,102.2 257.2,99.1L305.6,89.3C319.9,86.5 334.2,83.6 348.5,80.7C353.2,79.8 357.9,78.8 362.7,78.4C369.3,77.8 373.9,82 374.5,88.7C374.7,90.3 374.8,91.9 374.8,93.4Z" style="fill:currentColor;fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -2,12 +2,7 @@
<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
fill="white" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> fill="white" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css"> <path id="XMLID_11_" d="M418.5,139.4H232.4v139.8h186.1V139.4z M464.8,46.7H46.3C20.5,46.7,0,68.1,0,93.1v325.9
.st0{fill:#333333;}
</style>
<g id="XMLID_6_">
<path id="XMLID_11_" class="st0" d="M418.5,139.4H232.4v139.8h186.1V139.4z M464.8,46.7H46.3C20.5,46.7,0,68.1,0,93.1v325.9
c0,25.8,21.4,46.3,46.3,46.3h419.4c25.8,0,46.3-20.5,46.3-46.3V93.1C512,67.2,490.6,46.7,464.8,46.7z M464.8,418.9H46.3V92.2h419.4 c0,25.8,21.4,46.3,46.3,46.3h419.4c25.8,0,46.3-20.5,46.3-46.3V93.1C512,67.2,490.6,46.7,464.8,46.7z M464.8,418.9H46.3V92.2h419.4
v326.8H464.8z"/> v326.8H464.8z"/>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 627 B

Before After
Before After