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

@ -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)))
break;
if (g.hdcpLevel === "NONE"){
n.push(g)}
let cpc = g.allowedCPCMap ? JSON.stringify(g.allowedCPCMap) : "null";
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 {
variantMediaOptions: n,
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.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 += menudata.items[i].name
@ -290,7 +290,18 @@ const app = new Vue({
},
socialBadges: {
badgeMap: {},
version: ""
version: "",
mediaItems: [],
mediaItemDLState: 0 // 0 = not started, 1 = in progress, 2 = complete
},
menuPanel: {
visible: false,
event: null,
content: {
name: "",
items: {},
headerItems: {}
}
}
},
watch: {
@ -315,6 +326,53 @@ const app = new Vue({
},
},
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) {
let response = await fetch(url);
let data = await response.text();
@ -353,22 +411,11 @@ const app = new Vue({
history.forward()
},
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("body").classList.add("notransparency")
// document.querySelector("body").style.background = "#222";
break;
}
document.querySelector("html").style.background = "#222";
document.querySelector("body").classList.add("notransparency")
},
resetState() {
this.menuPanel.visible = false;
app.selectedMediaItems = [];
for (let key in app.modals) {
app.modals[key] = false;
@ -387,10 +434,12 @@ const app = new Vue({
id: self.selectedMediaItems[i].id,
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"
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 res = await self.mk.api.albumRelationship(self.selectedMediaItems[i].id, "tracks");
let ids = res.map(function (i) {
return {id: i.id, type: i.type}
})
pl_items = pl_items.concat(ids)
} else if (self.selectedMediaItems[i].kind == "library-song" || 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,
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"
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 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}
})
pl_items = pl_items.concat(ids)
} else {
pl_items.push({
@ -635,6 +686,11 @@ const app = new Vue({
this.$forceUpdate()
}, 500)
},
getAppClasses() {
if (this.cfg.advanced.experiments.includes('compactui')) {
return {compact: true}
}
},
invokeDrawer(panel) {
if (this.drawer.panel == panel && this.drawer.open) {
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") {
let self = this
@ -754,8 +810,8 @@ const app = new Vue({
}
}
).then(res => {
self.refreshPlaylists()
})
self.refreshPlaylists()
})
},
copyToClipboard(str) {
navigator.clipboard.writeText(str)
@ -886,7 +942,9 @@ const app = new Vue({
"fields[catalog]": "artistUrl,albumUrl",
"fields[songs]": "artistUrl,albumUrl"
}
if (!transient) {this.playlists.loadingState = 0;}
if (!transient) {
this.playlists.loadingState = 0;
}
let playlistId = ''
try {
@ -991,6 +1049,7 @@ const app = new Vue({
return hash;
},
appRoute(route) {
console.log(route)
if (route == "" || route == "#" || route == "/") {
return;
}
@ -1004,12 +1063,13 @@ const app = new Vue({
let hash = route.split("/")
let page = hash[0]
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({
kind: page,
id: id,
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
} else if (kind.toString().includes("artist")) {
app.getArtistInfo(id, isLibrary)
window.location.hash = `${kind}/${id}`
window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
document.querySelector("#app-content").scrollTop = 0
} else if (kind.toString().includes("record-label") || kind.toString().includes("curator")) {
@ -1057,7 +1117,7 @@ const app = new Vue({
let params = {extend: "editorialVideo"}
app.page = (kind) + "_" + (id);
app.getTypeFromID((kind), (id), (isLibrary), params);
window.location.hash = `${kind}/${id}`
window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}`
document.querySelector("#app-content").scrollTop = 0
} else {
app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
@ -1198,81 +1258,81 @@ const app = new Vue({
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
console.log(kind, id, isLibrary)
app.mk.stop().then(() => {
if (kind.includes("artist")) {
app.mk.setStationQueue({artist: 'a-' + id}).then(() => {
app.mk.play()
})
}
// else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))){
// /* Randomize array in-place using Durstenfeld shuffle algorithm */
// function shuffleArray(array) {
// for (var i = array.length - 1; i > 0; i--) {
// var j = Math.floor(Math.random() * (i + 1));
// var temp = array[i];
// array[i] = array[j];
// array[j] = temp;
// }
// }
// app.mk.clearQueue().then(function () { {
// app.mk.setQueue({[truekind]: [item.attributes.playParams.id ?? item.id]}).then(function () {
// app.mk.play().then(function (){
// app.mk.clearQueue().then(function (){
// var playlistId = id
// const params = {
// include: "tracks",
// platform: "web",
// "include[library-playlists]": "catalog,tracks",
// "fields[playlists]": "curatorName,playlistType,name,artwork,url",
// "include[library-songs]": "catalog,artists,albums",
// "fields[catalog]": "artistUrl,albumUrl",
// "fields[songs]": "artistUrl,albumUrl"
// }
// var playlistId = ''
if (kind.includes("artist")) {
app.mk.setStationQueue({artist: 'a-' + id}).then(() => {
app.mk.play()
})
}
// else if (kind.includes("playlist") && (id.startsWith("p.") || id.startsWith("pl."))){
// /* Randomize array in-place using Durstenfeld shuffle algorithm */
// function shuffleArray(array) {
// for (var i = array.length - 1; i > 0; i--) {
// var j = Math.floor(Math.random() * (i + 1));
// var temp = array[i];
// array[i] = array[j];
// array[j] = temp;
// }
// }
// app.mk.clearQueue().then(function () { {
// app.mk.setQueue({[truekind]: [item.attributes.playParams.id ?? item.id]}).then(function () {
// app.mk.play().then(function (){
// app.mk.clearQueue().then(function (){
// var playlistId = id
// const params = {
// include: "tracks",
// platform: "web",
// "include[library-playlists]": "catalog,tracks",
// "fields[playlists]": "curatorName,playlistType,name,artwork,url",
// "include[library-songs]": "catalog,artists,albums",
// "fields[catalog]": "artistUrl,albumUrl",
// "fields[songs]": "artistUrl,albumUrl"
// }
// var playlistId = ''
// try {
// function getPlaylist(id, params, isLibrary){
// if (isLibrary){
// return app.mk.api.library.playlist(id, params)
// } else { return app.mk.api.playlist(id, params)}
// }
// getPlaylist(id, params, isLibrary).then(res => {
// let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
// if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
// app.mk.queue.append(query)
// if (!res.relationships.tracks.next) {
// return
// } else {
// getPlaylistTracks(res.relationships.tracks.next)
// }
// try {
// function getPlaylist(id, params, isLibrary){
// if (isLibrary){
// return app.mk.api.library.playlist(id, params)
// } else { return app.mk.api.playlist(id, params)}
// }
// getPlaylist(id, params, isLibrary).then(res => {
// let query = res.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
// if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
// app.mk.queue.append(query)
// if (!res.relationships.tracks.next) {
// return
// } else {
// getPlaylistTracks(res.relationships.tracks.next)
// }
// function getPlaylistTracks(next) {
// app.apiCall(app.musicBaseUrl + next, res => {
// if (res.id != playlistId) {
// return
// }
// let query = res.data.map(item => new MusicKit.MediaItem(item))
// if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
// app.mk.queue.append(query)
// function getPlaylistTracks(next) {
// app.apiCall(app.musicBaseUrl + next, res => {
// if (res.id != playlistId) {
// return
// }
// let query = res.data.map(item => new MusicKit.MediaItem(item))
// if (app.mk.shuffleMode == 1){shuffleArray(query); console.log('shf')}
// app.mk.queue.append(query)
// if (res.next) {
// getPlaylistTracks(res.next)
// }
// })
// }
// })
// } catch (e) {}
// if (res.next) {
// getPlaylistTracks(res.next)
// }
// })
// }
// })
// } catch (e) {}
// })
// })
// })
// }
// })
// }
else {
app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
}
})
// })
// })
// })
// }
// })
// }
else {
app.playMediaItemById((id), (kind), (isLibrary), item.attributes.url ?? '')
}
})
},
async getTypeFromID(kind, id, isLibrary = false, params = {}, params2 = {}) {
let a;
@ -1306,13 +1366,14 @@ const app = new Vue({
},
searchLibrarySongs() {
let self = this
let prefs = this.cfg.libraryPrefs.songs
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
// 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) => {
let aa = a.attributes[self.library.songs.sorting]
let bb = b.attributes[self.library.songs.sorting]
let aa = a.attributes[prefs.sort]
let bb = b.attributes[prefs.sort]
if (self.library.songs.sorting == "genre") {
aa = a.attributes.genreNames[0]
bb = b.attributes.genreNames[0]
@ -1323,13 +1384,13 @@ const app = new Vue({
if (bb == null) {
bb = ""
}
if (self.library.songs.sortOrder == "asc") {
if (prefs.sortOrder == "asc") {
if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) {
return aa - bb
} else {
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+$/)) {
return bb - aa
} else {
@ -1570,9 +1631,10 @@ const app = new Vue({
} else {
if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", params, {includeResponseMeta: !0}).then((response) => {
processChunk(response)
}) } else {
downloaded.next("", params, {includeResponseMeta: !0}).then((response) => {
processChunk(response)
})
} else {
console.log("Download next", downloaded.next)
}
}
@ -1648,10 +1710,11 @@ const app = new Vue({
processChunk(response)
})
} else {
if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", params, {includeResponseMeta: !0}).then((response) => {
processChunk(response)
}) } else {
if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", params, {includeResponseMeta: !0}).then((response) => {
processChunk(response)
})
} else {
console.log("Download next", downloaded.next)
}
}
@ -1729,12 +1792,13 @@ const app = new Vue({
})
} else {
if (downloaded.next != null && typeof downloaded.next === "function") {
downloaded.next("", "artists", {includeResponseMeta: !0}).then((response) => {
downloaded.next("", "artists", {includeResponseMeta: !0}).then((response) => {
processChunk(response)
}) } else {
console.log("Download next", downloaded.next)
}
})
} else {
console.log("Download next", downloaded.next)
}
}
}
@ -1780,7 +1844,7 @@ const app = new Vue({
let hours = Math.floor(time / 3600)
let mins = Math.floor(time / 60) % 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 ""
} catch (err) {
return ""
@ -2354,76 +2418,84 @@ const app = new Vue({
array[j] = temp;
}
}
let kind = parent.substring(0, parent.indexOf(":"))
let id = parent.substring(parent.indexOf(":") + 1, parent.length)
let truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
console.log(truekind, id)
try {
if (app.library.songs.listing.length > childIndex && parent == "librarysongs") {
if (app.library.songs.displayListing.length > childIndex && parent == "librarysongs") {
console.log(item)
if (item && ((app.library.songs.listing[childIndex].id != item.id))) {
childIndex = app.library.songs.listing.indexOf(item)
if (item && ((app.library.songs.displayListing[childIndex].id != item.id))) {
childIndex = app.library.songs.displayListing.indexOf(item)
}
let query = app.library.songs.listing.map(item => new MusicKit.MediaItem(item));
try {
app.mk.stop()
} catch (e) {
}
this.mk.clearQueue().then(function (_) {
if (app.mk.shuffleMode == 1){ shuffleArray(query)}
app.mk.queue.append(query)
if (childIndex != -1) {
app.mk.changeToMediaAtIndex(childIndex)
} else if (item) {
app.mk.playNext({[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)
let query = app.library.songs.displayListing.map(item => new MusicKit.MediaItem(item));
app.mk.stop().then(() => {
this.mk.clearQueue().then(function (_) {
if (app.mk.shuffleMode == 1) {
shuffleArray(query)
}
app.mk.queue.append(query)
if (childIndex != -1) {
app.mk.changeToMediaAtIndex(childIndex)
} else if (item) {
app.mk.playNext({[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)
app.mk.play()
})
} else {
app.mk.play()
})
} else {
app.mk.play()
}
}
})
})
} else {
app.mk.stop().then(() => {
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.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1).then(function(){
if ((app.showingPlaylist && app.showingPlaylist.id == id)) {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (query.length > 100) {
let u = query.slice(100); if (app.mk.shuffleMode == 1) { shuffleArray(u) }
app.mk.queue.append(u)}
} else {
app.getPlaylistFromID(id, true).then(function () {
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.changeToMediaAtIndex(app.mk.queue._itemIDs.indexOf(item.id) ?? 1).then(function () {
if ((app.showingPlaylist && app.showingPlaylist.id == id)) {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
if (query.length > 100) {
let u = query.slice(100); if (app.mk.shuffleMode == 1) { shuffleArray(u) }
app.mk.queue.append(u)}
})
}
})
let u = query;
if (app.mk.shuffleMode == 1) {
shuffleArray(u)
}
app.mk.queue.append(u)
} else {
app.getPlaylistFromID(id, true).then(function () {
let query = app.showingPlaylist.relationships.tracks.data.map(item => new MusicKit.MediaItem(item));
let u = query;
if (app.mk.shuffleMode == 1) {
shuffleArray(u)
}
app.mk.queue.append(u)
})
}
})
})
}
else{
this.mk.setQueue({[truekind]: [id]}).then(function (queue) {
if (item && ((queue._itemIDs[childIndex] != item.id))) {
childIndex = queue._itemIDs.indexOf(item.id)
}
if (childIndex != -1) {
app.mk.changeToMediaAtIndex(childIndex)
} else if (item) {
app.mk.playNext({[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)
app.mk.play()
})
} else {
app.mk.play()
this.mk.setQueue({[truekind]: [id]}).then(function (queue) {
if (item && ((queue._itemIDs[childIndex] != item.id))) {
childIndex = queue._itemIDs.indexOf(item.id)
}
if (childIndex != -1) {
app.mk.changeToMediaAtIndex(childIndex)
} else if (item) {
app.mk.playNext({[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)
app.mk.play()
})
} else {
app.mk.play()
}
})
}
})}
})}
})
}
} catch (err) {
console.log(err)
try {
@ -2524,7 +2596,8 @@ const app = new Vue({
if (type.slice(-1) != "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) {
return type.type == this
@ -2535,7 +2608,11 @@ const app = new Vue({
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) {
let self = this
@ -2572,7 +2649,7 @@ const app = new Vue({
}
},
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"
}
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
},
_rgbToRgb(rgb = [0,0,0]) {
_rgbToRgb(rgb = [0, 0, 0]) {
// if rgb
return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`
},
@ -2602,7 +2679,7 @@ const app = new Vue({
this.currentTrackID = this.mk.nowPlayingItem["id"];
document.querySelector('.bg-artwork').src = "";
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 => {
artwork.src = img;
})
@ -2690,29 +2767,32 @@ const app = new Vue({
// },
async getCurrentArtURL(){
try{
async getCurrentArtURL() {
try {
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);
try{
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);}
catch (e) {}
try {
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
} catch (e) {
}
} else {
let data = await this.mk.api.library.song(this.mk.nowPlayingItem.id);
if (data != null && data !== "" && data.attributes != null && data.attributes.artwork != null) {
this.currentArtUrl = (data["attributes"]["artwork"]["url"] ?? '').replace('{w}', 50).replace('{h}', 50);
try{
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);}
catch (e) {}
} else {this.currentArtUrl = '';
try{
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);}
catch (e) {}
try {
document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`);
} catch (e) {
}
} else {
this.currentArtUrl = '';
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)
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 => {
artwork.src = img;
})
@ -2763,16 +2843,16 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) {
if (!type.startsWith("library-")) {
type = "library-" + type
}
id = item.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
return value
}else{
} else {
return 0
}
},
@ -2780,7 +2860,7 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) {
if (!type.startsWith("library-")) {
type = "library-" + type
}
id = item.id
@ -2804,7 +2884,7 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) {
if (!type.startsWith("library-")) {
type = "library-" + type
}
id = item.id
@ -2828,7 +2908,7 @@ const app = new Vue({
let type = item.type.slice(-1) === "s" ? item.type : item.type + "s"
let id = item.attributes.playParams.catalogId ? item.attributes.playParams.catalogId : item.id
if (item.id.startsWith("i.")) {
if(!type.startsWith("library-")) {
if (!type.startsWith("library-")) {
type = "library-" + type
}
id = item.id
@ -2842,16 +2922,20 @@ const app = new Vue({
},
volumeWheel(event) {
if (event.deltaY < 0) {
if(this.mk.volume < 1){
if (this.mk.volume < 1) {
if (this.mk.volume <= 0.9) {
this.mk.volume += 0.1
} else { this.mk.volume = 1 }
} else {
this.mk.volume = 1
}
}
} else if (event.deltaY > 0) {
if(this.mk.volume > 0){
if (this.mk.volume >= 0.1){
this.mk.volume -= 0.1
} else {this.mk.volume = 0}
if (this.mk.volume > 0) {
if (this.mk.volume >= 0.1) {
this.mk.volume -= 0.1
} else {
this.mk.volume = 0
}
}
}
},
@ -2921,10 +3005,53 @@ const app = new Vue({
items: []
},
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: [
{
"icon": "./assets/feather/list.svg",
"name": "Add to Playlist...",
"hidden": true,
"action": function () {
app.promptAddToPlaylist()
}
@ -2935,51 +3062,15 @@ const app = new Vue({
"name": "Add to Library...",
"disabled": false,
"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};
}
},
{
"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",
"name": "Start Radio",
"action": function () {
app.mk.setStationQueue({song: item_id}).then(() => {
app.mk.setStationQueue({song: app.mk.nowPlayingItem.id}).then(() => {
app.mk.play()
app.selectedMediaItems = []
})
@ -3004,17 +3095,23 @@ const app = new Vue({
// }else{
// menus.normal.items.find(x => x.id == "addToLibrary").disabled = true
// }
this.showMenuPanel(menus[useMenu], event)
try{
let rating = await app.getRating(app.mk.nowPlayingItem)
if (rating == 0) {
menus.normal.headerItems.find(x => x.id == 'love').disabled = false
menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
} else if (rating == 1) {
menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
menus.normal.headerItems.find(x => x.id == 'love').hidden = true
} 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) {
let rating = await app.getRating(app.mk.nowPlayingItem)
if(rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false
}else if(rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false
}else if(rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
}
CiderContextMenu.Create(event, menus[useMenu])
},
LastFMDeauthorize() {
ipcRenderer.invoke('setStoreValue', 'lastfm.enabled', false).catch((e) => console.error(e));
@ -3061,10 +3158,14 @@ const app = new Vue({
peak: peak
}
},
fullscreen(flag){
fullscreen(flag) {
if (flag) {
ipcRenderer.send('setFullScreen', true);
app.appMode = 'fullscreen';
if (app.mk.nowPlayingItem.type && app.mk.nowPlayingItem.type.toLowerCase().includes("video")) {
document.querySelector('video#apple-music-video-player').requestFullscreen()
} else {
app.appMode = 'fullscreen';
}
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && app.appMode === 'fullscreen') {
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', {
template: '#sidebar-library-item',
props: {
@ -3189,14 +3325,19 @@ document.addEventListener('musickitloaded', function () {
};
request.open("GET", "https://api.cider.sh/");
request.send();
// check for widevine failure and reconfigure the instance.
window.addEventListener("drmUnsupported", function () {
initMusicKit()
});
});
if ('serviceWorker' in navigator) {
// Use the window load event to keep the page load performant
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js?v=1');
navigator.serviceWorker.register('sw.js?v=1');
});
}
}
const getBase64FromUrl = async (url) => {
const data = await fetch(url);
@ -3269,6 +3410,12 @@ function xmlToJson(xml) {
return obj;
};
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
var checkIfScrollIsStatic = setInterval(() => {
try {
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);
}
a:-webkit-any-link {
color: var(--keyColor);
}
hr {
appearance: none;
border: none;
height: 1px;
background-color: rgb(255 255 255 / 20%);
}
body[loading] {
opacity: 0.5;
pointer-events: none;
@ -57,10 +68,10 @@ body[platform='linux'] {
body.notransparency::before {
content: "";
position: absolute;
top:0;
left:0;
right:0;
bottom:0;
top: 0;
left: 0;
right: 0;
bottom: 0;
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==);
}
@ -134,7 +145,7 @@ body.notransparency::before {
}
.bgGradientMaterial-base {
position:relative;
position: relative;
}
.bgGradientMaterial-base::before {
@ -224,13 +235,14 @@ input[type="text"], input[type="number"] {
z-index: -1;
.bg-artwork.a {
top:0;
left:0;
top: 0;
left: 0;
//mix-blend-mode: luminosity;
}
.bg-artwork.b {
bottom:0;
right:0;
bottom: 0;
right: 0;
animation-direction: reverse;
animation-delay: 10s;
}
@ -332,6 +344,8 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
left: 0;
padding: 32px;
width: 100%;
transition: zoom 1s;
zoom: 1;
}
.content-inner.centered {
@ -423,6 +437,18 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
.app-sidebar-footer {
padding: 11px;
.app-playback-controls {
.control-buttons {
display: flex;
justify-content: center;
align-content: center;
}
.volume {
display: flex;
}
}
}
.app-sidebar-button {
@ -496,6 +522,21 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
background: rgb(0 0 0 / 15%);
flex-direction: column;
padding: 20px 0px;
&.libraryNotification {
flex-direction: row;
padding: 0px;
.message {
flex-grow: 1;
}
.spinner {
width: 46px;
height: 30px;
margin-left: 1em;
}
}
}
.app-sidebar-content {
@ -1558,7 +1599,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
overflow-x: hidden;
display: flex;
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 {
@ -1568,7 +1609,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
font-weight: bold;
font-size: 26px;
}
.lyric-line {
--bgSpeed: 1s;
appearance: none;
@ -1653,7 +1694,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyrics-translation {
font-size: 1.6rem;
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);
}
@ -1672,7 +1713,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
.lyric-body:hover + .lyric-footer, .lyric-footer:hover {
display: flex;
display: flex;
}
.modular-fs .app-drawer .lyric-footer {
@ -1802,6 +1843,38 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
/* 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 {
-webkit-mask-image: url("ameres://icons/webui/close.svg");
-webkit-mask-repeat: no-repeat;
@ -1838,35 +1911,43 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&:active {
filter: brightness(75%);
transform: translateY(2px);
transform: scale(0.98);
transition: transform 0s var(--appleEase), box-shadow 0.2s var(--appleEase);
}
}
.md-select {
padding: 5px 10px;
font-size: 1em;
width: 100%;
padding: 6px;
border-radius: 6px;
border: 1px solid rgba(200, 200, 200, 0.1);
font-family: inherit;
border-radius: 4px;
border: 1px solid rgb(100 100 100 / 35%);
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.3), 0px 1px 1px rgba(0, 0, 0, 0.4);
background: #363636;
color: #eee;
}
font-size: 14px;
background: rgba(100, 100, 100, 0.25);
color: #c8c8c8;
font-weight: 500;
.md-select:focus {
outline: none;
}
option {
font-size: 1em;
font-family: inherit;
padding: 8px 16px;
background: #404040;
}
.md-select > option {
font-size: 1em;
font-family: inherit;
padding: 8px 16px;
optgroup {
background: #2c2c2c;
}
&:focus {
outline: solid 1px var(--selected);
}
}
.sidebar-playlist {
.folder-button-active {
background: rgb(255 255 255 / 12%);
}
.folder-body {
background: #ffffff0a;
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 */
.playlist-page {
--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 {
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 {
height: 100%;
width: 100%;
@ -2774,6 +3004,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&.hmedia-scroller-card {
height: 370px;
.mediaitem-card {
margin: 12px;
}
@ -2835,11 +3066,11 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
.info-rect {
width: 100%;
height: 100%;
display: flex;
flex-flow: column;
justify-content: center;
flex-grow: 1;
}
.title {
@ -2848,7 +3079,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.subtitle {
width: 90%;
font-size: 12px;
font-size: .8em;
opacity: 0.7;
}
@ -2870,21 +3101,19 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
justify-content: center;
}
.content-rating {
text-transform: uppercase;
font-size: 10px;
border-radius: 3px;
background: rgb(200 200 200 / 15%);
width: 60px;
text-align: center;
padding: 5px;
margin-right: 12px;
flex: 0 0 auto;
font-weight: 500;
color: #ccc;
.explicit-icon {
background-image: url("./assets/explicit.svg");
height: 12px;
width: 36px;
filter: contrast(0);
background-repeat: no-repeat;
}
.isLibrary {
flex: 0 0 auto;
width: 40px;
text-align: center;
button {
appearance: none;
border: 0px;
@ -2897,6 +3126,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&:hover {
background: rgb(200 200 200 / 10%);
box-shadow: var(--mediaItemShadow);
.overlay-play {
opacity: 1;
}
@ -2913,6 +3143,20 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
box-shadow: var(--mediaItemShadow);
color: #eee;
}
// list item compact
&.compact {
height: 40px;
font-size: 13px;
.artwork {
display: none;
}
.info-rect {
padding-left: 1em;
}
}
}
/* mediaitem-hrect */
@ -3261,9 +3505,10 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background: rgba(50, 50, 50, 0.7);
cursor: pointer;
transition: opacity 0.1s var(--appleEase);
:hover{
:hover {
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;
border-radius: 0px;
margin: 0;
.mediaitem-artwork {
border-radius: 0px;
@ -3382,15 +3628,15 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&::before {
background: var(--bgartwork);
content: "";
top:0;
left:0;
bottom:0;
right:0;
position:absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
background-size: cover;
background-position: bottom;
z-index: 0;
opacity:1;
opacity: 1;
filter: brightness(0.5) blur(50px) saturate(180%);
}
}
@ -3578,19 +3824,20 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background-color: rgba(200, 200, 200, 0.7);
}
.playback-button--small{
.playback-button--small {
opacity: 0.7;
}
.right-col{
.right-col {
height: 50vh;
}
@media only screen and (max-width: 1023px) {
.display--large {
display: flex !important;
display: flex !important;
}
}
.display--large {
display: flex;
@ -3612,7 +3859,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
height: 4px;
background-color: rgb(200 200 200 / 10%);
border-radius: 2px;
&::-webkit-slider-thumb {
opacity: 1;
transform: scale(0.5);
@ -3625,14 +3872,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
}
}
}
}
.background{
.background {
position: absolute;
background-size: cover;
width: 100%;
@ -3642,30 +3889,29 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
position: absolute;
width: 100%;
height: 100%;
.bg-artwork-container{
.bg-artwork-container {
z-index: unset;
}
.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{
animation : unset;
.no-animation {
animation: unset;
}
}
}
.lyrics-col {
.lyrics-col{
height: 62vh;
display: flex;
justify-content: center;
align-content: center;
width: 80%;
height: 62vh;
display: flex;
justify-content: center;
align-content: center;
width: 80%;
::-webkit-scrollbar-thumb {
box-shadow: unset;
@ -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%);
}
.no-lyrics{
.no-lyrics {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
}
.lyric-line{
.lyric-line {
font-size: 35px;
}
}
}
.queue-col{
.queue-col {
width: 60vh;
height: 50vh;
.queue-title{
.queue-title {
opacity: 0.6;
}
@ -3708,9 +3954,9 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
&:hover ::-webkit-scrollbar-thumb {
box-shadow: inset 0px 0px 10px 10px rgb(200 200 200 / 50%);
}
}
.tab-toggles{
}
.tab-toggles {
display: flex;
position: absolute;
bottom: 0;
@ -3718,18 +3964,18 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
width: 15vh;
height: 5vh;
justify-content: space-evenly;
.queue {
background-image: url("./assets/list.svg") ;
background-image: url("./assets/list.svg");
padding: 0.5vh;
width: 2.5vh;
height: 2.5vh;
background-origin: content-box;
background-repeat: no-repeat;
}
.lyrics {
background-image: url("./assets/quote-right.svg") ;
background-image: url("./assets/quote-right.svg");
padding: 0.5vh;
width: 2.5vh;
height: 2.5vh;
@ -3754,12 +4000,12 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
height: 50vh;
}
.controls-parents{
.controls-parents {
width: 50vh;
}
.app-playback-controls {
.song-artist , .song-name {
.app-playback-controls {
.song-artist, .song-name {
font-weight: 600;
text-align: center;
font-size: 0.9em;
@ -3772,16 +4018,18 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.song-name-normal {
height: inherit;
}
&.song-artist-marquee {
> marquee {
//margin-bottom: -3px;
}
}
}
.song-artist {
font-size: 0.875em;
}
.song-name {
width: unset !important;
margin-top: 0.15vh;
@ -3804,7 +4052,8 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
input[type="range"] {
width: 100%;
}
>div {
> div {
width: 100%;
text-align: center;
}
@ -3828,7 +4077,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
overflow: hidden;
margin: 0 0 0.5em;
}
&:hover {
> input[type=range] {
&::-webkit-slider-thumb {
@ -3838,14 +4087,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
}
}
}
input[type=range] {
input[type=range] {
appearance: none;
width: 100%;
height: 4px;
background-color: rgb(200 200 200 / 10%);
border-radius: 2px;
&::-webkit-slider-thumb {
opacity: 0;
transform: scale(0.5);
@ -3858,7 +4107,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
cursor: default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase);
}
&::-moz-range-thumb {
width: 8px;
height: 8px;
@ -3875,19 +4124,35 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
width: 100%;
justify-content: center;
}
}
}
}
/* Cider */
// 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 {
width: 18px;
height: 18px;
margin-right: 8px;
>svg {
> .svg-icon {
width: 100%;
height: 100%;
--color: #aaa;
}
> svg {
width: 100%;
height: 100%;
color: #aaa;
@ -3917,6 +4182,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.bgArtworkMaterial {
display: block;
&::before {
top: -50%;
left: -20%;
@ -3939,15 +4205,14 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
transform-origin: center;
animation: fsLyricIn var(--appleEase) .2s;
opacity: 0.9;
text-shadow:
-1px -1px 0 #000,
0 -1px 0 #000,
1px -1px 0 #000,
1px 0 0 #000,
1px 1px 0 #000,
0 1px 0 #000,
-1px 1px 0 #000,
-1px 0 0 #000;
text-shadow: -1px -1px 0 #000,
0 -1px 0 #000,
1px -1px 0 #000,
1px 0 0 #000,
1px 1px 0 #000,
0 1px 0 #000,
-1px 1px 0 #000,
-1px 0 0 #000;
&:not(.active) {
display: none;
@ -3966,12 +4231,12 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
@keyframes fsLyricIn {
0% {
opacity: 0;
transform: scale(0.98)
opacity: 0;
transform: scale(0.98)
}
100% {
opacity: 1;
transform: scale(1);
opacity: 1;
transform: scale(1);
}
}
@ -4162,7 +4427,7 @@ div#captions {
background: rgba(0, 0, 0, 0.6);
color: yellow;
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] {
@ -4276,6 +4541,7 @@ body.no-gpu {
.bg-artwork-container {
display: none;
}
#navigation-bar {
backdrop-filter: unset;
mix-blend-mode: unset;
@ -4314,4 +4580,6 @@ body.no-gpu {
.drawertransition-leave-to {
right: -300px;
}
}
}
@import url("less/compact.less");

View file

@ -15,7 +15,7 @@
</button>
</div>
<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>
<input type="search"
ref="searchInput"

View file

@ -7,14 +7,19 @@
:data-index="index"
:data-guid="guid"
:data-islibrary="this.item.attributes.playParams.isLibrary ?? false"
:key="item.attributes.playParams.id ?? item.id"
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">
<div class="isLibrary" v-if="showLibraryStatus == true">
<div class="isLibrary" :style="{opacity: (showInLibrary ? 1 : 0)}" v-if="showLibraryStatus == true">
<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 v-else @click="removeFromLibrary()">❤️</button>
<button v-else style="opacity:0;">❤️</button>
</div>
<div class="artwork" v-if="showArtwork == true">
<mediaitem-artwork
@ -24,7 +29,7 @@
<button class="overlay-play" @click="playTrack()"><%- include("../svg/play.svg") %></button>
</div>
<div class="info-rect" :style="{'padding-left': (showArtwork ? '' : '16px')}"
@dblclick="app.routeView(item)">
@dblclick="route()">
<div class="title text-overflow-elipsis">
{{ item.attributes.name }}
</div>
@ -44,10 +49,8 @@
</template>
</div>
</div>
<div class="content-rating" v-if="item.attributes.contentRating" @dblclick="app.routeView(item)">
{{ item.attributes.contentRating }}
</div>
<template v-if="showMetaData == true" @dblclick="app.routeView(item)">
<div class="explicit-icon" v-if="item.attributes && item.attributes.contentRating == 'explicit'"></div>
<template v-if="showMetaData == true" @dblclick="route()">
<div class="metainfo">
{{ item.attributes.releaseDate ? new Date(item.attributes.releaseDate).toLocaleDateString()
: "" }}
@ -56,7 +59,7 @@
{{ item.attributes.genreNames[0] ?? "" }}
</div>
</template>
<div class="duration" v-if="displayDuration" @dblclick="app.routeView(item)">
<div class="duration" v-if="displayDuration" @dblclick="route()">
{{ msToMinSec(item.attributes.durationInMillis ?? 0) }}
</div>
</template>
@ -68,11 +71,13 @@
template: '#mediaitem-list-item',
data: function () {
return {
showInLibrary: false,
isVisible: false,
addedToLibrary: false,
guid: this.uuidv4(),
app: this.$root,
displayDuration: true
displayDuration: true,
addClasses: {}
}
},
props: {
@ -84,14 +89,36 @@
'show-meta-data': {type: Boolean, default: false},
'show-duration': {type: Boolean, default: true},
'contextExt': {type: Object, required: false},
'class-list': {type: String, required: false, default: ""},
},
mounted() {
let duration = this.item.attributes.durationInMillis ?? 0
if (duration == 0 || !this.showDuration) {
this.displayDuration = false
}
this.getClasses()
},
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) {
evt.dataTransfer.setData('text/plain', JSON.stringify({
type: this.item.attributes.playParams.kind ?? this.item.type,
@ -197,12 +224,14 @@
items: [
{
"name": "Add to Playlist...",
"icon": "./assets/feather/plus.svg",
"action": function () {
app.promptAddToPlaylist()
}
},
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
"icon": "./assets/arrow-bend-up.svg",
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -224,6 +253,7 @@
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
"icon": "./assets/arrow-bend-down.svg",
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -245,7 +275,68 @@
]
},
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: [
{
"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",
"name": "Add to Playlist...",
@ -255,6 +346,7 @@
},
{
"name": "Play Next",
"icon": "./assets/arrow-bend-up.svg",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
@ -263,6 +355,7 @@
},
{
"name": "Play Later",
"icon": "./assets/arrow-bend-down.svg",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
@ -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",
"name": "Go to Artist",
@ -352,21 +409,38 @@
if (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{
let rating = await app.getRating(self.item)
if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false
menus.normal.headerItems.find(x => x.id == 'love').disabled = false
menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
} 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) {
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) {
}
CiderContextMenu.Create(event, menus[useMenu])
},
visibilityChanged: function (isVisible, entry) {
this.isVisible = isVisible
@ -474,6 +548,14 @@
else {
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: {
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 #
var rgb = parseInt(c, 16); // convert rrggbb to decimal
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
console.log(color)
console.log(luma)
if (luma > 140) {
return "#aaaaaa"
}else{
@ -142,9 +140,10 @@
}
},
async getBadges() {
let self = this
if (this.badges[this.item.attributes.playParams.id ?? this.item.id]) {
let friends = this.badges[this.item.attributes.playParams.id ?? this.item.id]
const self = this
const id = (this.item.attributes.playParams ? this.item.attributes.playParams.id : null) || this.item.id
if (id && this.badges[id]) {
let friends = this.badges[id]
if (friends) {
friends.forEach(function (friend) {
self.app.mk.api.socialProfile(friend).then(data => {
@ -247,7 +246,6 @@
} else {
console.log(event)
}
await this.isInLibrary();
let self = this
let useMenu = "normal"
if (app.selectedMediaItems.length <= 1) {
@ -261,6 +259,7 @@
items: [
{
name: `Play ${app.selectedMediaItems.length} tracks next`,
"icon": "./assets/arrow-bend-up.svg",
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -282,6 +281,7 @@
},
{
name: `Play ${app.selectedMediaItems.length} tracks later`,
"icon": "./assets/arrow-bend-down.svg",
action: () => {
let itemsToPlay = {}
app.selectedMediaItems.forEach(item => {
@ -303,6 +303,48 @@
]
},
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: [
{
"icon": "./assets/feather/list.svg",
@ -312,8 +354,35 @@
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",
"icon": "./assets/arrow-bend-up.svg",
"action": function () {
app.mk.playNext({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
@ -322,66 +391,13 @@
},
{
"name": "Play Later",
"icon": "./assets/arrow-bend-down.svg",
"action": function () {
app.mk.playLater({[self.item.attributes.playParams.kind ?? self.item.type]: self.item.attributes.playParams.id ?? self.item.id})
app.mk.queue._reindex()
app.selectedMediaItems = []
}
},
{
"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",
"name": "Share",
@ -398,14 +414,35 @@
return item.id != "addToPlaylist"
})
}
let rating = await app.getRating(self.item)
if (rating == 0) {
menus.normal.items.find(x => x.id == 'love').disabled = false
menus.normal.items.find(x => x.id == 'dislike').disabled = false
} else if (rating == 1) {
menus.normal.items.find(x => x.id == 'unlove').disabled = false
} else if (rating == -1) {
menus.normal.items.find(x => x.id == 'undo_dislike').disabled = false
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)
if (rating == 0) {
menus.normal.headerItems.find(x => x.id == 'love').disabled = false
menus.normal.headerItems.find(x => x.id == 'dislike').disabled = false
} else if (rating == 1) {
menus.normal.headerItems.find(x => x.id == 'unlove').hidden = false
menus.normal.headerItems.find(x => x.id == 'love').hidden = true
} 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) {
}
if (this.contextExt) {
@ -416,7 +453,6 @@
menus.multiple.items = menus.multiple.items.concat(this.contextExt.multiple)
}
}
CiderContextMenu.Create(event, menus[useMenu])
},
},
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) {
app.mk.changeToMediaAtIndex(index)

View file

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

View file

@ -30,7 +30,7 @@
</head>
<body oncontextmenu="return false;" loading="1" platform="<%= env.platform %>">
<div id="app">
<div id="app" :class="getAppClasses()">
<transition name="fsModeSwitch">
<div id="app-main" v-show="appMode == 'player'">
<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-title"></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"
@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">
<div class="app-chrome-item display--large">
<button class="playback-button previous" @click="prevButton()"></button>
</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 play" @click="mk.play()" v-else></button>
</div>
<div class="app-chrome-item">
<div class="app-chrome-item display--large">
<button class="playback-button next" @click="mk.skipToNextItem()"></button>
</div>
<div class="app-chrome-item">
<div class="app-chrome-item display--large">
<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"
@ -92,9 +92,10 @@
{{ mk.nowPlayingItem["attributes"]["artistName"] }}
</div>
<div class="song-artist item-navigate" style="display: inline-block;"
@click="getNowPlayingItemDetailed('album')">
@click="getNowPlayingItemDetailed('album')">
<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>
@ -127,7 +128,8 @@
<div class="app-chrome--right">
<div class="app-chrome-item volume display--large">
<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'">
</div>
<div class="app-chrome-item generic" v-if="false">
@ -181,21 +183,30 @@
<div class="app-sidebar-header-text">
Apple Music
</div>
<sidebar-library-item name="Home" svg-icon="./assets/feather/home.svg" page="home"></sidebar-library-item>
<sidebar-library-item name="Listen Now" svg-icon="./assets/feather/play-circle.svg" 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>
<sidebar-library-item name="Home" svg-icon="./assets/feather/home.svg"
page="home"></sidebar-library-item>
<sidebar-library-item name="Listen Now" svg-icon="./assets/feather/play-circle.svg"
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">
Library
</div>
<sidebar-library-item name="Recently Added" svg-icon="./assets/feather/plus-circle.svg" page="library-recentlyadded"></sidebar-library-item>
<sidebar-library-item name="Songs" svg-icon="./assets/feather/music.svg" 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>
<sidebar-library-item name="Recently Added" svg-icon="./assets/feather/plus-circle.svg"
page="library-recentlyadded"></sidebar-library-item>
<sidebar-library-item name="Songs" svg-icon="./assets/feather/music.svg"
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">
Playlists
</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>
<transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened">
@ -220,10 +231,14 @@
</div>
</div>
</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
</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
</button>
<button class="usermenu-item" @click="window.open('https://discord.gg/applemusic')">
@ -239,9 +254,45 @@
</div>
</transition>
<div class="app-sidebar-footer">
<input type="range" class="web-slider display--small" step="0.01" min="0" max="1"
v-model="mk.volume" @wheel="volumeWheel"
v-if="typeof mk.volume != 'undefined'">
<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'">
</div>
</div>
</div>
<button class="app-sidebar-button" style="width:100%"
:class="{active: chrome.menuOpened}"
@blur="setTimeout(()=>{chrome.menuOpened = false}, 100)"
@ -269,13 +320,9 @@
</div>
</button>
</div>
<div class="app-sidebar-notification" v-if="library.downloadNotification.show">
<div>{{ library.downloadNotification.message }}</div>
<div>{{ library.downloadNotification.progress }} / {{ library.downloadNotification.total }}
</div>
<div style="width: 100%">
<progress style="width: 80%;" :value="library.downloadNotification.progress"
:max="library.downloadNotification.total"></progress>
<div class="app-sidebar-notification libraryNotification" v-if="library.downloadNotification.show">
<div class="message">{{ library.downloadNotification.message }} ({{
library.downloadNotification.progress }} / {{ library.downloadNotification.total }})
</div>
</div>
</div>
@ -285,6 +332,18 @@
<button class="nav-item"
@click="navigateForward()"><%- include('svg/chevron-right.svg') %></button>
</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 -->
<transition name="wpfade">
<template v-if="page == 'artist-page' && artistPage.data.attributes">
@ -427,7 +486,9 @@
</transition>
<!-- Library - Songs -->
<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>
<!-- Library - Albums -->
<transition name="wpfade" v-on:enter="getLibraryAlbumsFull(null, 1); searchLibraryAlbums(1);">
@ -456,31 +517,42 @@
</div>
<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="bg-artwork-container">
<img class="bg-artwork a" :src="$store.state.artwork.playerLCD">
<img class="bg-artwork b" :src="$store.state.artwork.playerLCD">
</div>
</div>
<lyrics-view v-if="drawer.panel == 'lyrics'" :time="lyriccurrenttime" :lyrics="lyrics"
:richlyrics="richlyrics"></lyrics-view>
<div v-if="drawer.panel == 'lyrics'" class="lyric-footer">
<button class="md-btn" @click="modularUITest(!fullscreenLyrics)">{{fullscreenLyrics ? "Default View":'Fullscreen View'}}</button>
</div>
<lyrics-view v-if="drawer.panel == 'lyrics'" :time="lyriccurrenttime" :lyrics="lyrics"
:richlyrics="richlyrics"></lyrics-view>
<div v-if="drawer.panel == 'lyrics'" class="lyric-footer">
<button class="md-btn" @click="modularUITest(!fullscreenLyrics)">{{fullscreenLyrics ?
"Default View":'Fullscreen View'}}
</button>
</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>
</div>
</transition>
<cider-menu-panel v-if="menuPanel.visible">
</cider-menu-panel>
</div>
</div>
</transition>
<transition name="fsModeSwitch">
<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>
</transition>
<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 class="bg-artwork b"
@ -528,6 +600,11 @@
</div>
</div>
<!-- Apple Settings Page -->
<%- include('pages/apple-account-settings') %>
<!-- Library - Songs -->
<%- include('pages/library-songs') %>
<!-- Media Item Artwork-->
<%- include("components/mediaitem-artwork"); %>
<!-- Browse -->
@ -563,6 +640,9 @@
<!-- Search -->
<%- include('pages/search') %>
<!-- About -->
<%- include('pages/about') %>
<script type="text/x-template" id="am-musiccovershelf">
<h1>{{ component.attributes.title.stringForDisplay }}</h1>
</script>
@ -576,6 +656,8 @@
</button>
</script>
<!-- Menu Panel -->
<%- include('components/menu-panel') %>
<!-- Playlist Listing -->
<%- include('components/sidebar-playlist') %>
<!-- 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"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
All Rights
Reserved.</p>
<p style="text-align: center">"Apple Music" - Copyright © 2021 <a href="https://www.apple.com/" class="dt-footer__link"
target="_blank"
rel="noopener" data-dt-link-to-exclude="">Apple Inc.</a>
All Rights
Reserved.</p>
<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
Core - Developer - https://github.com/coredev-uk
Quacksire - Developer - https://github.com/child-duckling
booploops - Developer - https://github.com/booploops
vapormusic - Developer - https://github.com/vapormusic
Void - Social Communications Team - https://twitter.com/MoonyVoid
NoseySG - Social Communications Team - https://twitter.com/noah_grose
</div>
<div class="col">
<div class="row">
<div class="col">
<h3>Cider Team</h3>
<button class="md-btn teamBtn" @click="window.open(member.link)" v-for="member in team">
<img :src="member.avatar"/>
<div class="row" style="width:100%;">
<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')"
src="https://contrib.rocks/image?repo=ciderapp/Cider"/>
</div>
</div>
<img class="md-contributors"
onclick="window.open('https://github.com/ciderapp/Cider/graphs/contributors')"
src="https://contrib.rocks/image?repo=ciderapp/Cider"/>
</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>
</div>
</div>
<div class="well" style="margin-top:0px;">
<div class="well" style="margin-top:0;">
<template v-if="artistFeed.length > 0">
<mediaitem-list-item v-for="item in artistFeed" :item="item"></mediaitem-list-item>
</template>

View file

@ -52,10 +52,10 @@
</div>
<div class="col" v-if="data.views['top-songs']">
<div class="row">
<div class="col" style="padding:0px;">
<div class="col" style="padding:0;">
<h3>Top Songs</h3>
</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>
</div>
</div>
@ -138,12 +138,14 @@
let followAction = "follow"
let followActions = {
follow: {
icon: "./assets/feather/plus-circle.svg",
name: "Follow Artist",
action: ()=>{
self.app.cfg.home.followedArtists.push(self.data.id)
}
},
unfollow: {
icon: "./assets/feather/x-circle.svg",
name: "Unfollow Artist",
action: ()=>{
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)) {
followAction = "unfollow"
}
CiderContextMenu.Create(event, {
app.showMenuPanel({
items: [
{
icon: "./assets/feather/play.svg",
name: "Play Artist Radio",
action: ()=>{
app.mk.setStationQueue({artist:self.data.id}).then(()=>{
@ -168,13 +171,14 @@
},
followActions[followAction],
{
icon: "./assets/feather/share.svg",
name: "Share",
action: ()=>{
self.app.copyToClipboard(self.data.attributes.url)
}
}
]
})
}, event)
},
getArtistPalette(artist) {
if (artist["attributes"]["artwork"]) {

View file

@ -16,7 +16,7 @@
<div style="width: 260px;height:260px;">
<mediaitem-artwork
: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 : '')) : '' "
size="260"
></mediaitem-artwork>
@ -40,10 +40,11 @@
@click="data.attributes && data.attributes.artistName ? app.searchAndNavigate(data,'artist') : ''">
{{getArtistName(data)}}
</div>
<div class="playlist-desc" v-if="getDescription(data) != ''">
<div class="content"
v-html="getDescription(data)"></div>
<button class="more-btn" @click="editorialNotesExpanded = !editorialNotesExpanded">
<div class="playlist-desc" v-if="data.attributes.description && (data.attributes.description.standard || data.attributes.description.short)">
<div v-if="data.attributes.description.short" class="content" v-html="data.attributes.description.short"></div>
<div v-else-if="data.attributes.description.standard" class="content" v-html="data.attributes.description.standard"></div>
<button v-if="data.attributes.description.short" class="more-btn"
@click="editorialNotesExpanded = !editorialNotesExpanded">
More
</button>
</div>
@ -58,18 +59,30 @@
</div>
</template>
<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()">
Play
</button>
<button class="wr-btn" style="min-width: 120px;"
<button class="md-btn" style="min-width: 120px;"
@click="app.mk.shuffleMode = 1;play()">
Shuffle
</button>
<button class="wr-btn" style="min-width: 120px;" v-if="inLibrary!=null"
@click="(!inLibrary) ? addToLibrary(data.attributes.playParams.id.toString()) : removeFromLibrary(data.attributes.playParams.id.toString())">
<button class="md-btn" style="min-width: 120px;" v-if="inLibrary!=null && confirm!=true"
@click="confirmButton()">
{{ (!inLibrary) ? "Add to Library" : "Remove from Library" }}
</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>
@ -88,7 +101,8 @@
<div class="friends-info" v-if="itemBadges.length != 0">
<div class="well">
<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
:url="badge.attributes.artwork.url"
:size="60"></mediaitem-artwork>
@ -97,7 +111,7 @@
</div>
</div>
<div class="playlist-time">
{{getFormattedDate(data.attributes.releaseDate)}}
{{getFormattedDate()}}
</div>
<div class="playlist-time total">{{app.getTotalTime()}}</div>
<div class="playlist-time item-navigate" @click="app.searchAndNavigate(data,'recordLabel') "
@ -109,16 +123,19 @@
</div>
</script>
<script>
Vue.component('cider-playlist', {
template: "#cider-playlist",
props: ["data"],
data: function () {
return {
editorialNotesExpanded: false,
drag: false,
nameEditing: false,
inLibrary: null,
confirm: false,
app: this.$root,
itemBadges: [],
badgesRequested: false
@ -161,24 +178,14 @@
}
})
},
getDescription(data) {
console.log(data.attributes)
if (data.attributes.editorialNotes) {
if (data.attributes.editorialNotes.hasOwnProperty('short')) {
return data.attributes.editorialNotes.short
} 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 ''
confirmButton() {
// Return button to normal state after 3 seconds
this.confirm = true
setTimeout(() => this.confirm = false, 3000);
},
getArtistName(data) {
console.log(data.attributes)
if (data.attributes.artistName) {
return data.attributes.artistName
} else if (data.attributes.artist) {
@ -192,8 +199,12 @@
async isInLibrary() {
if (this.data.type && !this.data.type.includes("library")) {
// please keep using vars here
var params = { "fields[playlists]": "inLibrary", "fields[albums]": "inLibrary", "relate": "library" }
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 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
console.log(res)
} else {
@ -203,7 +214,7 @@
editPlaylist() {
this.app.editPlaylist(this.data.id, this.data.attributes.name);
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
}
})
@ -212,21 +223,23 @@
addToLibrary(id) {
app.mk.addToLibrary(id)
this.inLibrary = true
this.confirm = false
},
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 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) {
id = res.relationships.library.data[0].id
}
let kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
var truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({ [truekind]: id })
const truekind = (!kind.endsWith("s")) ? (kind + "s") : kind;
app.mk.api.library.remove({[truekind]: id})
this.inLibrary = false
this.confirm = false
},
editPlaylistName() {
if (this.data.attributes.canEdit && this.data.type == "library-playlists") {
if (this.data.attributes.canEdit && this.data.type === "library-playlists") {
this.nameEditing = true
setTimeout(() => {
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) {
kind = data.attributes.playParams.kind;
id = data.attributes.playParams.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 "";
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 {
var releaseDate = new Date(date);
month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(releaseDate);
const releaseDate = new Date(date);
console.log(date, releaseDate)
month = new Intl.DateTimeFormat('en-US', {month: 'long'}).format(releaseDate);
date = releaseDate.getDate();
year = releaseDate.getFullYear();
return date + " " + month + " " + year;
return prefix + date + " " + month + " " + year;
} catch (e) {
return ""
}
@ -322,25 +384,27 @@
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")
var kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
const kind = this.data.attributes.playParams.kind ?? this.data.type ?? '';
//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));
app.mk.stop().then(function () {
app.mk.setQueue({[truekind]: [id]}).then(function () {
app.mk.setQueue({[truekind]: [id]}).then(function () {
app.mk.play().then(function () {
if (query.length > 100) {
let u = query.slice(100); if (app.mk.shuffleMode == 1) { shuffleArray(u) }
app.mk.queue.append(u)}
})
let u = query.slice(100);
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>
</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">
<mediaitem-list-item v-for="item in artistFeed.limit(6)" :item="item"></mediaitem-list-item>
</template>
@ -55,7 +55,14 @@
</div>
<div class="row" v-if="friendsListeningTo && friendsListeningTo.length > 0">
<div class="col">
<h3>Friends Listening To</h3>
<div class="row">
<div class="col nopadding">
<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">
<template v-if="isSectionReady('friendsListeningTo')">
<mediaitem-square kind="small" v-for="item in friendsListeningTo"

View file

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

View file

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

View file

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

View file

@ -1,46 +1,80 @@
<template v-if="page == 'library-songs'">
<div class="content-inner">
<div class="row">
<div class="col" style="padding:0px;">
<h1 class="header-text">Songs</h1>
</div>
<div class="col-auto">
<button v-if="library.songs.downloadState == 2" @click="getLibrarySongsFull(true)" class="reload-btn"><%- include('../svg/redo.svg') %></button>
</div>
</div>
<div class="row">
<div class="col" style="padding:0px;">
<div class="search-input-container" style="width:100%;margin: 16px 0px;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="searchLibrarySongs"
v-model="library.songs.search" class="search-input">
<script type="text/x-template" id="cider-library-songs">
<div class="content-inner library-page">
<div class="library-header">
<div class="row">
<div class="col" style="padding:0;">
<div class="search-input-container" style="width:100%;margin: 16px 0;">
<div class="search-input--icon"></div>
<input type="search"
style="width:100%;"
spellcheck="false"
placeholder="Search..."
@input="$root.searchLibrarySongs"
v-model="library.songs.search" class="search-input">
</div>
</div>
</div>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="library.songs.sorting" @change="searchLibrarySongs()">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.songs.sortOrder" @change="searchLibrarySongs()">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="prefs.sort" @change="$root.searchLibrarySongs()">
<optgroup label="Sort By">
<option v-for="(sort, index) in library.songs.sortingOptions" :value="index">{{ sort }}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="prefs.sortOrder" @change="$root.searchLibrarySongs()">
<optgroup label="Sort Order">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="prefs.size" @change="$root.searchLibrarySongs()">
<optgroup label="Size">
<option value="normal">Normal</option>
<option value="compact">Compact</option>
</optgroup>
</select>
</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>
<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 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>
</div>
</div>
</template>
</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="row">
<div class="col" style="padding:0px">
<div class="col" style="padding:0">
<h1 class="header-text">Made For You</h1>
</div>
</div>

View file

@ -90,18 +90,6 @@
<input type="checkbox" switch v-model="app.cfg.visual.bg_artwork_rotation"/>
</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-segment">
Hardware Acceleration<br>
@ -447,7 +435,7 @@
<div class="md-option-segment">
LastFM Scrobbling
</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"
onclick="app.LastFMAuthenticate()">Connect</label>
</div>
@ -477,77 +465,92 @@
</div>
</div>
<div class="md-option-header">
<span>Unfinished / Non Functional</span>
<span>Experimental</span>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Theme
Compact UI
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Cider</option>
</select>
<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 class="md-option-line">
<div class="md-option-segment">
Theme Options
<div style="opacity: 0.5; pointer-events: none">
<div class="md-option-header">
<span>Unfinished / Non Functional</span>
</div>
<div class="md-option-segment md-option-segment_auto">
<button class="btn">Theme Options</button>
<div class="md-option-line">
<div class="md-option-segment">
Theme
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Cider</option>
</select>
</div>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Scrollbars
<div class="md-option-line">
<div class="md-option-segment">
Theme Options
</div>
<div class="md-option-segment md-option-segment_auto">
<button class="btn">Theme Options</button>
</div>
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Show on hover</option>
<option value="1">Always show</option>
<option value="2">Hidden</option>
</select>
<div class="md-option-line">
<div class="md-option-segment">
Scrollbars
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value="0">Show on hover</option>
<option value="1">Always show</option>
<option value="2">Hidden</option>
</select>
</div>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Refresh Rate
<div class="md-option-line">
<div class="md-option-segment">
Refresh Rate
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Automatic</option>
<option value='30'>30</option>
<option value='60'>60</option>
<option value='144'>144</option>
<option value='175'>175</option>
<option value='240'>240</option>
<option value='360'>30</option>
</select>
</div>
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Automatic</option>
<option value='30'>30</option>
<option value='60'>60</option>
<option value='144'>144</option>
<option value='175'>175</option>
<option value='240'>240</option>
<option value='360'>30</option>
</select>
<div class="md-option-line">
<div class="md-option-segment">
Close Button Behavior
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Minimize to system tray</option>
<option value='1'>Minimize to taskbar ? dock</option>
<option value='2'>Quit Cider</option>
</select>
</div>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Close Button Behavior
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Minimize to system tray</option>
<option value='1'>Minimize to taskbar ? dock</option>
<option value='2'>Quit Cider</option>
</select>
</div>
</div>
<div class="md-option-line">
<div class="md-option-segment">
Open Cider on Startup
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Never</option>
<option value='1'>Always</option>
<option value='2'>Always, minimized</option>
<option value='2'>Always, hidden in tray</option>
</select>
<div class="md-option-line">
<div class="md-option-segment">
Open Cider on Startup
</div>
<div class="md-option-segment md-option-segment_auto">
<select class="md-select">
<option value='0'>Never</option>
<option value='1'>Always</option>
<option value='2'>Always, minimized</option>
<option value='2'>Always, hidden in tray</option>
</select>
</div>
</div>
</div>
</div>
@ -573,6 +576,12 @@
}
},
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(){
if (app.cfg.advanced.AudioContext){
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) -->
<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">
<style type="text/css">
.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
<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
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"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 716 B

After

Width:  |  Height:  |  Size: 627 B

Before After
Before After