diff --git a/package.json b/package.json index 944f6bf3..138fc8d0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "@sentry/electron": "^2.5.4", "discord-rpc": "^4.0.1", "ejs": "^3.1.6", - "electron-acrylic-window": "^0.5.11", "electron-fetch": "^1.7.4", "electron-log": "^4.4.4", "electron-store": "^8.0.1", @@ -37,6 +36,7 @@ "lastfmapi": "^0.1.1", "mpris-service": "^2.1.2", "music-metadata": "^7.11.4", + "qrcode-terminal": "^0.12.0", "react": "^17.0.2", "react-dom": "^17.0.2", "source-map-support": "^0.5.21", diff --git a/src/renderer/apple-hls.js b/src/renderer/apple-hls.js index e4936dda..1f920850 100644 --- a/src/renderer/apple-hls.js +++ b/src/renderer/apple-hls.js @@ -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, diff --git a/src/renderer/assets/arrow-bend-down.svg b/src/renderer/assets/arrow-bend-down.svg new file mode 100644 index 00000000..cbfa2e8a --- /dev/null +++ b/src/renderer/assets/arrow-bend-down.svg @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/src/renderer/assets/arrow-bend-up.svg b/src/renderer/assets/arrow-bend-up.svg new file mode 100644 index 00000000..64fd7119 --- /dev/null +++ b/src/renderer/assets/arrow-bend-up.svg @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/assets/banner.png b/src/renderer/assets/banner.png new file mode 100644 index 00000000..1edba696 Binary files /dev/null and b/src/renderer/assets/banner.png differ diff --git a/src/renderer/assets/ko_fi.svg b/src/renderer/assets/ko_fi.svg new file mode 100644 index 00000000..8630f586 --- /dev/null +++ b/src/renderer/assets/ko_fi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/assets/open_collective.svg b/src/renderer/assets/open_collective.svg new file mode 100644 index 00000000..ea86a543 --- /dev/null +++ b/src/renderer/assets/open_collective.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/index.js b/src/renderer/index.js index f1320cae..d6071209 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -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 += `` } 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: "
{{ displayNumber }}
", + 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) { diff --git a/src/renderer/less/compact.less b/src/renderer/less/compact.less new file mode 100644 index 00000000..23f5e47f --- /dev/null +++ b/src/renderer/less/compact.less @@ -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; + } +} \ No newline at end of file diff --git a/src/renderer/style.less b/src/renderer/style.less index 1a278f0c..9df636f5 100644 --- a/src/renderer/style.less +++ b/src/renderer/style.less @@ -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; } -} \ No newline at end of file +} + +@import url("less/compact.less"); diff --git a/src/renderer/views/components/add-to-playlist.ejs b/src/renderer/views/components/add-to-playlist.ejs index ae5d0a30..a061cb51 100644 --- a/src/renderer/views/components/add-to-playlist.ejs +++ b/src/renderer/views/components/add-to-playlist.ejs @@ -15,7 +15,7 @@