Merge remote-tracking branch 'origin/main'

This commit is contained in:
Core 2022-06-03 12:39:14 +01:00
commit de1ea918c8
No known key found for this signature in database
GPG key ID: FE9BF1B547F8F3C6
66 changed files with 4630 additions and 3210 deletions

View file

@ -1,6 +1,6 @@
<p align="center"> <p align="center">
<img src="./resources/banner.png" width="80%" height="60%" alt="Banner"><br> <img src="./resources/banner.png" width="80%" height="60%" alt="Banner"><br>
<b>A new cross-platform Apple Music experience based on Electron and Vue.js written from scratch with performance in mind. 🚀</b> <b>A new cross-platform Apple Music experience based on Electron and Vue.js written from scratch with performance & visuals in mind. 🚀</b>
<br><br> <br><br>
<img src="https://img.shields.io/github/stars/ciderapp/Cider?label=Stars" alt="GitHub Stars"/> <img src="https://img.shields.io/github/stars/ciderapp/Cider?label=Stars" alt="GitHub Stars"/>
<img src="https://img.shields.io/github/forks/ciderapp/Cider?label=Forks" alt="GitHub Forks"/> <img src="https://img.shields.io/github/forks/ciderapp/Cider?label=Forks" alt="GitHub Forks"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Before After
Before After

View file

@ -39,7 +39,7 @@
"appx": { "appx": {
"applicationId": "CiderAlpha", "applicationId": "CiderAlpha",
"publisher": "CN=CiderCollective, OID.2.25.311729368913984317654407730594956997722=1", "publisher": "CN=CiderCollective, OID.2.25.311729368913984317654407730594956997722=1",
"displayName": "Cider (Alpha)", "displayName": "Cider",
"identityName": "CiderCollective.CiderAlpha", "identityName": "CiderCollective.CiderAlpha",
"backgroundColor": "transparent", "backgroundColor": "transparent",
"setBuildNumber": true "setBuildNumber": true

View file

@ -2,7 +2,7 @@
"name": "cider", "name": "cider",
"applicationId": "Cider", "applicationId": "Cider",
"productName": "Cider", "productName": "Cider",
"version": "1.4.7", "version": "1.5.0",
"description": "A new cross-platform Apple Music experience based on Electron and Vue.js written from scratch with performance in mind.", "description": "A new cross-platform Apple Music experience based on Electron and Vue.js written from scratch with performance in mind.",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "./build/index.js", "main": "./build/index.js",
@ -40,7 +40,7 @@
"@sentry/electron": "^3.0.7", "@sentry/electron": "^3.0.7",
"@sentry/integrations": "^6.19.6", "@sentry/integrations": "^6.19.6",
"adm-zip": "0.4.10", "adm-zip": "0.4.10",
"airtunes2": "git+https://github.com/vapormusic/node_airtunes2.git#hap", "airtunes2": "git+https://github.com/ciderapp/node_airtunes2",
"castv2-client": "^1.2.0", "castv2-client": "^1.2.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"discord-auto-rpc": "^1.0.16", "discord-auto-rpc": "^1.0.16",
@ -54,6 +54,7 @@
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"express": "^4.17.3", "express": "^4.17.3",
"get-port": "^5.1.1", "get-port": "^5.1.1",
"jimp": "^0.16.1",
"jsonc": "^2.0.0", "jsonc": "^2.0.0",
"lastfmapi": "^0.1.1", "lastfmapi": "^0.1.1",
"mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git", "mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git",
@ -110,9 +111,9 @@
} }
], ],
"build": { "build": {
"electronVersion": "18.3.0", "electronVersion": "19.0.1",
"electronDownload": { "electronDownload": {
"version": "18.3.0+wvcus", "version": "19.0.1+wvcus",
"mirror": "https://github.com/castlabs/electron-releases/releases/download/v" "mirror": "https://github.com/castlabs/electron-releases/releases/download/v"
}, },
"appId": "cider", "appId": "cider",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 7 KiB

Before After
Before After

View file

@ -427,3 +427,12 @@ Update 24/05/2022 21:15 UTC
* `settings.option.general.updateCider.branch.main`: Deleted for all language files * `settings.option.general.updateCider.branch.main`: Deleted for all language files
* `settings.option.general.updateCider.branch.develop`: Deleted for all language files * `settings.option.general.updateCider.branch.develop`: Deleted for all language files
* `settings.notyf.updateCider.update-error`: Deleted for all language files * `settings.notyf.updateCider.update-error`: Deleted for all language files
Update 30/5/2022 05:35 UTC
* `settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM`: Added to `en_US`
* `settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE`: Added to `en_US`
* `settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk`: Added to `en_US`
* `settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k`: Added to `en_US`
* `settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.adaptive`: Added to `en_US`
* `settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.legacy`: Added to `en_US`

View file

@ -7,7 +7,7 @@
"date.format": "${d}.${m}.${y}", "date.format": "${d}.${m}.${y}",
"dialog.cancel": "Abbrechen", "dialog.cancel": "Abbrechen",
"dialog.ok": "OK", "dialog.ok": "OK",
"notification.updatingLibrarySongs": "Aktualisiere Songs...", "notification.updatingLibrarySongs": "Aktualisiere Titel...",
"notification.updatingLibraryAlbums": "Aktualisiere Alben...", "notification.updatingLibraryAlbums": "Aktualisiere Alben...",
"notification.updatingLibraryArtists": "Aktualisiere Künstler...", "notification.updatingLibraryArtists": "Aktualisiere Künstler...",
"term.appleInc": "Apple Inc.", "term.appleInc": "Apple Inc.",
@ -24,13 +24,13 @@
"term.privateSession": "Private Sitzung", "term.privateSession": "Private Sitzung",
"term.queue": "Warteschlange", "term.queue": "Warteschlange",
"term.history": "Verlauf", "term.history": "Verlauf",
"term.search": "Suche", "term.search": "Suchen",
"term.library": "Mediathek", "term.library": "Mediathek",
"term.listenNow": "Jetzt hören", "term.listenNow": "Jetzt hören",
"term.browse": "Entdecken", "term.browse": "Entdecken",
"term.radio": "Radio", "term.radio": "Radio",
"term.recentlyAdded": "Zuletzt hinzugefügt", "term.recentlyAdded": "Zuletzt hinzugefügt",
"term.songs": "Songs", "term.songs": "Titel",
"term.albums": "Alben", "term.albums": "Alben",
"term.artists": "Künstler", "term.artists": "Künstler",
"term.podcasts": "Podcasts", "term.podcasts": "Podcasts",
@ -282,6 +282,44 @@
"settings.option.experimental.compactUI": "Kompaktes UI", "settings.option.experimental.compactUI": "Kompaktes UI",
"settings.option.window.close_button_hide": "Schließtaste soll die App verbergen", "settings.option.window.close_button_hide": "Schließtaste soll die App verbergen",
"settings.option.experimental.inline_playlists": "Inline Playlists und Alben", "settings.option.experimental.inline_playlists": "Inline Playlists und Alben",
"settings.header.advanced": "Erweitert",
"settings.option.debug.openAppData": "Öffne Cider-Ordner",
"settings.option.visual.theme.github.explore": "Durchsuche GitHub Themes",
"settings.option.visual.plugin.github.explore": "Durchsuche GitHub Plugins",
"settings.option.experimental.reinstallwidevine": "WidevineCDM neu installieren",
"settings.option.experimental.reinstallwidevine.confirm": "Möchtest du wirklich Widevine neu installieren?",
"settings.option.visual.theme.checkForUpdates": "Nach Updates suchen",
"settings.option.visual.theme.manageStyles": "Styles verwalten",
"settings.option.window.useNativeTitleBar": "Native Fenster-Titelleiste verwenden",
"settings.option.window.windowControlStyle": "Fenstersteuerungs-Stil",
"settings.option.window.windowControlStyle.right": "Rechts",
"settings.option.experimental.unknownPlugin": "Unbekannte Quellen",
"settings.option.experimental.unknownPlugin.description": "Erlaube Installation von Plugins von anderen Quellen als der Cider-Plugin-Quelle",
"settings.option.window.windowControlStyle.left": "Links",
"settings.option.visual.windowStyle": "Fenster-Style",
"settings.option.general.resumebehavior": "Fortsetzungs-Verhalten",
"settings.option.general.resumebehavior.description": "Fortsetzungs-Verhalten beeinflusst, wie Cider die Sitzung fortsetzt, wenn man zur App zurückkehrt.",
"settings.option.general.resumebehavior.locally": "Lokal",
"settings.option.general.resumebehavior.locally.description": "Cider wird die letzte Sitzung auf diesem Rechner fortsetzen.",
"settings.option.general.resumebehavior.history": "Verlauf",
"settings.option.general.resumebehavior.history.description": "Cider wird den letzten Song aus dem geräteübergreifenden Apple-Music-Verlauf in die Warteschlange stellen.",
"settings.option.general.resumetabs" : "Tab beim Start öffnen",
"settings.option.general.resumetabs.description" : "Wähle welcher Tab beim Starten von Cider geöffnet werden soll.",
"settings.option.general.resumetabs.dynamic" : "Dynamisch",
"settings.option.general.resumetabs.dynamic.description" : "Cider wird den zuletzt genutzten Tab öffnen.",
"settings.option.general.keybindings": "Tastenkombinationen",
"settings.option.general.keybindings.pressCombination": "Drücke eine Kombination aus zwei Tasten um die Tastenkombination zu aktualisieren.",
"settings.option.general.keybindings.pressEscape": "Drücke Escape um zurückzukehren.",
"settings.notyf.general.keybindings.update.success": "Tastenkombination erfolgreich aktualisiert",
"settings.prompt.general.keybindings.update.success": "Tastenkombination erfolgreich aktualisiert. Drücke OK um Cider neuzustarten.",
"settings.option.general.keybindings.open": "Öffnen",
"settings.option.general.themeUpdateNotification": "Automatisch nach Theme-Updates suchen",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Verwaltet vom Audio Lab",
"settings.option.advanced.playlistTrackMapping.description": "Ermöglicht tiefes Scannen von Wiedergabelisten, um festzustellen, welche Titel sich in welchen Wiedergabelisten befinden. Die Erstellungszeiten für den Wiedergabelisten-Cache können sich erheblich verlängern.",
"settings.option.visual.transparent": "Transparentes Fenster",
"settings.option.visual.transparent.description": "Benötigt Theme Support, Neustart erforderlich",
"action.cast.todevices": "An Geräte streamen",
"action.cast.stop": "Streamen an alle Geräte beenden",
"spatial.notTurnedOn": "Die Audio-Spatialisierung ist deaktiviert. Um sie zu verwenden, musst du diese zuerst aktivieren.", "spatial.notTurnedOn": "Die Audio-Spatialisierung ist deaktiviert. Um sie zu verwenden, musst du diese zuerst aktivieren.",
"spatial.spatialProperties": "Räumliche Einstellungen", "spatial.spatialProperties": "Räumliche Einstellungen",
"spatial.width": "Breite", "spatial.width": "Breite",
@ -299,10 +337,39 @@
"spatial.right": "Rechts", "spatial.right": "Rechts",
"spatial.back": "Hinten", "spatial.back": "Hinten",
"spatial.down": "Unten", "spatial.down": "Unten",
"spatial.listener": "Hörer*in", "spatial.listener": "Hörer",
"spatial.audioSource": "Audioquelle", "spatial.audioSource": "Audioquelle",
"settings.header.unfinished": "Unvollendet", "settings.header.unfinished": "Unvollendet",
"settings.option.window.openOnStartup": "Öffne Cider beim Start",
"settings.option.window.openOnStartup.hidden": "Öffne Cider minimiert",
"term.audioControls": "Lautstärkeregelung",
"term.variables": "Variablen",
"settings.option.audio.volumeStep": "Lautstärke-Schritt",
"settings.option.audio.maxVolume": "Max. Lautstärke",
"settings.option.audio.changePlaybackRate": "Wiedergabegeschwindigkeit ändern",
"settings.option.audio.playbackRate": "Wiedergabegeschwindigkeit",
"settings.option.audio.playbackRate.change": "Ändern",
"settings.option.audio.dbspl.display": "dB SPL Anzeige",
"settings.option.audio.dbspl.description": "(Nur für erfahrene Benutzer) Zeigt dB SPL anstelle von dBFS am Lautstärkeregler.",
"settings.option.audio.dbfs.calibration": "0 dBFS Kalibration",
"settings.option.audio.dbfs.description": "Geb den Spitzenwert des Z-gewichteten dB SPL an, wenn Cider auf 0 dBFS steht.",
"settings.option.connectivity.discordRPC.hideButtons": "Buttons im Discord Rich Presence ausblenden",
"settings.option.connectivity.discordRPC.hideTimestamp": "Zeitstempel im Discord Rich Presence ausblenden",
"settings.option.general.showLovedTracksInline": "Zeige geliebte Titel inline",
"settings.option.audio.advanced": "Erweiterte Lautstärkeregelung",
"remote.web.title": "Cider Remote", "remote.web.title": "Cider Remote",
"remote.web.description": "Scanne den QR-Code um dein Handy mit dieser Cider-Instanz zu verbinden", "remote.web.description": "Scanne den QR-Code um dein Handy mit dieser Cider-Instanz zu verbinden",
"about.thanks": "Vielen lieben Dank an das Cider Collective Team und an alle Mithelfer." "about.thanks": "Vielen lieben Dank an das Cider Collective Team und an alle Mithelfer.",
"term.navigateBack": "Zurück",
"term.navigateForward": "Weiter",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Verbessert die wahrgenommene Audioqualität von AAC-kodierten Audiodaten durch die Verwendung eines Echtzeit-Algorithmus, der sowohl psychoakustische Modelle des menschlichen Gehörs als auch AAC-Kodierungsmerkmale nutzt.",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "Verwirklicht eine andere musikalische Atmosphäre, die nach dem Stand der Technik von Audio-Setups modelliert ist.",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "Ändert die Betriebsart des Atmosphere Realizer-Moduls.",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "Vorabgestimmter Spatialisierungseffekt. Deaktiviert die anpassbaren Einstellungen der Audio-Spatialisierung.",
"settings.option.audio.audioLab.description": "Eine Auswahl an selbst entwickelten Audioeffekten für Cider.",
"action.open": "Öffnen",
"settings.option.visual.theme.github.openfolder": "Öffne Themes Ordner",
"settings.option.debug.copy_log": "Logs in die Zwischenablage kopieren",
"settings.header.visual.theme.github.page": "Themes von GitHub",
"settings.header.visual.plugin.github.page": "Plugins von GitHub"
} }

View file

@ -131,6 +131,8 @@
"term.audioControls": "Vowume Contwows", "term.audioControls": "Vowume Contwows",
"term.clearAll": "Cweaw Aww", "term.clearAll": "Cweaw Aww",
"term.recentStations": "Wecent Stations", "term.recentStations": "Wecent Stations",
"term.personalStations": "Pewsonyaw Stations",
"term.amLive": "Appwe Music Wive",
"term.language": "Wanguage", "term.language": "Wanguage",
"term.funLanguages": "Fun", "term.funLanguages": "Fun",
"term.noLyrics": "Woading... / Wywics nyot found./ Instwumentaw.", "term.noLyrics": "Woading... / Wywics nyot found./ Instwumentaw.",
@ -556,4 +558,4 @@
"share.platform.songLink": "Copy with song.wink", "share.platform.songLink": "Copy with song.wink",
"share.platform.clipboard": "Copy Wink", "share.platform.clipboard": "Copy Wink",
"about.thanks": "Majow thanks to the Cidew Cowwective Team and aww of ouw contwibutows." "about.thanks": "Majow thanks to the Cidew Cowwective Team and aww of ouw contwibutows."
} }

View file

@ -2,7 +2,7 @@
"i18n.languageName": "English (US)", "i18n.languageName": "English (US)",
"i18n.languageNameEnglish": "English (US)", "i18n.languageNameEnglish": "English (US)",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@maikirakiwi @kyw504100 @nosh118", "i18n.authors": "@notmaikiwi @kyw504100 @nosh118 @cryptofyre",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${m} ${d}, ${y}", "date.format": "${m} ${d}, ${y}",
"dialog.cancel": "Cancel", "dialog.cancel": "Cancel",
@ -212,6 +212,12 @@
"podcast.episodes": "Episodes", "podcast.episodes": "Episodes",
"podcast.playEpisode": "Play Episode", "podcast.playEpisode": "Play Episode",
"podcast.website": "Podcast Website", "podcast.website": "Podcast Website",
"action.hideLibrary": "Hide Library",
"action.showLibrary": "Show Library",
"action.cut": "Cut",
"action.paste": "Paste",
"action.selectAll": "Select All",
"action.delete": "Delete",
"action.edit": "Edit", "action.edit": "Edit",
"action.done": "Done", "action.done": "Done",
"action.editTracklist": "Edit Tracklist", "action.editTracklist": "Edit Tracklist",
@ -367,6 +373,7 @@
"settings.header.audio": "Audio", "settings.header.audio": "Audio",
"settings.header.audio.description": "Adjust the audio settings for Cider.", "settings.header.audio.description": "Adjust the audio settings for Cider.",
"settings.option.audio.volumeStep": "Volume Step", "settings.option.audio.volumeStep": "Volume Step",
"settings.option.audio.advanced": "Advanced Volume Control",
"settings.option.audio.maxVolume": "Max Volume", "settings.option.audio.maxVolume": "Max Volume",
"settings.option.audio.changePlaybackRate": "Change Playback Rate", "settings.option.audio.changePlaybackRate": "Change Playback Rate",
"settings.option.audio.playbackRate": "Playback Rate", "settings.option.audio.playbackRate": "Playback Rate",
@ -406,18 +413,22 @@
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "Jasmine Macchiato", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "Jasmine Macchiato",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "Hokkaido Milk Tea", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "Hokkaido Milk Tea",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500": "Moonlight Softcake", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500": "Moonlight Softcake",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "Brown Sugar Creme Brûlée Milk",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE": "Cuddle Warmth",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Enhances the perceived audio quality of AAC encoded audio by using a real-time algorithm that takes advantage of both psychoacoustic models of human hearing and AAC encoding characteristics.", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Enhances the perceived audio quality of AAC encoded audio by using a real-time algorithm that takes advantage of both psychoacoustic models of human hearing and AAC encoding characteristics.",
"settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAP is not compatible with Spatialization. Please disable Spatialization to continue.", "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAP is not compatible with Spatialization. Please disable Spatialization to continue.",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAP Strength", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAP Strength",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Changes the strength of the processing done to the audio. (Aggressive may yield undesirable results)", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Changes the strength of the processing done to the audio. (Aggressive may yield undesirable results)",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Standard", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Standard",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.adaptive": "Adaptive",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.legacy": "Legacy",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Aggressive", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Aggressive",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Audio Normalization", "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Audio Normalization",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Normalizes peak volume for individual tracks to create a more uniform listening experience. (Does not work on user uploaded tracks)", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Normalizes peak volume for individual tracks to create a more uniform listening experience. (Does not work on user uploaded tracks)",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Managed by Audio Lab", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Managed by Audio Lab",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider Tuned Spatialization", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider Tuned Spatialization",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "Pre-tuned Spatializing Effect, disables the customizable settings of Audio Spatialization. Spatialization must be enabled as a prerequisite.", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "Pre-tuned Spatializing Effect, disables the customizable settings of Audio Spatialization.",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Spatialization Profile", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Spatialization Profile",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "Changes the Tuning Profile of the Spatialization.", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "Changes the Tuning Profile of the Spatialization.",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "Standard", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "Standard",
@ -426,6 +437,8 @@
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiophile", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiophile",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "Diffused", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "Diffused",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "Encore",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "Expanded Encore",
"settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Spatialization is not compatible with CAP. Please disable CAP to continue.", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Spatialization is not compatible with CAP. Please disable CAP to continue.",
"settings.option.audio.dbspl.display": "dB SPL Display", "settings.option.audio.dbspl.display": "dB SPL Display",
"settings.option.audio.dbspl.description": "(Advanced users only) Display dB SPL instead of dBFS on the volume slider.", "settings.option.audio.dbspl.description": "(Advanced users only) Display dB SPL instead of dBFS on the volume slider.",
@ -564,5 +577,44 @@
"share.platform.email": "Email", "share.platform.email": "Email",
"share.platform.songLink": "Copy with song.link", "share.platform.songLink": "Copy with song.link",
"share.platform.clipboard": "Copy Link", "share.platform.clipboard": "Copy Link",
"about.thanks": "Major thanks to the Cider Collective Team and all of our contributors." "about.thanks": "Major thanks to the Cider Collective Team and all of our contributors.",
"oobe.yes": "Yes",
"oobe.no": "No",
"oobe.next": "Next",
"oobe.previous": "Previous",
"oobe.done": "Done",
"oobe.amupsell.title": "Before we start",
"oobe.amupsell.text": "Cider requires an active, paid Apple Music subscription\nCider will not work with Apple Music Voice Plan or some promotional trial subscriptions. If you already have a qualified Apple Music subscription click Next to continue.",
"oobe.amupsell.subscribeBtn": "Subscribe to Apple Music",
"oobe.amupsell.explainBtn": "Explain",
"oobe.amupsell.subscribeUrl": "https://apple.co/3MdqJVQ",
"oobe.amupsell.amWebUrl": "https://beta.music.apple.com/",
"oobe.amupsell.promoExplained": "Some promotional and non US Apple Music trial subscriptions do not have access to the required Apple Music Web Player API's needed for Cider to function. To verify if your active trial will work with Cider go to <a href='{{ amWebUrl }}'>{{ amWebUrl }}</a> log in and try to play some music. If it works, great! You're ready to use Cider, however if it does not consider subscribing to Apple Music here: <a href='{{ subscribeUrl }}'>{{ subscribeUrl }}</a>",
"oobe.intro.title": "Welcome to Cider",
"oobe.intro.subtitle": "",
"oobe.intro.text": "Let's get a few things set up so you can use Cider, how you'd like. You can always change these settings later.",
"oobe.general.title": "General",
"oobe.general.subtitle": "",
"oobe.general.text": "",
"oobe.audio.title": "Audio",
"oobe.audio.subtitle": "",
"oobe.audio.text": "Cider features a custom tuned and designed audio stack that delivers a rich high quality audio experience.\nFeaturing Cider Adrenaline, Atmosphere Realizer, and Spatialized Audio.\nTo enable this functionality \"Advanced Audio Functionality\" must be enabled.\nEnabling Advanced Audio Functionality will give you access to these enhancements in the Cider Audio Labs, found in the app settings.",
"oobe.audio.advancedFunctionality": "",
"oobe.visual.title": "Visual",
"oobe.visual.subtitle": "",
"oobe.visual.text": "",
"oobe.visual.layout.text": "Cider features two window different layouts.\nMaverick is an iTunes like layout with the player at the top of the window.\nMojave is a new spin created by the Cider Collective.\n\nYou can change the layout any time in the settings.",
"oobe.visual.suggestingThemes": "Theming is a great way to personalize your experience. Here are a few we suggest: ",
"oobe.visual.suggestingThemes.subtext": "(These themes will be downloaded from GitHub)",
"oobe.visual.suggestingThemes.default": "Cider",
"oobe.visual.suggestingThemes.default.text": "The classic Cider theme.",
"oobe.visual.suggestingThemes.dark": "Dark",
"oobe.visual.suggestingThemes.dark.text": "Darkness.",
"oobe.visual.suggestingThemes.community1": "Groovy",
"oobe.visual.suggestingThemes.community1.text": "A WinUI influenced theme",
"oobe.visual.suggestingThemes.community2": "iTheme",
"oobe.visual.suggestingThemes.community2.text": "The classic big fruit layout.",
"oobe.visual.suggestingThemes.community3": "Dracula",
"oobe.visual.suggestingThemes.community3.text": "The iconic Dracula color scheme.",
"oobe.amsignin.title": ""
} }

View file

@ -2,7 +2,7 @@
"i18n.languageName": "हिन्दी", "i18n.languageName": "हिन्दी",
"i18n.languageNameEnglish": "Hindi", "i18n.languageNameEnglish": "Hindi",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@maikirakiwi @vringster", "i18n.authors": "@notmaikiwi @vringster",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${m} ${d}, ${y}", "date.format": "${m} ${d}, ${y}",
"dialog.cancel": "रोकें", "dialog.cancel": "रोकें",

View file

@ -2,7 +2,7 @@
"i18n.languageName": "日本語", "i18n.languageName": "日本語",
"i18n.languageNameEnglish": "Japanese", "i18n.languageNameEnglish": "Japanese",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@maikirakiwi @tanaka_kakuel", "i18n.authors": "@notmaikiwi @tanaka_kakuel",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${y}年${m}月${d}日", "date.format": "${y}年${m}月${d}日",
"dialog.cancel": "キャンセル", "dialog.cancel": "キャンセル",
@ -334,22 +334,31 @@
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "春毫ジャスミンマキアート", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "春毫ジャスミンマキアート",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "北海道のロイヤルミルクティー", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "北海道のロイヤルミルクティー",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500": "ムーンライトソフトケーキ", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500": "ムーンライトソフトケーキ",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "黒糖クレームブリュレミルク",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE": "布団はやわらかいなー",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "音楽をより豊かに、生き生きとさせます", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "人の聴覚心理モデルとAAC符号化の特徴を活用したリアルタイムアルゴリズムにより、AACの認知音質を向上させます。",
"settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAPはSpatializationと互換性がありません", "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAPはSpatializationと互換性がありません",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAPの強さ", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAPの強さ",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "音に施す処理の強さを設定します (強くしすぎると望ましい結果が得られないかもしれません)", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "音に施す処理の強さを設定します (強くしすぎると望ましい結果が得られないかもしれません)",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "標準", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "標準",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "高", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "高",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.adaptive": "アダプティブ",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.legacy": "レガシー",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization": "オーディオノーマライズ", "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "オーディオノーマライズ",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "さまざまな曲の音量を均一にし、より整った音を楽しめるようにする機能です。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "さまざまな曲の音量を均一にし、より整った音を楽しめるようにする機能です。",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Audio Labの設定", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Audio Labの設定",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider Tuned Spatialization", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider Tuned Spatialization",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "開発チームがチューニングした設定を使用することができます。(オーディオ空間化を有効にする必要があります)", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "開発チームがチューニングした設定を使用することができます。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Spatialization Profile", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Spatialization Profile",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "プロファイルをSpatializationに変更します", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "プロファイルをSpatializationに変更します",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "スタンダード", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "スタンダード",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiophile", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.soundstage": "音場",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation": "分離感",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "ミニマル",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "拡散",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "アンコール",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "拡張アンコール",
"settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "オーディオ空間化はCAPと互換性がありません", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "オーディオ空間化はCAPと互換性がありません",
"settings.header.visual": "ビジュアル", "settings.header.visual": "ビジュアル",
"settings.header.visual.description": "Ciderのビジュアル設定", "settings.header.visual.description": "Ciderのビジュアル設定",

View file

@ -2,7 +2,7 @@
"i18n.languageName": "English (US)", "i18n.languageName": "English (US)",
"i18n.languageNameEnglish": "English (US)", "i18n.languageNameEnglish": "English (US)",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@maikirakiwi @kyw504100 @nosh118", "i18n.authors": "@notmaikiwi @kyw504100 @nosh118 @cryptofyre",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${m} ${d}, ${y}", "date.format": "${m} ${d}, ${y}",
"dialog.cancel": "Cancel", "dialog.cancel": "Cancel",
@ -212,6 +212,12 @@
"podcast.episodes": "Episodes", "podcast.episodes": "Episodes",
"podcast.playEpisode": "Play Episode", "podcast.playEpisode": "Play Episode",
"podcast.website": "Podcast Website", "podcast.website": "Podcast Website",
"action.hideLibrary": "Hide Library",
"action.showLibrary": "Show Library",
"action.cut": "Cut",
"action.paste": "Paste",
"action.selectAll": "Select All",
"action.delete": "Delete",
"action.edit": "Edit", "action.edit": "Edit",
"action.done": "Done", "action.done": "Done",
"action.editTracklist": "Edit Tracklist", "action.editTracklist": "Edit Tracklist",
@ -263,6 +269,7 @@
"action.tray.playpause": "Play/Pause", "action.tray.playpause": "Play/Pause",
"action.tray.next": "Next", "action.tray.next": "Next",
"action.tray.previous": "Previous", "action.tray.previous": "Previous",
"action.tray.listento": "Listen To:",
"action.update": "Update", "action.update": "Update",
"action.install": "Install", "action.install": "Install",
"action.copy": "Copy", "action.copy": "Copy",
@ -366,6 +373,7 @@
"settings.header.audio": "Audio", "settings.header.audio": "Audio",
"settings.header.audio.description": "Adjust the audio settings for Cider.", "settings.header.audio.description": "Adjust the audio settings for Cider.",
"settings.option.audio.volumeStep": "Volume Step", "settings.option.audio.volumeStep": "Volume Step",
"settings.option.audio.advanced": "Advanced Volume Control",
"settings.option.audio.maxVolume": "Max Volume", "settings.option.audio.maxVolume": "Max Volume",
"settings.option.audio.changePlaybackRate": "Change Playback Rate", "settings.option.audio.changePlaybackRate": "Change Playback Rate",
"settings.option.audio.playbackRate": "Playback Rate", "settings.option.audio.playbackRate": "Playback Rate",
@ -398,20 +406,29 @@
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "Realizes a different musical atmosphere modelled after the state of the art audio setups.", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "Realizes a different musical atmosphere modelled after the state of the art audio setups.",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode": "Cider Atmosphere Realizer™ Mode", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode": "Cider Atmosphere Realizer™ Mode",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "Changes the mode of operation of the Atmosphere Realizer module.", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "Changes the mode of operation of the Atmosphere Realizer module.",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_STANDARD": "Natural (Standard)", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_STANDARD": "Hōjicha Cheese Foam Tea",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_PLUS": "Natural (Plus)", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_PLUS": "Genmaicha Tapioca Milk Tea",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E68_1": "Rock Salt Cheese Foam Tea",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E68_2": "Uji Matcha Milk Tea",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "Jasmine Macchiato",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "Hokkaido Milk Tea",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500": "Moonlight Softcake",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "Brown Sugar Creme Brûlée Milk",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE": "Cuddle Warmth",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Enhances the perceived audio quality of 256 kbps AAC audio by using a real-time algorithm that takes advantage of both psychoacoustic models of human hearing and AAC encoding characteristics.", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Enhances the perceived audio quality of AAC encoded audio by using a real-time algorithm that takes advantage of both psychoacoustic models of human hearing and AAC encoding characteristics.",
"settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAP is not compatible with Spatialization. Please disable Spatialization to continue.", "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "CAP is not compatible with Spatialization. Please disable Spatialization to continue.",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAP Strength", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "CAP Strength",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Changes the strength of the processing done to the audio. (Aggressive may yield undesirable results)", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Changes the strength of the processing done to the audio. (Aggressive may yield undesirable results)",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Standard", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Standard",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.adaptive": "Adaptive",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.legacy": "Legacy",
"settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Aggressive", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Aggressive",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Audio Normalization", "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Audio Normalization",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Normalizes peak volume for individual tracks to create a more uniform listening experience. (Does not work on user uploaded tracks)", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Normalizes peak volume for individual tracks to create a more uniform listening experience. (Does not work on user uploaded tracks)",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Managed by Audio Lab", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Managed by Audio Lab",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider Tuned Spatialization", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider Tuned Spatialization",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "Pre-tuned Spatializing Effect, disables the customizable settings of Audio Spatialization. Spatialization must be enabled as a prerequisite.", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "Pre-tuned Spatializing Effect, disables the customizable settings of Audio Spatialization.",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Spatialization Profile", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Spatialization Profile",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "Changes the Tuning Profile of the Spatialization.", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "Changes the Tuning Profile of the Spatialization.",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "Standard", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "Standard",
@ -419,6 +436,9 @@
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation": "Separation", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation": "Separation",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiophile", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiophile",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "Diffused",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "Encore",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "Expanded Encore",
"settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Spatialization is not compatible with CAP. Please disable CAP to continue.", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Spatialization is not compatible with CAP. Please disable CAP to continue.",
"settings.option.audio.dbspl.display": "dB SPL Display", "settings.option.audio.dbspl.display": "dB SPL Display",
"settings.option.audio.dbspl.description": "(Advanced users only) Display dB SPL instead of dBFS on the volume slider.", "settings.option.audio.dbspl.description": "(Advanced users only) Display dB SPL instead of dBFS on the volume slider.",
@ -557,5 +577,44 @@
"share.platform.email": "Email", "share.platform.email": "Email",
"share.platform.songLink": "Copy with song.link", "share.platform.songLink": "Copy with song.link",
"share.platform.clipboard": "Copy Link", "share.platform.clipboard": "Copy Link",
"about.thanks": "Major thanks to the Cider Collective Team and all of our contributors." "about.thanks": "Major thanks to the Cider Collective Team and all of our contributors.",
"oobe.yes": "Yes",
"oobe.no": "No",
"oobe.next": "Next",
"oobe.previous": "Previous",
"oobe.done": "Done",
"oobe.amupsell.title": "Before we start",
"oobe.amupsell.text": "Cider requires an active, paid Apple Music subscription\nCider will not work with Apple Music Voice Plan or some promotional trial subscriptions. If you already have a qualified Apple Music subscription click Next to continue.",
"oobe.amupsell.subscribeBtn": "Subscribe to Apple Music",
"oobe.amupsell.explainBtn": "Explain",
"oobe.amupsell.subscribeUrl": "https://apple.co/3MdqJVQ",
"oobe.amupsell.amWebUrl": "https://beta.music.apple.com/",
"oobe.amupsell.promoExplained": "Some promotional and non US Apple Music trial subscriptions do not have access to the required Apple Music Web Player API's needed for Cider to function. To verify if your active trial will work with Cider go to <a href='{{ amWebUrl }}'>{{ amWebUrl }}</a> log in and try to play some music. If it works, great! You're ready to use Cider, however if it does not consider subscribing to Apple Music here: <a href='{{ subscribeUrl }}'>{{ subscribeUrl }}</a>",
"oobe.intro.title": "Welcome to Cider",
"oobe.intro.subtitle": "",
"oobe.intro.text": "Let's get a few things set up so you can use Cider, how you'd like. You can always change these settings later.",
"oobe.general.title": "General",
"oobe.general.subtitle": "",
"oobe.general.text": "",
"oobe.audio.title": "Audio",
"oobe.audio.subtitle": "",
"oobe.audio.text": "Cider features a custom tuned and designed audio stack that delivers a rich high quality audio experience.\nFeaturing Cider Adrenaline, Atmosphere Realizer, and Spatialized Audio.\nTo enable this functionality \"Advanced Audio Functionality\" must be enabled.\nEnabling Advanced Audio Functionality will give you access to these enhancements in the Cider Audio Labs, found in the app settings.",
"oobe.audio.advancedFunctionality": "",
"oobe.visual.title": "Visual",
"oobe.visual.subtitle": "",
"oobe.visual.text": "",
"oobe.visual.layout.text": "Cider features two window different layouts.\nMaverick is an iTunes like layout with the player at the top of the window.\nMojave is a new spin created by the Cider Collective.\n\nYou can change the layout any time in the settings.",
"oobe.visual.suggestingThemes": "Theming is a great way to personalize your experience. Here are a few we suggest: ",
"oobe.visual.suggestingThemes.subtext": "(These themes will be downloaded from GitHub)",
"oobe.visual.suggestingThemes.default": "Cider",
"oobe.visual.suggestingThemes.default.text": "The classic Cider theme.",
"oobe.visual.suggestingThemes.dark": "Dark",
"oobe.visual.suggestingThemes.dark.text": "Darkness.",
"oobe.visual.suggestingThemes.community1": "Groovy",
"oobe.visual.suggestingThemes.community1.text": "A WinUI influenced theme",
"oobe.visual.suggestingThemes.community2": "iTheme",
"oobe.visual.suggestingThemes.community2.text": "The classic big fruit layout.",
"oobe.visual.suggestingThemes.community3": "Dracula",
"oobe.visual.suggestingThemes.community3.text": "The iconic Dracula color scheme.",
"oobe.amsignin.title": ""
} }

View file

@ -2,7 +2,7 @@
"i18n.languageName": "简体中文(中国)", "i18n.languageName": "简体中文(中国)",
"i18n.languageNameEnglish": "Simp. Chinese (China)", "i18n.languageNameEnglish": "Simp. Chinese (China)",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@maikirakiwi @BillKerman @jay900604", "i18n.authors": "@notmaikiwi @BillKerman @jay900604",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${y}年${m}月${d}日", "date.format": "${y}年${m}月${d}日",
"dialog.cancel": "取消", "dialog.cancel": "取消",
@ -290,7 +290,7 @@
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自动将歌曲播放音量调整到相同水平,享受更舒适的聆听体验。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自动将歌曲播放音量调整到相同水平,享受更舒适的聆听体验。",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "此功能由音频实验室管理", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "此功能由音频实验室管理",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider 空间音频效果", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider 空间音频效果",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "预先调整音频空间效果,关闭空间音讯可自订设置。但必须先打开音频空间才可以做设置。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "预先调整音频空间效果,关闭空间音讯可自订设置。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 音频空间配置档", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 音频空间配置档",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "变更音频空间的配置档,需重新启动应用程序。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "变更音频空间的配置档,需重新启动应用程序。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "标准", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "标准",

View file

@ -2,7 +2,7 @@
"i18n.languageName": "繁體中文(香港)", "i18n.languageName": "繁體中文(香港)",
"i18n.languageNameEnglish": "Trad. Chinese (Hong Kong)", "i18n.languageNameEnglish": "Trad. Chinese (Hong Kong)",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@kyw504100 @maikirakiwi @BillKerman @jay900604", "i18n.authors": "@kyw504100 @notmaikiwi @BillKerman @jay900604",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${y} 年 ${m} 月 ${d} 日", "date.format": "${y} 年 ${m} 月 ${d} 日",
"dialog.cancel": "取消", "dialog.cancel": "取消",
@ -399,7 +399,7 @@
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自動將歌曲播放音量調整至相同水平,建立更統一的聆聽體驗。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自動將歌曲播放音量調整至相同水平,建立更統一的聆聽體驗。",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "由音訊實驗室管理", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "由音訊實驗室管理",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider 空間音訊效果", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider 空間音訊效果",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "預先調整空間音訊效果,關閉空間音訊可自訂設定。但必須先開啟空間音訊才可以做設定。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "預先調整空間音訊效果,關閉空間音訊可自訂設定。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 空間音訊配置檔案", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 空間音訊配置檔案",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "變更空間音訊的配置檔案,需要重新啟動應用程式。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "變更空間音訊的配置檔案,需要重新啟動應用程式。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal",

View file

@ -2,7 +2,7 @@
"i18n.languageName": "繁體中文(臺灣)", "i18n.languageName": "繁體中文(臺灣)",
"i18n.languageNameEnglish": "Trad. Chinese (Taiwan)", "i18n.languageNameEnglish": "Trad. Chinese (Taiwan)",
"i18n.category": "main", "i18n.category": "main",
"i18n.authors": "@maikirakiwi @jay900604 @kyw504100 @BillKerman", "i18n.authors": "@notmaikiwi @jay900604 @kyw504100 @BillKerman",
"app.name": "Cider", "app.name": "Cider",
"date.format": "${y}年${m}月${d}日", "date.format": "${y}年${m}月${d}日",
"dialog.cancel": "取消", "dialog.cancel": "取消",
@ -42,12 +42,19 @@
"term.createNewPlaylist": "新增播放列表", "term.createNewPlaylist": "新增播放列表",
"term.createNewPlaylistFolder": "新增播放列表檔案夾", "term.createNewPlaylistFolder": "新增播放列表檔案夾",
"term.deletePlaylist": "你確定要刪除此播放列表嗎?", "term.deletePlaylist": "你確定要刪除此播放列表嗎?",
"term.navigateBack": "回上一頁",
"term.navigateForward": "到下一頁",
"term.play": "播放", "term.play": "播放",
"term.pause": "暫停", "term.pause": "暫停",
"term.stop": "停止",
"term.previous": "上一首", "term.previous": "上一首",
"term.next": "下一首", "term.next": "下一首",
"term.shuffle": "隨機播放", "term.shuffle": "隨機播放",
"term.repeat": "重複播放", "term.repeat": "重複播放",
"term.enableShuffle": "開啟隨機播放",
"term.disableShuffle": "取消隨機播放",
"term.repeat": "開啟單曲循環",
"term.enableRepeatOne": "取消單曲循環",
"term.volume": "音量", "term.volume": "音量",
"term.mute": "靜音", "term.mute": "靜音",
"term.unmute": "取消靜音", "term.unmute": "取消靜音",
@ -264,10 +271,10 @@
"settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.description": "改變模擬溫暖模組處理的強度。", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.description": "改變模擬溫暖模組處理的強度。",
"settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.smooth": "溫和", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.smooth": "溫和",
"settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.warm": "溫暖", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.warm": "溫暖",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer": "Cider 音樂氣氛實現器™️", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer": "Cider 臨場音效™️",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "以最先進的音訊設定為設計,實現不同的音樂氣氛。", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "以業界頂尖的算法,實現擁有臨場感的音樂體驗。",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode": "Cider 音樂氣氛™️模式", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode": "Cider 臨場音效™️模式",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "更改氣氛實現器模塊的操作模式。", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "更改臨場音效感的模式。",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_STANDARD": "自然(標準)", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_STANDARD": "自然(標準)",
"settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_PLUS": "自然(增強)", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_PLUS": "自然(增強)",
"settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider 數位增強音訊處理™️", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider 數位增強音訊處理™️",
@ -280,7 +287,7 @@
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自動將歌曲播放音量調整至相同位準,享受更舒適的聆聽體驗。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自動將歌曲播放音量調整至相同位準,享受更舒適的聆聽體驗。",
"settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "此功能由音訊實驗室管理", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "此功能由音訊實驗室管理",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider 空間音訊效果", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Cider 空間音訊效果",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "預先調整空間音訊效果,關閉空間音訊可自訂設定。但必須先開啟空間音訊才可以做設定。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "預先調整空間音訊效果,關閉空間音訊可自訂設定。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 空間音訊配置檔案", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 空間音訊配置檔案",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "變更空間音訊的配置檔案,需要重新啟動應用程式。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "變更空間音訊的配置檔案,需要重新啟動應用程式。",
"settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "標準", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "標準",

View file

@ -55,6 +55,9 @@ export class AppEvents {
app.exit() app.exit()
} }
// Try limiting JS memory to 350MB.
app.commandLine.appendSwitch('js-flags', '--max-old-space-size=350');
// Expose GC // Expose GC
app.commandLine.appendSwitch('js-flags', '--expose_gc') app.commandLine.appendSwitch('js-flags', '--expose_gc')

View file

@ -1,5 +1,5 @@
import {join} from "path"; import {join} from "path";
import {app, BrowserWindow as bw, ipcMain, ShareMenu, shell} from "electron"; import {app, BrowserWindow as bw, ipcMain, ShareMenu, shell, screen} from "electron";
import * as windowStateKeeper from "electron-window-state"; import * as windowStateKeeper from "electron-window-state";
import * as express from "express"; import * as express from "express";
import * as getPort from "get-port"; import * as getPort from "get-port";
@ -28,6 +28,7 @@ import * as os from "os";
import wallpaper from "wallpaper"; import wallpaper from "wallpaper";
import * as AdmZip from "adm-zip"; import * as AdmZip from "adm-zip";
/** /**
* @file Creates the BrowserWindow * @file Creates the BrowserWindow
* @author CiderCollective * @author CiderCollective
@ -55,6 +56,7 @@ export class BrowserWindow {
"pages/library-songs", "pages/library-songs",
"pages/library-albums", "pages/library-albums",
"pages/library-artists", "pages/library-artists",
"pages/library-recentlyadded",
"pages/browse", "pages/browse",
"pages/groupings", "pages/groupings",
"pages/settings", "pages/settings",
@ -81,11 +83,11 @@ export class BrowserWindow {
"pages/zoo", "pages/zoo",
"pages/plugin-renderer", "pages/plugin-renderer",
"pages/keybinds", "pages/keybinds",
"pages/oobe",
"components/mediaitem-artwork", "components/mediaitem-artwork",
"components/artwork-material", "components/artwork-material",
"components/menu-panel", "components/menu-panel",
"components/sidebar-playlist", "components/sidebar-playlist",
"components/spatial-properties",
"components/audio-settings", "components/audio-settings",
"components/plugin-menu", "components/plugin-menu",
"components/audio-controls", "components/audio-controls",
@ -118,6 +120,11 @@ export class BrowserWindow {
"components/inline-collection-list", "components/inline-collection-list",
], ],
appRoutes: [ appRoutes: [
{
page: "library-recentlyadded",
component: `<cider-recentlyadded></cider-recentlyadded>`,
condition: "page == 'library-recentlyadded'"
},
{ {
page: "plugin-renderer", page: "plugin-renderer",
component: `<plugin-renderer></plugin-renderer>`, component: `<plugin-renderer></plugin-renderer>`,
@ -479,7 +486,7 @@ export class BrowserWindow {
const impulseExternals = join(utils.getPath("externals"), "/impulses/") const impulseExternals = join(utils.getPath("externals"), "/impulses/")
const impulseFile = join(impulseExternals, req.params.file) const impulseFile = join(impulseExternals, req.params.file)
if(existsSync(impulseFile)) { if(existsSync(impulseFile)) {
res.sendFile(impulseFile) res.sendFile(impulseFile)
}else{ }else{
res.sendFile(join(utils.getPath('srcPath'), "./renderer/audio/impulses/" + req.params.file)) res.sendFile(join(utils.getPath('srcPath'), "./renderer/audio/impulses/" + req.params.file))
} }
@ -738,15 +745,28 @@ export class BrowserWindow {
return json; return json;
}) })
ipcMain.on("get-wallpaper", async (event) => { ipcMain.on("get-wallpaper", async (event, args) => {
const wpPath: string = await wallpaper.get(); const wpPath: string = await wallpaper.get();
// get the wallpaper and encode it to base64 then return const Jimp = require("jimp")
const wpBase64: string = await readFileSync(wpPath, 'base64') const img = await Jimp.read(wpPath)
// add the data:image properties const blurAmount = args.blurAmount ?? 256
const wpData: string = `data:image/png;base64,${wpBase64}` if(blurAmount) {
img.blur(blurAmount)
}
const screens = await screen.getAllDisplays()
const width = screens.reduce((a, b) => a + b.size.width, 0)
const height = screens.reduce((a, b) => a + b.size.height, 0)
img.cover(width, height, Jimp.HORIZONTAL_ALIGN_LEFT | Jimp.VERTICAL_ALIGN_MIDDLE)
const result = await img.getBase64Async(Jimp.MIME_PNG)
event.returnValue = { event.returnValue = {
path: wpPath, path: wpPath,
data: wpData data: result,
res: {
width: width,
height: height
}
}; };
}) })
@ -1489,4 +1509,3 @@ export class BrowserWindow {
console.log('remote broadcasted') console.log('remote broadcasted')
} }
} }

View file

@ -13,7 +13,7 @@ export class Store {
"general": { "general": {
"close_button_hide": false, "close_button_hide": false,
"discordrpc": { "discordrpc": {
"enabled": false, "enabled": true,
"client": "Cider", "client": "Cider",
"clear_on_pause": true, "clear_on_pause": true,
"hide_buttons": false, "hide_buttons": false,
@ -87,14 +87,17 @@ export class Store {
"W" "W"
], ],
"audioSettings": [ "audioSettings": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : "Shift", process.platform == "darwin" ? "Option" : "Shift",
"A" "A"
], ],
"pluginMenu": [ "pluginMenu": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : "Shift", process.platform == "darwin" ? "Option" : "Shift",
"P" "P"
], ],
"castToDevices": [ "castToDevices": [
"CommandOrControl",
process.platform == "darwin" ? "Option" : "Shift", process.platform == "darwin" ? "Option" : "Shift",
"C" "C"
], ],

View file

@ -0,0 +1,67 @@
[
"pages/podcasts",
"pages/apple-account-settings",
"pages/library-songs",
"pages/library-albums",
"pages/library-artists",
"pages/browse",
"pages/groupings",
"pages/settings",
"pages/installed-themes",
"pages/listen_now",
"pages/radio",
"pages/home",
"pages/artist-feed",
"pages/cider-playlist",
"pages/playlist-inline",
"pages/recordLabel",
"pages/cider-multiroom",
"pages/collection-list",
"pages/apple-curator",
"pages/artist",
"pages/search",
"pages/about",
"pages/library-videos",
"pages/remote-pair",
"pages/themes-github",
"pages/plugins-github",
"pages/replay",
"pages/audiolabs",
"pages/zoo",
"pages/plugin-renderer",
"pages/keybinds",
"pages/oobe",
"components/mediaitem-artwork",
"components/artwork-material",
"components/menu-panel",
"components/sidebar-playlist",
"components/audio-settings",
"components/plugin-menu",
"components/audio-controls",
"components/audio-playbackrate",
"components/qrcode-modal",
"components/moreinfo-modal",
"components/equalizer",
"components/add-to-playlist",
"components/queue",
"components/mediaitem-scroller-horizontal",
"components/mediaitem-scroller-horizontal-large",
"components/mediaitem-scroller-horizontal-sp",
"components/mediaitem-scroller-horizontal-mvview",
"components/mediaitem-list-item",
"components/mediaitem-hrect",
"components/mediaitem-square",
"components/mediaitem-mvview",
"components/listennow-child",
"components/mediaitem-mvview-sp",
"components/animatedartwork-view",
"components/listitem-horizontal",
"components/lyrics-view",
"components/fullscreen",
"components/miniplayer",
"components/castmenu",
"components/airplay-modal",
"components/artist-chip",
"components/hello-world",
"components/inline-collection-list"
]

174
src/main/base/vrouting.json Normal file
View file

@ -0,0 +1,174 @@
[
{
"page": "plugin-renderer",
"component": "<plugin-renderer></plugin-renderer>",
"condition": "page == 'plugin-renderer'"
},
{
"page": "zoo",
"component": "<cider-zoo></cider-zoo>",
"condition": "page == 'zoo'"
},
{
"page": "podcasts",
"component": "<apple-podcasts></apple-podcasts>",
"condition": "page == 'podcasts'"
},
{
"page": "library-videos",
"component": "<cider-library-videos></cider-library-videos>",
"condition": "page == 'library-videos'"
},
{
"page": "apple-account-settings",
"component": "<apple-account-settings></apple-account-settings>",
"condition": "page == 'apple-account-settings'"
},
{
"page": "about",
"component": "<about-page></about-page>",
"condition": "page == 'about'"
},
{
"page": "cider-artist",
"component": "<cider-artist :data=\"artistPage.data\"></cider-artist>",
"condition": "page == 'artist-page' && artistPage.data.attributes"
},
{
"page": "collection-list",
"component": "<cider-collection-list :data=\"collectionList.response\" :type=\"collectionList.type\" :title=\"collectionList.title\"></cider-collection-list>",
"condition": "page == 'collection-list'"
},
{
"page": "home",
"component": "<cider-home></cider-home>",
"condition": "page == 'home'"
},
{
"page": "artist-feed",
"component": "<cider-artist-feed></cider-artist-feed>",
"condition": "page == 'artist-feed'"
},
{
"page": "playlist-inline",
"component": "<playlist-inline :data=\"showingPlaylist\"></playlist-inline>",
"condition": "modals.showPlaylist"
},
{
"page": "playlist_",
"component": "<cider-playlist :data=\"showingPlaylist\"></cider-playlist>",
"condition": "page.includes('playlist_')"
},
{
"page": "oobe",
"component": "<cider-oobe/>",
"condition": "page == 'oobe'"
},
{
"page": "album_",
"component": "<cider-playlist :data=\"showingPlaylist\"></cider-playlist>",
"condition": "page.includes('album_')"
},
{
"page": "recordLabel_",
"component": "<cider-recordlabel :data=\"showingPlaylist\"></cider-recordlabel>",
"condition": "page.includes('recordLabel_')"
},
{
"page": "multiroom",
"component": "<cider-multiroom :data=\"multiroom\"></cider-multiroom>",
"condition": "page.includes('multiroom')"
},
{
"page": "curator_",
"component": "<cider-recordlabel :data=\"showingPlaylist\"></cider-recordlabel>",
"condition": "page.includes('curator_')"
},
{
"page": "browsepage",
"component": "<cider-browse :data=\"browsepage\"></cider-browse>",
"condition": "page == 'browse'",
"onEnter": ""
},
{
"page": "groupings",
"component": "<cider-groupings :data=\"browsepage\"></cider-groupings>",
"condition": "page == 'groupings'",
"onEnter": ""
},
{
"page": "listen_now",
"component": "<cider-listen-now :data=\"listennow\"></cider-listen-now>",
"condition": "page == 'listen_now'",
"onEnter": ""
},
{
"page": "radio",
"component": "<cider-radio :data=\"radio\"></cider-radio>",
"condition": "page == 'radio'",
"onEnter": ""
},
{
"page": "settings",
"component": "<cider-settings></cider-settings>",
"condition": "page == 'settings'"
},
{
"page": "installed-themes",
"component": "<installed-themes></installed-themes>",
"condition": "page == 'installed-themes'"
},
{
"page": "search",
"component": "<cider-search :search=\"search\"></cider-search>",
"condition": "page == 'search'"
},
{
"page": "library-songs",
"component": "<cider-library-songs :data=\"library.songs\"></cider-library-songs>",
"condition": "page == 'library-songs'",
"onEnter": ""
},
{
"page": "library-albums",
"component": "<cider-library-albums :data=\"library.songs\"></cider-library-albums>",
"condition": "page == 'library-albums'",
"onEnter": ""
},
{
"page": "library-artists",
"component": "<cider-library-artists></cider-library-artists>",
"condition": "page == 'library-artists'",
"onEnter": ""
},
{
"page": "appleCurator",
"component": "<cider-applecurator :data=\"appleCurator\"></cider-applecurator>",
"condition": "page.includes('appleCurator')"
},
{
"page": "themes-github",
"component": "<themes-github></themes-github>",
"condition": "page == 'themes-github'"
},
{
"page": "plugins-github",
"component": "<plugins-github></plugins-github>",
"condition": "page == 'plugins-github'"
},
{
"page": "remote-pair",
"component": "<remote-pair></remote-pair>",
"condition": "page == 'remote-pair'"
},
{
"page": "audiolabs",
"component": "<audiolabs-page></audiolabs-page>",
"condition": "page == 'audiolabs'"
},
{
"page": "replay",
"component": "<replay-page></replay-page>",
"condition": "page == 'replay'"
}
]

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View file

@ -158,9 +158,6 @@ const CiderAudio = {
CiderAudio.audioNodes.spatialNode.buffer = await CiderAudio.context.decodeAudioData(bufferedImpulse); CiderAudio.audioNodes.spatialNode.buffer = await CiderAudio.context.decodeAudioData(bufferedImpulse);
}); });
}, },
spatialOff: function () {
CiderAudio.hierarchical_loading();
},
intelliGainComp_h0_0: function () { intelliGainComp_h0_0: function () {
let filters = []; const precisionHz = 12; let filters = []; const precisionHz = 12;
if (CiderAudio.audioNodes.audioBands !== null) { filters = filters.concat(CiderAudio.audioNodes.audioBands) } if (CiderAudio.audioNodes.audioBands !== null) { filters = filters.concat(CiderAudio.audioNodes.audioBands) }

View file

@ -1,294 +1,346 @@
var notyf = new Notyf(); var notyf = new Notyf();
const MusicKitObjects = { const MusicKitObjects = {
LibraryPlaylist: function () { LibraryPlaylist: function () {
this.id = "" this.id = "";
this.type = "library-playlist-folders" this.type = "library-playlist-folders";
this.href = "" this.href = "";
this.attributes = { this.attributes = {
dateAdded: "", dateAdded: "",
name: "" name: "",
} };
this.playlists = [] this.playlists = [];
} },
} };
// limit an array to a certain number of items // limit an array to a certain number of items
Array.prototype.limit = function (n) { Array.prototype.limit = function (n) {
return this.slice(0, n); return this.slice(0, n);
}; };
Vue.component('animated-number', { Vue.component("animated-number", {
template: "<div style='display: inline-block;'>{{ displayNumber }}</div>",
props: { number: { default: 0 } },
template: "<div style='display: inline-block;'>{{ displayNumber }}</div>", data() {
props: { 'number': { default: 0 } }, return {
displayNumber: 0,
interval: false,
};
},
data() { ready() {
return { this.displayNumber = this.number ? this.number : 0;
displayNumber: 0, },
interval: false
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);
}, },
},
});
ready() { Vue.component("sidebar-library-item", {
this.displayNumber = this.number ? this.number : 0; template: "#sidebar-library-item",
props: {
name: {
type: String,
required: true,
}, },
page: {
watch: { type: String,
number() { required: true,
clearInterval(this.interval); },
svgIcon: {
if (this.number == this.displayNumber) { type: String,
return; required: false,
} default: "",
},
this.interval = window.setInterval(() => { cdClick: {
if (this.displayNumber != this.number) { type: Function,
var change = (this.number - this.displayNumber) / 10; required: false,
change = change >= 0 ? Math.ceil(change) : Math.floor(change); },
this.displayNumber = this.displayNumber + change; },
} data: function () {
}, 20); return {
} app: app,
svgIconData: "",
};
},
async mounted() {
if (this.svgIcon) {
this.svgIconData = await this.app.getSvgIcon(this.svgIcon);
} }
}) },
methods: {},
Vue.component('sidebar-library-item', {
template: '#sidebar-library-item',
props: {
name: {
type: String,
required: true
},
page: {
type: String,
required: true
},
svgIcon: {
type: String,
required: false,
default: ''
},
cdClick: {
type: Function,
required: false
}
},
data: function () {
return {
app: app,
svgIconData: ""
}
},
async mounted() {
if (this.svgIcon) {
this.svgIconData = await this.app.getSvgIcon(this.svgIcon)
}
},
methods: {}
}); });
function fallbackinitMusicKit() { function fallbackinitMusicKit() {
const request = new XMLHttpRequest(); const request = new XMLHttpRequest();
function loadAlternateKey() { function loadAlternateKey() {
let parsedJson = JSON.parse(this.responseText) let parsedJson = JSON.parse(this.responseText);
MusicKit.configure({ MusicKit.configure({
developerToken: parsedJson.developerToken, developerToken: parsedJson.developerToken,
app: { app: {
name: 'Apple Music', name: "Apple Music",
build: '1978.4.1', build: "1978.4.1",
version: "1.0" version: "1.0",
}, },
sourceType: 24, sourceType: 24,
suppressErrorDialog: true suppressErrorDialog: true,
})
setTimeout(() => {
app.init()
if (app.cfg.visual.window_background_style == "mica" && !app.isDev) {
app.spawnMica()
}
}, 1000)
}
request.addEventListener("load", loadAlternateKey);
request.open("GET", "https://raw.githubusercontent.com/lujjjh/LitoMusic/main/token.json");
request.send();
}
document.addEventListener('musickitloaded', function () {
console.log('MusicKit loaded')
// MusicKit global is now defined
function initMusicKit() {
let parsedJson = JSON.parse(this.responseText)
MusicKit.configure({
developerToken: parsedJson.token,
app: {
name: 'Apple Music',
build: '1978.4.1',
version: "1.0"
},
sourceType: 24,
suppressErrorDialog: true
}).then(() => {
function waitForApp() {
if (typeof app.init !== "undefined") {
app.init()
if (app.cfg.visual.window_background_style == "mica" && !app.isDev) {
app.spawnMica()
}
}
else {
setTimeout(waitForApp, 250);
}
}
waitForApp()
})
}
const request = new XMLHttpRequest();
request.timeout = 5000;
request.addEventListener("load", initMusicKit);
request.onreadystatechange = function (aEvt) {
if (request.readyState == 4) {
if (request.status != 200)
fallbackinitMusicKit()
}
};
request.open("GET", "https://api.cider.sh/v1/");
request.send();
// check for widevine failure and reconfigure the instance.
window.addEventListener("drmUnsupported", function () {
initMusicKit()
}); });
setTimeout(() => {
app.init();
if (app.cfg.visual.window_background_style == "mica" && !app.isDev) {
app.spawnMica();
}
}, 1000);
}
request.addEventListener("load", loadAlternateKey);
request.open(
"GET",
"https://raw.githubusercontent.com/lujjjh/LitoMusic/main/token.json"
);
request.send();
}
document.addEventListener("musickitloaded", function () {
console.log("MusicKit loaded");
// MusicKit global is now defined
function initMusicKit() {
let parsedJson = JSON.parse(this.responseText);
MusicKit.configure({
developerToken: parsedJson.token,
app: {
name: "Apple Music",
build: "1978.4.1",
version: "1.0",
},
sourceType: 24,
suppressErrorDialog: true,
}).then(() => {
function waitForApp() {
if (typeof app.init !== "undefined") {
app.init();
if (app.cfg.visual.window_background_style == "mica" && !app.isDev) {
app.spawnMica();
}
} else {
setTimeout(waitForApp, 250);
}
}
waitForApp();
});
}
const request = new XMLHttpRequest();
request.timeout = 5000;
request.addEventListener("load", initMusicKit);
request.onreadystatechange = function (aEvt) {
if (request.readyState == 4) {
if (request.status != 200) fallbackinitMusicKit();
}
};
request.open("GET", "https://api.cider.sh/v1/");
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", () => {
if ('serviceWorker' in navigator) { navigator.serviceWorker.register("sw.js?v=1");
// Use the window load event to keep the page load performant });
window.addEventListener('load', () => {
navigator.serviceWorker.register('sw.js?v=1');
});
} }
const getBase64FromUrl = async (url) => { const getBase64FromUrl = async (url) => {
const data = await fetch(url); const data = await fetch(url);
const blob = await data.blob(); const blob = await data.blob();
return new Promise((resolve) => { return new Promise((resolve) => {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(blob); reader.readAsDataURL(blob);
reader.onloadend = () => { reader.onloadend = () => {
const base64data = reader.result; const base64data = reader.result;
resolve(base64data); resolve(base64data);
} };
}); });
} };
function Clone(obj) { function Clone(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
function uuidv4() { function uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) (
); c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
} }
function xmlToJson(xml) { function xmlToJson(xml) {
// Create the return object
let obj = {};
// Create the return object if (xml.nodeType == 1) {
let obj = {}; // element
// do attributes
if (xml.nodeType == 1) { // element if (xml.attributes.length > 0) {
// do attributes obj["@attributes"] = {};
if (xml.attributes.length > 0) { for (var j = 0; j < xml.attributes.length; j++) {
obj["@attributes"] = {}; let attribute = xml.attributes.item(j);
for (var j = 0; j < xml.attributes.length; j++) { obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
let attribute = xml.attributes.item(j); }
obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
}
}
} else if (xml.nodeType == 3) { // text
obj = xml.nodeValue;
} }
} else if (xml.nodeType == 3) {
// text
obj = xml.nodeValue;
}
// do children // do children
if (xml.hasChildNodes()) { if (xml.hasChildNodes()) {
for (var i = 0; i < xml.childNodes.length; i++) { for (var i = 0; i < xml.childNodes.length; i++) {
var item = xml.childNodes.item(i); var item = xml.childNodes.item(i);
var nodeName = item.nodeName; var nodeName = item.nodeName;
if (typeof (obj[nodeName]) == "undefined") { if (typeof obj[nodeName] == "undefined") {
obj[nodeName] = xmlToJson(item); obj[nodeName] = xmlToJson(item);
} else { } else {
if (typeof (obj[nodeName].push) == "undefined") { if (typeof obj[nodeName].push == "undefined") {
var old = obj[nodeName]; var old = obj[nodeName];
obj[nodeName] = []; obj[nodeName] = [];
obj[nodeName].push(old); obj[nodeName].push(old);
}
obj[nodeName].push(xmlToJson(item));
}
} }
obj[nodeName].push(xmlToJson(item));
}
} }
console.log(obj); }
return obj; console.log(obj);
}; return obj;
}
async function asyncForEach(array, callback) { async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) { for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array); await callback(array[index], index, array);
} }
} }
var checkIfScrollIsStatic = setInterval(() => { var checkIfScrollIsStatic = setInterval(() => {
try { try {
if (position === document.getElementsByClassName('lyric-body')[0].scrollTop) { if (
clearInterval(checkIfScrollIsStatic) position === document.getElementsByClassName("lyric-body")[0].scrollTop
// do something ) {
} clearInterval(checkIfScrollIsStatic);
position = document.getElementsByClassName('lyric-body')[0].scrollTop // do something
} catch (e) {
} }
position = document.getElementsByClassName("lyric-body")[0].scrollTop;
} catch (e) {}
}, 50); }, 50);
// WebGPU Console Notification // WebGPU Console Notification
async function webGPU() { async function webGPU() {
try { try {
const currentGPU = await navigator.gpu.requestAdapter() const currentGPU = await navigator.gpu.requestAdapter();
console.log("WebGPU enabled on", currentGPU.name, "with feature ID", currentGPU.features.size) console.log(
} catch (e) { "WebGPU enabled on",
console.log("WebGPU disabled / WebGPU initialization failed") currentGPU.name,
} "with feature ID",
currentGPU.features.size
);
} catch (e) {
console.log("WebGPU disabled / WebGPU initialization failed");
}
} }
function isJson(item) { function isJson(item) {
item = typeof item !== "string" item = typeof item !== "string" ? JSON.stringify(item) : item;
? JSON.stringify(item)
: item;
try {
item = JSON.parse(item);
} catch (e) {
return false;
}
if (typeof item === "object" && item !== null) {
return true;
}
try {
item = JSON.parse(item);
} catch (e) {
return false; return false;
}
if (typeof item === "object" && item !== null) {
return true;
}
return false;
} }
webGPU().then() webGPU().then();
let screenWidth = screen.width; let screenWidth = screen.width;
let screenHeight = screen.height; let screenHeight = screen.height;
window.onerror = function (error) { document.addEventListener("DOMContentLoaded", async function () {
console.log(error) // app.oobeInit()
bootbox.alert("Error occurred: " + error) });
};
document.addEventListener(
"contextmenu",
function (e) {
if (
e.target.tagName.toLowerCase() == "textarea" ||
(e.target.tagName.toLowerCase() == "input" &&
e.target.type != "checkbox" &&
e.target.type != "radio" &&
e.target.disabled == false)
) {
e.preventDefault();
const menuPanel = {
items: {
cut: {
name: app.getLz("action.cut"),
action: function () {
document.execCommand("cut");
},
},
copy: {
name: app.getLz("action.copy"),
action: function () {
document.execCommand("copy");
},
},
paste: {
name: app.getLz("action.paste"),
action: function () {
document.execCommand("paste");
},
},
delete: {
name: app.getLz("action.delete"),
action: function () {
document.execCommand("delete");
},
},
selectAll: {
name: app.getLz("action.selectAll"),
action: function () {
document.execCommand("selectAll");
},
},
},
};
app.showMenuPanel(menuPanel, e);
}
},
false
);

View file

@ -0,0 +1,20 @@
@colorMixRate: 1%;
@transparencyRate: 50%;
@keyColor : #fc3c44;
@ciderColor: #ff2654;
@baseColor: #1e1e1e;
@baseColorMix: mix(@baseColor, transparent, @transparencyRate);
@sidebarColor: #2e2e2e;
@sidebarColorMix: mix(@sidebarColor, transparent, @transparencyRate);
@appOpacity: 0.15;
:root {
--baseColor: @baseColor;
--baseColorMix: @baseColorMix;
--sidebarColor: @sidebarColor;
--sidebarColorMix: @sidebarColorMix;
--ciderColor: @ciderColor;
--appOpacity: @appOpacity;
--transparencyRate: @transparencyRate;
}

View file

@ -845,6 +845,7 @@
padding : 0.5rem 1rem; padding : 0.5rem 1rem;
background-color: rgba(0, 0, 0, 0.03); background-color: rgba(0, 0, 0, 0.03);
border-top : 1px solid rgba(0, 0, 0, 0.125); border-top : 1px solid rgba(0, 0, 0, 0.125);
text-align : center;
} }
.card-footer:last-child { .card-footer:last-child {

View file

@ -6,7 +6,7 @@
@font-face { @font-face {
font-family: "codicon"; font-family: "codicon";
font-display: block; font-display: block;
src: url("codicon.ttf") format("truetype"); src: url("./codicon.ttf?f06865699f1720ee6ca6e0a4aa084d76") format("truetype");
} }
.codicon[class*='codicon-'] { .codicon[class*='codicon-'] {
@ -43,6 +43,10 @@
opacity: 0.5; opacity: 0.5;
} }
.codicon-modifier-hidden {
opacity: 0;
}
/* custom speed & easing for loading icon */ /* custom speed & easing for loading icon */
.codicon-loading { .codicon-loading {
animation-duration: 1s !important; animation-duration: 1s !important;
@ -551,3 +555,7 @@
.codicon-arrow-circle-left:before { content: "\ebfd" } .codicon-arrow-circle-left:before { content: "\ebfd" }
.codicon-arrow-circle-right:before { content: "\ebfe" } .codicon-arrow-circle-right:before { content: "\ebfe" }
.codicon-arrow-circle-up:before { content: "\ebff" } .codicon-arrow-circle-up:before { content: "\ebff" }
.codicon-layout-sidebar-right-off:before { content: "\ec00" }
.codicon-layout-panel-off:before { content: "\ec01" }
.codicon-layout-sidebar-left-off:before { content: "\ec02" }
.codicon-blank:before { content: "\ec03" }

Binary file not shown.

View file

@ -46,7 +46,7 @@
@media (max-width: 951px) { @media (max-width: 951px) {
#app-content { #app-content {
zoom: 0.8; // zoom: 0.8;
} }
} }
} }
@ -54,6 +54,6 @@
// if page width is less than 951px // if page width is less than 951px
@media (max-width: 951px) { @media (max-width: 951px) {
#app-content { #app-content {
zoom: 0.8; // zoom: 0.8;
} }
} }

View file

@ -5,26 +5,103 @@
#app.twopanel { #app.twopanel {
--chromeHeight1: 46px; --chromeHeight1: 46px;
--chromeHeight2: 90px; --chromeHeight2: 90px;
--chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2)); --chromeHeight : calc(var(--chromeHeight1) + var(--chromeHeight2));
.modular-fs .app-drawer .lyric-footer {
bottom: var(--chromeHeight2);
}
.app-chrome { .app-chrome {
&:not(.chrome-bottom) {
.app-chrome--center {
flex: 1;
.top-nav-group {
background : #1e1e1e99;
border : 1px solid lighten(@baseColor, 8);
border-radius: 12px;
display : flex;
height : 32px;
.app-sidebar-item {
background-color: #1e1e1e00;
border-radius : 10px !important;
border : 0px;
min-width : 120px;
padding : 6px;
justify-content : center;
align-items : center;
margin : 0px;
height : 100%;
position : relative;
&:before {
--dist : 1px;
content : '';
position : absolute;
top : var(--dist);
left : var(--dist);
right : var(--dist);
bottom : var(--dist);
background-color: #fff;
opacity : 0;
border-radius : 10px;
transform : scale(0.5);
transition : transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
}
&:after {
display: none;
}
&:hover {
background-color: transparent;
&:before {
transition: transform 0.1s ease-in-out, opacity 0.1s ease-in-out;
opacity : .1;
transform : scale(1);
}
}
&.active {
background-color: transparent;
&:before {
opacity : .2;
transform: scale(1);
}
}
&.md-btn-primary {
box-shadow : 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
background-color: lighten(@baseColor, @colorMixRate * 5);
z-index : 1;
}
}
}
}
}
.app-mainmenu { .app-mainmenu {
width: 30px; width : 30px;
height: 30px; height: 30px;
} }
.search-input { .search-input {
width: 300px; width: 180px;
} }
height: var(--chromeHeight1); height: var(--chromeHeight1);
&.chrome-bottom { &.chrome-bottom {
background: var(--color2); background : var(--color2);
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height: var(--chromeHeight2); height : var(--chromeHeight2);
box-shadow: 0px -1px 0px rgba(0, 0, 0, 0.25); box-shadow : 0px -2px 6px rgb(20 20 20 / 12%),
z-index: 1; 0px -1px 0px 0px rgb(200 200 200 / 12%);
z-index: 4;
.app-chrome-playback-duration-bottom { .app-chrome-playback-duration-bottom {
width: 100%; width: 100%;
@ -34,33 +111,33 @@
} }
.col-sm-auto { .col-sm-auto {
width: 4em; width : 4em;
display: flex; display : flex;
justify-content: center; justify-content: center;
align-items: center; align-items : center;
font-size: 0.8em; font-size : 0.8em;
} }
input[type=range] { input[type=range] {
appearance: none; appearance : none;
width: 100%; width : 100%;
height: 5px; height : 5px;
background-color: rgb(200 200 200 / 10%); background-color: rgb(200 200 200 / 10%);
border-radius: 6px; border-radius : 6px;
box-shadow: 0px 0px 0px 1px rgba(0 0 0 / 10%); box-shadow : 0px 0px 0px 1px rgba(0 0 0 / 10%);
align-self: center; align-self : center;
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 0; opacity : 0;
transform: scale(1); transform : scale(1);
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance : none;
width: 16px; width : 16px;
height: 16px; height : 16px;
border-radius: 100%; border-radius : 100%;
background: var(--keyColor); background : var(--keyColor);
cursor: default; cursor : default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition : opacity .10s var(--appleEase), transform .10s var(--appleEase);
} }
&:hover { &:hover {
@ -86,38 +163,38 @@
.playback-button.play, .playback-button.play,
.playback-button.pause, .playback-button.pause,
.playback-button.stop{ .playback-button.stop {
width: 42px; width : 42px;
height: 42px; height : 42px;
border-radius: 50%; border-radius: 50%;
margin: 6px; margin : 6px;
} }
.app-chrome--center { .app-chrome--center {
display: flex; display : flex;
flex-direction: column; flex-direction: column;
.app-chrome-playback-controls { .app-chrome-playback-controls {
display: flex; display : flex;
z-index: 1; z-index : 1;
// margin-bottom: 12px; // margin-bottom: 12px;
} }
.app-chrome-playback-duration { .app-chrome-playback-duration {
position: relative; position : relative;
width: 80%; width : 80%;
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height: 16px; height : 16px;
.song-progress { .song-progress {
@bgColor: transparent; @bgColor : transparent;
height: 16px; height : 16px;
position: absolute; position : absolute;
bottom: 4px; bottom : 4px;
left: 0px; left : 0px;
right: 4px; right : 4px;
background: @bgColor; background: @bgColor;
z-index: 0; z-index : 0;
.song-duration { .song-duration {
@ -126,41 +203,41 @@
.song-duration p { .song-duration p {
font-weight: 400; font-weight: 400;
font-size: 10px; font-size : 10px;
height: 1.2em; height : 1.2em;
line-height: 1.3em; line-height: 1.3em;
overflow: hidden; overflow : hidden;
margin: 0 0 0 0.25em; margin : 0 0 0 0.25em;
} }
&:hover { &:hover {
> input[type=range] { >input[type=range] {
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 1; opacity : 1;
transform: scale(1); transform: scale(1);
z-index: 1; z-index : 1;
} }
} }
} }
input[type=range] { input[type=range] {
appearance: none; appearance : none;
width: 100%; width : 100%;
height: 4px; height : 4px;
background-color: rgb(200 200 200 / 10%); background-color: rgb(200 200 200 / 10%);
border-radius: 2px; border-radius : 2px;
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
opacity: 0; opacity : 0;
transform: scale(0.5); transform : scale(0.5);
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance : none;
width: 12px; width : 12px;
height: 12px; height : 12px;
border-radius: 100%; border-radius : 100%;
background: var(--keyColor); background : var(--keyColor);
cursor: default; cursor : default;
transition: opacity .10s var(--appleEase), transform .10s var(--appleEase); transition : opacity .10s var(--appleEase), transform .10s var(--appleEase);
} }
} }
} }
@ -169,21 +246,21 @@
} }
.app-chrome--left { .app-chrome--left {
width: 30%; width : 30%;
justify-content: flex-start; justify-content : flex-start;
align-items: flex-start; align-items : flex-start;
-webkit-app-region: no-drag !important; -webkit-app-region: no-drag !important;
.playback-controls { .playback-controls {
-webkit-app-region: no-drag !important; -webkit-app-region: no-drag !important;
.artwork { .artwork {
--offset: 20px; --offset : 20px;
--marginOffset: 2; --marginOffset: 2;
--size: calc(var(--chromeHeight2) - var(--offset)); --size : calc(var(--chromeHeight2) - var(--offset));
width: var(--size); width : var(--size);
height: var(--size); height : var(--size);
margin: 0 calc(var(--offset) / var(--marginOffset)) 0 calc(var(--offset) / var(--marginOffset)); margin : 0 calc(var(--offset) / var(--marginOffset)) 0 calc(var(--offset) / var(--marginOffset));
.mediaitem-artwork, .mediaitem-artwork,
img { img {
@ -193,20 +270,25 @@
.playback-info { .playback-info {
align-items: flex-start; align-items: flex-start;
margin: 6px; margin : 6px;
.song-name { .song-name {
text-align: left; text-align : left;
font-size: 0.98em; font-size : 0.8em;
font-weight: 500; font-weight : 500;
width: 100%; width : 100%;
-webkit-mask-image: linear-gradient(-90deg, transparent 0%, transparent 10%, black 20%); -webkit-mask-image: linear-gradient(-90deg, transparent 0%, transparent 10%, black 20%);
} }
.song-artist, .song-album { .song-artist,
.song-album {
font-size: 0.75em; font-size: 0.75em;
opacity: 0.8; opacity: 0.8;
cursor: pointer; cursor: pointer;
white-space: nowrap;
max-width: 360px;
-webkit-mask-image: linear-gradient(-90deg, transparent 0%, transparent 10%, black 20%);
width: 100%;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
@ -219,23 +301,23 @@
.song-artist-album-content { .song-artist-album-content {
text-align: left; text-align: left;
font-size: 12px; font-size : 12px;
} }
} }
width: 100%; width : 100%;
height: 100%; height : 100%;
max-width: 100%; max-width: 100%;
border: 0px; border : 0px;
} }
flex: 0 0 auto; flex: 0 0 auto;
} }
.app-chrome--right { .app-chrome--right {
width: 30%; width : 30%;
flex: 0 0 auto; flex : 0 0 auto;
padding-right: 8px; padding-right: 8px;
} }
} }
@ -245,4 +327,36 @@
bottom: 96px; bottom: 96px;
} }
} }
} }
// screen width is less than 768px
@media (max-width: 1100px) {
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center {
flex: unset;
}
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center .top-nav-group .app-sidebar-item {
min-width: 110px;
font-size: 0em;
.sidebar-icon {
margin: 0px;
}
}
}
@media (max-width: 1000px) {
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center {
flex: unset;
}
#app.twopanel .app-chrome:not(.chrome-bottom) .app-chrome--center .top-nav-group .app-sidebar-item {
min-width: 60px;
font-size: 0em;
.sidebar-icon {
margin: 0px;
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,107 +1,91 @@
import { CiderCache } from "./cidercache.js" import { CiderCache } from "./cidercache.js";
async function spawnMica() { async function spawnMica() {
if (typeof window.micaSpawned !== "undefined") { if (typeof window.micaSpawned !== "undefined") {
return return;
} else {
window.micaSpawned = true;
}
const micaDiv = document.createElement("div");
const blurIterations = 6;
micaDiv.id = "micaEffect";
micaDiv.style.position = "fixed";
micaDiv.style.top = "0";
micaDiv.style.left = "0";
micaDiv.style.right = "0";
micaDiv.style.bottom = "0";
micaDiv.style.zIndex = -1;
let lastScreenX;
let lastScreenY;
let lastScreenWidth;
let lastScreenHeight;
let regen = true;
let imgSrc = await ipcRenderer.sendSync("get-wallpaper", {
blurAmount: 256
});
// let micaCache = await CiderCache.getCache("mica-cache");
// if (!micaCache) {
// micaCache = {
// path: "",
// data: "",
// };
// }
// if (micaCache.path == imgSrc.path) {
// regen = false;
// imgSrc = micaCache;
// }
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let img = new Image();
micaDiv.style.backgroundImage = `url(${imgSrc.data})`;
document.body.appendChild(micaDiv);
function onScreenMove(cb) {
function detectScreenMove() {
if (lastScreenY !== window.screenY || lastScreenX !== window.screenX) {
lastScreenY = window.screenY;
lastScreenX = window.screenX;
cb();
}
// window size change
if (
lastScreenWidth !== window.innerWidth ||
lastScreenHeight !== window.innerHeight
) {
lastScreenWidth = window.innerWidth;
lastScreenHeight = window.innerHeight;
cb();
}
if (true) {
requestAnimationFrame(detectScreenMove);
}
}
if (true) {
requestAnimationFrame(detectScreenMove);
}
}
onScreenMove(function () {
const screenHeight = window.screen.height;
const screenWidth = window.screen.width;
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
const ratio = windowWidth / windowHeight;
const x = window.screenX;
const y = window.screenY;
micaDiv.style.backgroundSize = `${screenWidth}px ${screenHeight}px`;
// micaDiv.style.backgroundPosition = `-${x}px -${y}px`;
if (x < 0) {
micaDiv.style.backgroundPosition = `${screenWidth + x}px -${y}px`;
} else { } else {
window.micaSpawned = true micaDiv.style.backgroundPosition = `-${x}px -${y}px`;
} }
const micaDiv = document.createElement('div'); });
const blurIterations = 6 return true;
micaDiv.id = 'micaEffect';
micaDiv.style.position = "fixed"
micaDiv.style.top = "0"
micaDiv.style.left = "0"
micaDiv.style.right = "0"
micaDiv.style.bottom = "0"
micaDiv.style.zIndex = -1
let lastScreenX;
let lastScreenY;
let lastScreenWidth;
let lastScreenHeight;
let regen = true
let imgSrc = await ipcRenderer.sendSync("get-wallpaper")
let micaCache = await CiderCache.getCache("mica-cache")
if (!micaCache) {
micaCache = {
path: "",
data: ""
}
}
if (micaCache.path == imgSrc.path) {
regen = false
imgSrc = micaCache
}
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
let img = new Image();
img.src = imgSrc.data;
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
if (regen) {
for (let i = 0; i < blurIterations; i++) {
StackBlur.canvasRGB(canvas, 0, 0, img.width, img.height, 128);
}
micaCache.path = imgSrc.path
micaCache.data = canvas.toDataURL()
CiderCache.putCache("mica-cache", micaCache)
}
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
micaDiv.style.backgroundImage = `url(${micaCache.data})`;
document.body.appendChild(micaDiv);
// on animation finished set animation to unset
micaDiv.addEventListener('animationend', function () {
micaDiv.style.opacity = '1';
micaDiv.style.animation = 'unset';
})
}
function onScreenMove(cb) {
function detectScreenMove() {
if (lastScreenY !== window.screenY || lastScreenX !== window.screenX) {
lastScreenY = window.screenY;
lastScreenX = window.screenX;
cb();
}
// window size change
if (lastScreenWidth !== window.innerWidth || lastScreenHeight !== window.innerHeight) {
lastScreenWidth = window.innerWidth;
lastScreenHeight = window.innerHeight;
cb();
}
if (true) {
requestAnimationFrame(detectScreenMove);
}
}
if (true) {
requestAnimationFrame(detectScreenMove);
}
}
onScreenMove(function () {
const screenHeight = window.screen.height;
const screenWidth = window.screen.width;
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
const ratio = windowWidth / windowHeight;
const x = window.screenX;
const y = window.screenY;
micaDiv.style.backgroundSize = `${screenWidth}px ${screenHeight}px`;
// micaDiv.style.backgroundPosition = `-${x}px -${y}px`;
if (x < 0) {
micaDiv.style.backgroundPosition = `${screenWidth + x}px -${y}px`;
} else {
micaDiv.style.backgroundPosition = `-${x}px -${y}px`;
}
});
return true
} }
export { spawnMica } export { spawnMica };

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,14 @@ const store = new Vuex.Store({
// recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"), // recentlyAdded: ipcRenderer.sendSync("get-library-recentlyAdded"),
// playlists: ipcRenderer.sendSync("get-library-playlists") // playlists: ipcRenderer.sendSync("get-library-playlists")
}, },
pageState: {
recentlyAdded: {
loaded: false,
nextUrl: null,
items: [],
size: "normal"
}
},
artwork: { artwork: {
playerLCD: "" playerLCD: ""
} }

View file

@ -4,6 +4,7 @@
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@100;300;400;500;700;900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@100;300;400;500;700;900&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100;300;400;500;700;900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100;300;400;500;700;900&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap");
@import url("less/appvars.less");
@import url("less/bootstrap-vue.min.less"); @import url("less/bootstrap-vue.min.less");
@import url("less/ameframework.less"); @import url("less/ameframework.less");
@import url("less/codicon.css"); @import url("less/codicon.css");
@ -29,7 +30,8 @@
--selected: rgb(130 130 130 / 30%); --selected: rgb(130 130 130 / 30%);
--selected-click: rgb(80 80 80 / 30%); --selected-click: rgb(80 80 80 / 30%);
--hover: rgb(200 200 200 / 10%); --hover: rgb(200 200 200 / 10%);
--keyColor: #fa586a; // --keyColor: #fa586a;
--keyColor: @keyColor;
--keyColor-rgb: 250, 88, 106; --keyColor-rgb: 250, 88, 106;
--keyColor-rollover: #ff8a9c; --keyColor-rollover: #ff8a9c;
--keyColor-rollover-rgb: 255, 138, 156; --keyColor-rollover-rgb: 255, 138, 156;
@ -46,6 +48,7 @@
--textColor: #eee; --textColor: #eee;
--replayGradient: linear-gradient(45deg, hsl(248deg 58% 29%), hsl(13deg 41% 42%)); --replayGradient: linear-gradient(45deg, hsl(248deg 58% 29%), hsl(13deg 41% 42%));
--glassFilter: blur(16px) saturate(180%); --glassFilter: blur(16px) saturate(180%);
--sidebarWidth: 260px;
} }
*:focus-visible { *:focus-visible {
@ -164,7 +167,7 @@ body.notransparency::before {
--chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2)); --chromeHeight: calc(var(--chromeHeight1) + var(--chromeHeight2));
width: 100%; width: 100%;
height: 100%; height: 100%;
background: var(--color1); background: transparent;
color: var(--textColor); color: var(--textColor);
user-select: none; user-select: none;
margin: 0 auto; margin: 0 auto;
@ -174,23 +177,7 @@ body.notransparency::before {
&.simplebg { &.simplebg {
background: #0e0e0e; background: #0e0e0e;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.5;
z-index: 0;
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==);
} }
.app-chrome {
z-index: 1;
}
}
} }
.bgGradientMaterial-base { .bgGradientMaterial-base {
@ -383,16 +370,19 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
flex-direction: column; flex-direction: column;
opacity: 1; opacity: 1;
overflow: hidden; overflow: hidden;
background-color: rgba(20 20 20 / .7);
} }
#app-sidebar { #app-sidebar {
/* background-color: var(--color1); */
height: 100%; height: 100%;
width: 260px; width: var(--sidebarWidth);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 0 0 auto; flex: 0 0 auto;
position: relative; position: relative;
background: linear-gradient(180deg, var(--baseColorMix) calc(var(--chromeHeight1) + 1px), var(--sidebarColorMix) calc(var(--chromeHeight1) + 1px));
max-width: var(--sidebarWidth);
padding-top: var(--chromeHeight1);
} }
#app-navbar { #app-navbar {
@ -407,16 +397,21 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
} }
#app-content { #app-content {
background-color: var(--color3); --navigationBarHeight: var(--chromeHeight1);
background-color: var(--baseColorMix);
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow-y: scroll; overflow-y: scroll;
overflow-y: overlay; overflow-y: overlay;
overflow-x: hidden; overflow-x: hidden;
border-radius: 10px 0px 0px 0px; border-radius: 0;
border-left: 1px solid rgb(0 0 0 / 25%); border-left: 1px solid var(--baseColorMix);
border-top: 1px solid rgb(0 0 0 / 25%); // border-top: 1px solid rgb(0 0 0 / 25%);
position: relative; position: relative;
&::-webkit-scrollbar-track-piece {
margin-top: var(--chromeHeight1);
}
} }
.app-drawer { .app-drawer {
@ -424,14 +419,14 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
flex: 0 0 auto; flex: 0 0 auto;
position: absolute; position: absolute;
right: 16px; right: 16px;
top: 3%;
background: var(--color2); background: var(--color2);
border-radius: 12px; border-radius: 12px;
z-index: 10; z-index: 10;
height: 94%;
backdrop-filter: blur(40px) saturate(180%); backdrop-filter: blur(40px) saturate(180%);
box-shadow: var(--ciderShadow-Generic); box-shadow: var(--ciderShadow-Generic);
overflow: hidden; overflow: hidden;
height: calc(calc(100% - 6%) - var(--chromeHeight1));
top: calc(var(--chromeHeight1) + 3%);
.bgArtworkMaterial { .bgArtworkMaterial {
display: none; display: none;
@ -453,15 +448,16 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
width: 100%; width: 100%;
padding: 6px; padding: 6px;
border-radius: 6px; border-radius: 6px;
border: 1px solid rgb(200 200 200 / 10%); border: 1px solid rgba(100, 100, 100, 0.35);
border-top: 1px solid rgba(100, 100, 100, 0.5);
border-bottom: 1px solid rgb(60 60 60 / 62%);
font-family: inherit; font-family: inherit;
font-size: 14px; font-size: 14px;
background: rgb(100 100 100 / 25%); background: #1e1e1e99;
color: rgb(200 200 200); color: #c8c8c8;
font-weight: 500; font-weight: 500;
padding-left: 32px; padding-left: 32px;
position: relative; position: relative;
filter: contrast(0.1);
} }
.search-input:focus { .search-input:focus {
@ -531,6 +527,40 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
display: flex; display: flex;
} }
} }
&.collapseTab {
display:flex;
padding:6px;
border:0;
>button {
appearance: none;
width: 100%;
position: relative;
padding-left: 40px;
text-align: left;
font-family: inherit;
&:hover {
background-color: var(--selected);
}
&:active {
background-color: var(--selected-click);
}
&:after {
content: '';
display: flex;
justify-content: center;
align-items: center;
width: 46px;
height: 100%;
position: absolute;
top: 0;
left: 0;
font-weight: bold;
font-size: 1em;
font-family: "codicon";
}
}
}
} }
.app-sidebar-button { .app-sidebar-button {
@ -633,7 +663,9 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
#cmenu() { #cmenu() {
.container { .container {
position: absolute; position: absolute;
width: 100%; left: 0px;
width: var(--sidebarWidth);
max-width: var(--sidebarWidth);
padding: 10px; padding: 10px;
z-index: 1; z-index: 1;
} }
@ -690,7 +722,7 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
} }
.usermenu-container { .usermenu-container {
top: 0px; top: var(--chromeHeight1);
z-index: 200001 !important; z-index: 200001 !important;
#cmenu.container(); #cmenu.container();
@ -874,36 +906,61 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
cursor: default; cursor: default;
} }
.app-sidebar-item.active::after {
content: '';
width: 4px;
height: 16px;
display: block;
position: absolute;
top: calc(100% - 72%);
border-radius: 10px;
left: 0px;
background: var(--keyColor);
// animation: expandIndicator .2s cubic-bezier(0.25, 1, 0.5, 1);
// @keyframes expandIndicator {
// 0% {
// transform: scaleY(0);
// }
// 100% {
// transform: scaleY(1);
// }
// }
}
.app-chrome { .app-chrome {
background: var(--color1); background-color: var(--baseColorMix);
box-shadow: 0px 3px 6px rgb(20 20 20 / 12%),
0px 1px 0px 0px rgb(200 200 200 / 12%);
width: 100%; width: 100%;
height: var(--chromeHeight1); height: var(--chromeHeight1);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
-webkit-app-region: drag; -webkit-app-region: drag;
z-index: 4;
&:not(.chrome-bottom) {
// box-shadow: 0px 0px
z-index: 16;
position: fixed;
backdrop-filter: var(--glassFilter);
.app-chrome--center {
flex:1;
}
}
.top-nav-group {
background: var(--baseColor);
border: 1px solid lighten(@baseColor, 8);
border-radius: 10px;
display: flex;
height: 32px;
.app-sidebar-item {
background-color: var(--baseColor);
border-radius: 10px!important;
border:0px;
min-width: 120px;
padding:6px;
justify-content: center;
align-items: center;
margin: 0px;
height: 100%;
&:hover {
background-color: lighten(@baseColor, @colorMixRate * 5);
}
&.active {
background-color: lighten(@baseColor, @colorMixRate * 5);
}
&.md-btn-primary {
box-shadow: 0px 0px 0px 1px lighten(@baseColor, @colorMixRate * 8);
background-color: lighten(@baseColor, @colorMixRate * 5);
z-index: 1;
}
}
}
.vdiv { .vdiv {
width: 1px; width: 1px;
@ -959,16 +1016,21 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
align-items: center; align-items: center;
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
height: auto; height: auto;
}
.app-chrome .app-chrome-item.generic {
width: 50px;
opacity: 0.70;
}
.app-chrome .app-chrome-item.volume { &.generic {
width: 100px; width: 50px;
margin-right: 6px; opacity: 0.70;
}
&.volume {
width: 100px;
margin-right: 6px;
}
&.search {
margin-right: 6px;
}
} }
.volume-button { .volume-button {
@ -1442,8 +1504,11 @@ div[data-type="musicVideo"] .info-rect .title::before {
} }
.app-navigation { .app-navigation {
background : transparent;
align-items : center;
justify-content: center;
background: var(--color1); background: var(--color1);
height: calc(100% - var(--chromeHeight)); height: calc(100% - var(--chromeHeight2));
width: 100%; width: 100%;
display: flex; display: flex;
position: relative; position: relative;
@ -1579,7 +1644,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
overflow-x: hidden; overflow-x: hidden;
display: flex; display: flex;
flex-flow: column; flex-flow: column;
font-family: 'Inter', 'Noto Sans JP', 'Source Han Sans SC', 'Source Han Sans HK', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif; font-family: "Pretendard Variable", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
} }
.lyric-body .no-lyrics { .lyric-body .no-lyrics {
@ -1613,6 +1678,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyric-line.active .verse.verse-active { .lyric-line.active .verse.verse-active {
opacity: 1; opacity: 1;
transition: opacity 0.35s var(--appleEase);
} }
.lyric-line:hover { .lyric-line:hover {
@ -1638,7 +1704,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
opacity: 1; opacity: 1;
transform: scale(1); transform: scale(1);
/*background: var(--keyColor);*/ /*background: var(--keyColor);*/
transition: transform 0.2s var(--appleEase); transition: transform 0.2s var(--appleEase), opacity 0.35s var(--appleEase);
} }
.lyric-line:not(.active) { .lyric-line:not(.active) {
@ -1683,7 +1749,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
.lyrics-translation { .lyrics-translation {
font-size: 1.6rem; font-size: 1.6rem;
font-weight: 450; font-weight: 450;
font-family: 'Inter', 'Noto Sans JP', 'Noto Sans SC', 'Noto Sans TC', 'Noto Sans HK', 'Noto Sans KR', sans-serif; font-family: "Pretendard Variable", -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
filter: contrast(0.5); filter: contrast(0.5);
} }
@ -1909,7 +1975,6 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
background: rgba(200, 200, 200, 0.05); background: rgba(200, 200, 200, 0.05);
border-radius: 10px; border-radius: 10px;
padding: var(--contentInnerPadding); padding: var(--contentInnerPadding);
box-shadow: rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;
margin-top: 16px; margin-top: 16px;
@ -1917,6 +1982,9 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
display: flex; display: flex;
flex-flow: wrap; flex-flow: wrap;
justify-content: center; justify-content: center;
.cd-mediaitem-square-container{
align-items: center;
}
.cd-mediaitem-square { .cd-mediaitem-square {
width: 220px; width: 220px;
height: 260px; height: 260px;
@ -1930,12 +1998,27 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
max-width: 240px; max-width: 240px;
flex-grow: 1; flex-grow: 1;
} }
&.collection-list-square {
display: grid;
grid-template-columns: repeat(4, minmax(200px, 1fr));
// screen size > 1200px
@media screen and (min-width: 1500px) {
grid-template-columns: repeat(6, minmax(200px, 1fr));
}
// less than 1100px
@media screen and (max-width: 1150px) {
grid-template-columns: repeat(3, minmax(200px, 1fr));
}
}
} }
} }
.cd-mediaitem-square-container{ .cd-mediaitem-square-container{
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
justify-content: center;
} }
.reasonSP{ .reasonSP{
@ -2755,7 +2838,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
#micaEffect { #micaEffect {
opacity:1; opacity:1;
// animation: micaEnter 1s ease-in-out forwards; // animation: micaEnter 1s ease-in-out forwards;
filter: brightness(0.5); filter: brightness(1) saturate(320%);
@keyframes micaEnter { @keyframes micaEnter {
0% { 0% {
opacity: 0; opacity: 0;
@ -2781,6 +2864,16 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
// Modular // Modular
.modular-fs { .modular-fs {
.app-chrome.chrome-bottom {
position: fixed;
bottom: 0;
left: 0;
z-index: 14!important;
backdrop-filter: var(--glassFilter);
}
.app-navigation {
height: 100%;
}
.app-drawer { .app-drawer {
width: 100%; width: 100%;
right: 0px; right: 0px;
@ -2862,6 +2955,16 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
transform: translateY(20px); transform: translateY(20px);
} }
.fade-enter-active,
.fade-leave-active {
transition: opacity .15s var(--appleEase);
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.modal-enter-active, .modal-enter-active,
.modal-leave-active { .modal-leave-active {
transition: opacity .1s var(--appleEase), transform .1s var(--appleEase); transition: opacity .1s var(--appleEase), transform .1s var(--appleEase);
@ -2943,6 +3046,15 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
opacity: 0; opacity: 0;
} }
.sidebartransition-enter-active,
.sidebartransition-leave-active {
transition: margin-left .35s var(--appleEase);
}
.sidebartransition-enter,
.sidebartransition-leave-to {
margin-left: calc(var(--sidebarWidth) * -1);
}
.drawertransition-enter-active, .drawertransition-enter-active,
.drawertransition-leave-active { .drawertransition-leave-active {
@ -3056,7 +3168,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb {
} }
#app.twopanel #apple-music-video-container { #app.twopanel #apple-music-video-container {
top: var(--chromeHeight1); top:0;
bottom: unset; bottom: unset;
} }
.inactive { .inactive {
@ -3503,4 +3615,4 @@ body[platform='darwin'] {
@import url("less/linux.less"); @import url("less/linux.less");
@import url("less/compact.less"); @import url("less/compact.less");
@import url("less/directives.less"); @import url("less/directives.less");
@import url("less/macosemu.less"); @import url("less/macosemu.less");

View file

@ -1,4 +1,4 @@
<div id="app-content" @scroll.passive="setContentScrollPos" :scrollpos="chrome.contentScrollPosY" scrollaxis="y" :style="{'overflow': (chrome.contentAreaScrolling ? '' : 'hidden')}"> <div id="app-content" :scrollpos="chrome.contentScrollPosY" scrollaxis="y" :style="{'overflow': (chrome.contentAreaScrolling ? '' : 'hidden')}">
<div id="navigation-bar" v-if="getThemeDirective('appNavigation') == 'seperate'"> <div id="navigation-bar" v-if="getThemeDirective('appNavigation') == 'seperate'">
<button class="nav-item" @click="navigateBack()"> <button class="nav-item" @click="navigateBack()">
<%- include('../svg/chevron-left.svg') %> <%- include('../svg/chevron-left.svg') %>
@ -24,11 +24,6 @@
</template> </template>
</transition> </transition>
<% } %> <% } %>
<!-- Library - Recently Added -->
<transition :name="chrome.desiredPageTransition" v-on:enter="getLibraryAlbumsFull(null, 0); searchLibraryAlbums(0);">
<%- include('../pages/library-recentlyadded') %>');
</transition>
<!-- Library - Made For You --> <!-- Library - Made For You -->
<transition :name="chrome.desiredPageTransition" v-on:enter="getMadeForYou()"> <transition :name="chrome.desiredPageTransition" v-on:enter="getMadeForYou()">
<template v-if="page == 'library-madeforyou'"> <template v-if="page == 'library-madeforyou'">

View file

@ -1,5 +1,132 @@
<div class="app-navigation" v-cloak> <div class="app-navigation" v-cloak>
<%- include("sidebar") %> <transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened">
<div class="usermenu-body">
<button
class="app-sidebar-button"
style="width: 100%"
@click="appRoute('apple-account-settings')"
>
<img
class="sidebar-user-icon"
loading="lazy"
:src="getMediaItemArtwork(chrome.hideUserInfo ? 'http://localhost:9000/assets/logocut.png' : (chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : ''), 26)"
/>
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.id || mk.isAuthorized">
<div class="fullname text-overflow-elipsis">
{{
chrome.userinfo != null &&
chrome.userinfo.attributes != null
? chrome.userinfo.attributes.name ?? ""
: ""
}}
</div>
<div class="handle-text text-overflow-elipsis">
{{
chrome.userinfo != null &&
chrome.userinfo.attributes != null
? chrome.userinfo.attributes.handle ?? ""
: ""
}}
</div>
</template>
<template v-else>
<div @click="mk.authorize()">
{{ $root.getLz("term.login") }}
</div>
</template>
</div>
<div class="sidebar-user-text" v-else>
{{ $root.getLz("app.name") }}
</div>
</button>
<!-- Use 20px SVG for usermenu icon -->
<button
class="usermenu-item"
v-if="cfg.general.privateEnabled"
@click="cfg.general.privateEnabled = false"
>
<span class="usermenu-item-icon">
<%- include("../svg/x.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.disablePrivateSession")
}}</span>
</button>
<button class="usermenu-item" @click="appRoute('remote-pair')">
<span class="usermenu-item-icon">
<%- include("../svg/smartphone.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("action.showWebRemoteQR")
}}</span>
</button>
<button
class="usermenu-item"
@click="cfg.advanced.AudioContext ? modals.castMenu = true : $root.notyf.error($root.getLz('settings.warn.enableAdvancedFunctionality'))"
>
<span class="usermenu-item-icon">
<%- include("../svg/cast.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.cast")
}}</span>
</button>
<button
class="usermenu-item"
@click="cfg.advanced.AudioContext ? modals.audioSettings = true : $root.notyf.error($root.getLz('settings.warn.enableAdvancedFunctionality'))"
>
<span class="usermenu-item-icon">
<%- include("../svg/headphones.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.audioSettings")
}}</span>
</button>
<button
class="usermenu-item"
v-if="pluginInstalled"
@click="modals.pluginMenu = true"
>
<span class="usermenu-item-icon">
<%- include("../svg/grid.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.plugin")
}}</span>
</button>
<button class="usermenu-item" @click="appRoute('about')">
<span class="usermenu-item-icon">
<%- include("../svg/info.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.about")
}}</span>
</button>
<button class="usermenu-item" @click="appRoute('settings')">
<span class="usermenu-item-icon">
<%- include("../svg/settings.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.settings")
}}</span>
</button>
<button class="usermenu-item" @click="unauthorize()">
<span class="usermenu-item-icon" style="right: 2.5px">
<%- include("../svg/log-out.svg") %>
</span>
<span class="usermenu-item-name">{{
$root.getLz("term.logout")
}}</span>
</button>
</div>
</div>
</transition>
<transition name="sidebartransition">
<%- include("sidebar") %>
</transition>
<%- include("app-content") %> <%- include("app-content") %>
<transition name="drawertransition"> <transition name="drawertransition">
<div class="app-drawer" <div class="app-drawer"

View file

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

View file

@ -13,9 +13,6 @@
<transition name="modal"> <transition name="modal">
<add-to-playlist :playlists="playlists.listing" v-if="modals.addToPlaylist"></add-to-playlist> <add-to-playlist :playlists="playlists.listing" v-if="modals.addToPlaylist"></add-to-playlist>
</transition> </transition>
<transition name="modal">
<spatial-properties v-if="modals.spatialProperties"></spatial-properties>
</transition>
<transition name="modal"> <transition name="modal">
<audio-controls v-if="modals.audioControls"></audio-controls> <audio-controls v-if="modals.audioControls"></audio-controls>
</transition> </transition>

View file

@ -1,245 +1,303 @@
<div id="app-sidebar"> <div id="app-sidebar" v-if="!chrome.sidebarCollapsed">
<template >
<template v-if="getThemeDirective('windowLayout') != 'twopanel'"> <template v-if="getThemeDirective('windowLayout') != 'twopanel'">
<div class="app-sidebar-header"> <div class="app-sidebar-header">
<div class="search-input-container"> <div class="search-input-container">
<div class="search-input--icon"></div> <div class="search-input--icon"></div>
<input type="search" spellcheck="false" @click="showSearch()" @focus="search.showHints = true" <input
@blur="setTimeout(()=>{search.showHints = false}, 300)" type="search"
v-on:keyup.enter="searchQuery();search.showHints = false" @change="showSearch();" spellcheck="false"
@input="getSearchHints()" :placeholder="$root.getLz('term.search') + '...'" v-model="search.term" @click="showSearch()"
ref="searchInput" class="search-input"> @focus="search.showHints = true"
@blur="setTimeout(()=>{search.showHints = false}, 300)"
v-on:keyup.enter="searchQuery();search.showHints = false"
@change="showSearch();"
@input="getSearchHints()"
:placeholder="$root.getLz('term.search') + '...'"
v-model="search.term"
ref="searchInput"
class="search-input"
/>
<div class="search-hints-container" v-if="search.showHints && search.hints.length != 0"> <div
<div class="search-hints"> class="search-hints-container"
<button class="search-hint text-overflow-elipsis" v-for="hint in search.hints" v-if="search.showHints && search.hints.length != 0"
@click="search.term = hint;search.showHints = false;searchQuery(hint)"> >
{{ hint }} <div class="search-hints">
</button> <button
</div> class="search-hint text-overflow-elipsis"
</div> v-for="hint in search.hints"
@click="search.term = hint;search.showHints = false;searchQuery(hint)"
>
{{ hint }}
</button>
</div> </div>
</div>
</div> </div>
</div>
</template> </template>
<div class="app-sidebar-content" scrollaxis="y"> <div class="app-sidebar-content" scrollaxis="y">
<div class="app-sidebar-header-text" <!-- AM Navigation -->
@click="cfg.general.sidebarCollapsed.cider = !cfg.general.sidebarCollapsed.cider" <template v-if="getThemeDirective('windowLayout') != 'twopanel'">
:class="{collapsed: cfg.general.sidebarCollapsed.cider}"> <div
{{$root.getLz('app.name')}} class="app-sidebar-header-text"
@click="cfg.general.sidebarCollapsed.cider = !cfg.general.sidebarCollapsed.cider"
:class="{collapsed: cfg.general.sidebarCollapsed.cider}"
>
{{ $root.getLz("app.name") }}
</div> </div>
<template v-if="!cfg.general.sidebarCollapsed.cider"> <template v-if="!cfg.general.sidebarCollapsed.cider">
<sidebar-library-item :name="$root.getLz('home.title')" svg-icon="./assets/feather/home.svg" page="home"> <sidebar-library-item
</sidebar-library-item> :name="$root.getLz('home.title')"
svg-icon="./assets/feather/home.svg"
page="home"
>
</sidebar-library-item>
</template> </template>
<div class="app-sidebar-header-text" <div
@click="cfg.general.sidebarCollapsed.applemusic = !cfg.general.sidebarCollapsed.applemusic" class="app-sidebar-header-text"
:class="{collapsed: cfg.general.sidebarCollapsed.applemusic}"> @click="cfg.general.sidebarCollapsed.applemusic = !cfg.general.sidebarCollapsed.applemusic"
{{$root.getLz('term.appleMusic')}} :class="{collapsed: cfg.general.sidebarCollapsed.applemusic}"
>
{{ $root.getLz("term.appleMusic") }}
</div> </div>
<template v-if="!cfg.general.sidebarCollapsed.applemusic"> <template v-if="!cfg.general.sidebarCollapsed.applemusic">
<sidebar-library-item :name="$root.getLz('term.listenNow')" svg-icon="./assets/feather/play-circle.svg" <sidebar-library-item
page="listen_now"></sidebar-library-item> :name="$root.getLz('term.listenNow')"
<sidebar-library-item :name="$root.getLz('term.browse')" svg-icon="./assets/feather/globe.svg" svg-icon="./assets/feather/play-circle.svg"
page="browse"> page="listen_now"
</sidebar-library-item> ></sidebar-library-item>
<sidebar-library-item :name="$root.getLz('term.radio')" svg-icon="./assets/feather/radio.svg" <sidebar-library-item
page="radio"></sidebar-library-item> :name="$root.getLz('term.browse')"
svg-icon="./assets/feather/globe.svg"
page="browse"
>
</sidebar-library-item>
<sidebar-library-item
:name="$root.getLz('term.radio')"
svg-icon="./assets/feather/radio.svg"
page="radio"
></sidebar-library-item>
</template> </template>
</template>
<div class="app-sidebar-header-text" <div
class="app-sidebar-header-text"
@click="cfg.general.sidebarCollapsed.library = !cfg.general.sidebarCollapsed.library" @click="cfg.general.sidebarCollapsed.library = !cfg.general.sidebarCollapsed.library"
:class="{collapsed: cfg.general.sidebarCollapsed.library}"> :class="{collapsed: cfg.general.sidebarCollapsed.library}"
{{$root.getLz('term.library')}} >
</div> {{ $root.getLz("term.library") }}
<template v-if="!cfg.general.sidebarCollapsed.library"> </div>
<sidebar-library-item :name="$root.getLz('term.recentlyAdded')" svg-icon="./assets/feather/plus-circle.svg" <template v-if="!cfg.general.sidebarCollapsed.library">
v-if="cfg.general.sidebarItems.recentlyAdded" page="library-recentlyadded"></sidebar-library-item> <sidebar-library-item
<sidebar-library-item :name="$root.getLz('term.songs')" svg-icon="./assets/feather/music.svg" :name="$root.getLz('term.recentlyAdded')"
v-if="cfg.general.sidebarItems.songs" page="library-songs"></sidebar-library-item> svg-icon="./assets/feather/plus-circle.svg"
<sidebar-library-item :name="$root.getLz('term.albums')" svg-icon="./assets/feather/disc.svg" v-if="cfg.general.sidebarItems.recentlyAdded"
v-if="cfg.general.sidebarItems.albums" page="library-albums"></sidebar-library-item> page="library-recentlyadded"
<sidebar-library-item :name="$root.getLz('term.artists')" svg-icon="./assets/feather/user.svg" ></sidebar-library-item>
v-if="cfg.general.sidebarItems.artists" page="library-artists"></sidebar-library-item> <sidebar-library-item
<sidebar-library-item :name="$root.getLz('term.videos')" svg-icon="./assets/feather/video.svg" :name="$root.getLz('term.songs')"
v-if="cfg.general.sidebarItems.videos" page="library-videos"></sidebar-library-item> svg-icon="./assets/feather/music.svg"
<sidebar-library-item :name="$root.getLz('term.podcasts')" svg-icon="./assets/feather/mic.svg" v-if="cfg.general.sidebarItems.songs"
v-if="cfg.general.sidebarItems.podcasts" page="podcasts"> page="library-songs"
</sidebar-library-item> ></sidebar-library-item>
</template> <sidebar-library-item
:name="$root.getLz('term.albums')"
svg-icon="./assets/feather/disc.svg"
v-if="cfg.general.sidebarItems.albums"
page="library-albums"
></sidebar-library-item>
<sidebar-library-item
:name="$root.getLz('term.artists')"
svg-icon="./assets/feather/user.svg"
v-if="cfg.general.sidebarItems.artists"
page="library-artists"
></sidebar-library-item>
<sidebar-library-item
:name="$root.getLz('term.videos')"
svg-icon="./assets/feather/video.svg"
v-if="cfg.general.sidebarItems.videos"
page="library-videos"
></sidebar-library-item>
<sidebar-library-item
:name="$root.getLz('term.podcasts')"
svg-icon="./assets/feather/mic.svg"
v-if="cfg.general.sidebarItems.podcasts"
page="podcasts"
>
</sidebar-library-item>
</template>
<template v-if="getPlaylistFolderChildren('p.applemusic').length != 0"> <template v-if="getPlaylistFolderChildren('p.applemusic').length != 0">
<div class="app-sidebar-header-text" <div
@click="cfg.general.sidebarCollapsed.amplaylists = !cfg.general.sidebarCollapsed.amplaylists" class="app-sidebar-header-text"
@contextmenu="playlistHeaderContextMenu" @click="cfg.general.sidebarCollapsed.amplaylists = !cfg.general.sidebarCollapsed.amplaylists"
:class="{collapsed: cfg.general.sidebarCollapsed.amplaylists}"> @contextmenu="playlistHeaderContextMenu"
{{ $root.getLz('term.appleMusic') }} {{ $root.getLz('term.playlists') }} :class="{collapsed: cfg.general.sidebarCollapsed.amplaylists}"
</div> >
<template v-if="!cfg.general.sidebarCollapsed.amplaylists"> {{ $root.getLz("term.appleMusic") }}
<sidebar-playlist v-for="item in getPlaylistFolderChildren('p.applemusic')" :item="item"> {{ $root.getLz("term.playlists") }}
</sidebar-playlist> </div>
</template> <template v-if="!cfg.general.sidebarCollapsed.amplaylists">
<sidebar-playlist
v-for="item in getPlaylistFolderChildren('p.applemusic')"
:item="item"
>
</sidebar-playlist>
</template> </template>
<div class="app-sidebar-header-text" </template>
<div
class="app-sidebar-header-text"
@click="cfg.general.sidebarCollapsed.playlists = !cfg.general.sidebarCollapsed.playlists" @click="cfg.general.sidebarCollapsed.playlists = !cfg.general.sidebarCollapsed.playlists"
@contextmenu="playlistHeaderContextMenu" @contextmenu="playlistHeaderContextMenu"
:class="{collapsed: cfg.general.sidebarCollapsed.playlists}"> :class="{collapsed: cfg.general.sidebarCollapsed.playlists}"
{{ $root.getLz('term.playlists') }} >
</div> {{ $root.getLz("term.playlists") }}
<template v-if="!cfg.general.sidebarCollapsed.playlists"> </div>
<button class="app-sidebar-item" @click="playlistHeaderContextMenu"> <template v-if="!cfg.general.sidebarCollapsed.playlists">
<div class="sidebar-icon"> <button class="app-sidebar-item" @click="playlistHeaderContextMenu">
<svg width="46" height="46" fill="none" stroke="currentColor" stroke-linecap="round" <div class="sidebar-icon">
stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg
<path d="M12 5v14"></path> width="46"
<path d="M5 12h14"></path> height="46"
</svg> fill="none"
</div> stroke="currentColor"
{{ getLz('action.createNew') }} stroke-linecap="round"
</button> stroke-linejoin="round"
<sidebar-playlist v-for="item in getPlaylistFolderChildren('p.playlistsroot')" :item="item"> stroke-width="2"
</sidebar-playlist> viewBox="0 0 24 24"
</template> xmlns="http://www.w3.org/2000/svg"
>
<path d="M12 5v14"></path>
<path d="M5 12h14"></path>
</svg>
</div>
{{ getLz("action.createNew") }}
</button>
<sidebar-playlist
v-for="item in getPlaylistFolderChildren('p.playlistsroot')"
:item="item"
>
</sidebar-playlist>
</template>
</div> </div>
<transition name="wpfade">
<div class="usermenu-container" v-if="chrome.menuOpened">
<div class="usermenu-body">
<button class="app-sidebar-button" style="width:100%" @click="appRoute('apple-account-settings')">
<img class="sidebar-user-icon" loading="lazy"
:src="getMediaItemArtwork(chrome.hideUserInfo ? 'http://localhost:9000/assets/logocut.png' : (chrome.userinfo.attributes['artwork'] ? chrome.userinfo.attributes['artwork']['url'] : ''), 26)" />
<div class="sidebar-user-text" v-if="!chrome.hideUserInfo">
<template v-if="chrome.userinfo.id || mk.isAuthorized">
<div class="fullname text-overflow-elipsis">{{ (chrome.userinfo != null &&
chrome.userinfo.attributes != null) ? (chrome.userinfo.attributes.name ?? "") :
""
}}
</div>
<div class="handle-text text-overflow-elipsis">{{
(chrome.userinfo != null && chrome.userinfo.attributes != null) ?
(chrome.userinfo.attributes.handle ?? "") : ""
}}
</div>
</template>
<template v-else>
<div @click="mk.authorize()">
{{$root.getLz('term.login')}}
</div>
</template>
</div>
<div class="sidebar-user-text" v-else>
{{$root.getLz('app.name')}}
</div>
</button>
<!-- Use 20px SVG for usermenu icon -->
<button class="usermenu-item" v-if="cfg.general.privateEnabled" @click="cfg.general.privateEnabled = false">
<span class="usermenu-item-icon">
<%- include("../svg/x.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.disablePrivateSession')}}</span>
</button>
<button class="usermenu-item" @click="appRoute('remote-pair')">
<span class="usermenu-item-icon">
<%- include("../svg/smartphone.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('action.showWebRemoteQR')}}</span>
</button>
<button class="usermenu-item" @click="cfg.advanced.AudioContext ? modals.castMenu = true : $root.notyf.error($root.getLz('settings.warn.enableAdvancedFunctionality'))">
<span class="usermenu-item-icon">
<%- include("../svg/cast.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.cast')}}</span>
</button>
<button class="usermenu-item" @click="cfg.advanced.AudioContext ? modals.audioSettings = true : $root.notyf.error($root.getLz('settings.warn.enableAdvancedFunctionality'))">
<span class="usermenu-item-icon">
<%- include("../svg/headphones.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.audioSettings')}}</span>
</button>
<button class="usermenu-item" v-if="pluginInstalled" @click="modals.pluginMenu = true">
<span class="usermenu-item-icon">
<%- include("../svg/grid.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.plugin')}}</span>
</button>
<button class="usermenu-item" @click="appRoute('about')">
<span class="usermenu-item-icon">
<%- include("../svg/info.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.about')}}</span>
</button>
<button class="usermenu-item" @click="appRoute('settings')">
<span class="usermenu-item-icon">
<%- include("../svg/settings.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.settings')}}</span>
</button>
<button class="usermenu-item" @click="unauthorize()">
<span class="usermenu-item-icon" style="right:2.5px;">
<%- include("../svg/log-out.svg") %>
</span>
<span class="usermenu-item-name">{{$root.getLz('term.logout')}}</span>
</button>
</div>
</div>
</transition>
<div class="app-sidebar-footer display--small app-sidebar-footer--controls"> <div class="app-sidebar-footer display--small app-sidebar-footer--controls">
<div
<div class="app-playback-controls " v-if="mkReady()" @contextmenu="nowPlayingContextMenu"> class="app-playback-controls"
<div class="control-buttons"> v-if="mkReady()"
<div class="app-chrome-item"> @contextmenu="nowPlayingContextMenu"
<button class="playback-button--small shuffle" v-if="mk.shuffleMode == 0" >
@click="mk.shuffleMode = 1" :title="$root.getLz('term.enableShuffle')" <div class="control-buttons">
v-b-tooltip.hover.righttop></button> <div class="app-chrome-item">
<button class="playback-button--small shuffle active" v-else <button
@click="mk.shuffleMode = 0" :title="$root.getLz('term.disableShuffle')" class="playback-button--small shuffle"
v-b-tooltip.hover.righttop></button> v-if="mk.shuffleMode == 0"
</div> @click="mk.shuffleMode = 1"
<div class="app-chrome-item"> :title="$root.getLz('term.enableShuffle')"
<button class="playback-button previous" @click="prevButton()" v-b-tooltip.hover.righttop
:title="$root.getLz('term.previous')" v-b-tooltip.hover></button> ></button>
</div> <button
<div class="app-chrome-item"> class="playback-button--small shuffle active"
<button class="playback-button pause" @click="mk.pause()" v-if="mk.isPlaying" v-else
:title="$root.getLz('term.pause')" v-b-tooltip.hover></button> @click="mk.shuffleMode = 0"
<button class="playback-button play" @click="mk.play()" v-else :title="$root.getLz('term.disableShuffle')"
:title="$root.getLz('term.play')" v-b-tooltip.hover></button> v-b-tooltip.hover.righttop
</div> ></button>
<div class="app-chrome-item"> </div>
<button class="playback-button next" @click="skipToNextItem()" <div class="app-chrome-item">
:title="$root.getLz('term.next')" v-b-tooltip.hover></button> <button
</div> class="playback-button previous"
<div class="app-chrome-item"> @click="prevButton()"
<button class="playback-button--small repeat" v-if="mk.repeatMode == 0" :title="$root.getLz('term.previous')"
@click="mk.repeatMode = 1" :title="$root.getLz('term.enableRepeatOne')" v-b-tooltip.hover
v-b-tooltip.hover></button> ></button>
<button class="playback-button--small repeat repeatOne" @click="mk.repeatMode = 2" </div>
v-else-if="mk.repeatMode == 1" :title="$root.getLz('term.disableRepeatOne')" <div class="app-chrome-item">
v-b-tooltip.hover></button> <button
<button class="playback-button--small repeat active" @click="mk.repeatMode = 0" class="playback-button pause"
v-else-if="mk.repeatMode == 2" :title="$root.getLz('term.disableRepeat')" @click="mk.pause()"
v-b-tooltip.hover></button> v-if="mk.isPlaying"
</div> :title="$root.getLz('term.pause')"
</div> v-b-tooltip.hover
<div class="app-chrome-item volume"> ></button>
<div class="input-container"> <button
<button class="volume-button--small volume" @click="muteButtonPressed()" class="playback-button play"
:class="{'active': this.cfg.audio.volume == 0}" @click="mk.play()"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')" v-else
v-b-tooltip.hover></button> :title="$root.getLz('term.play')"
<input type="range" class="" @wheel="volumeWheel" :step="cfg.audio.volumeStep" min="0" v-b-tooltip.hover
:max="cfg.audio.maxVolume" v-model="mk.volume" v-if="typeof mk.volume != 'undefined'" ></button>
@change="checkMuteChange()" v-b-tooltip.hover </div>
:title="formatVolumeTooltip()"> <div class="app-chrome-item">
</div> <button
</div> class="playback-button next"
@click="skipToNextItem()"
:title="$root.getLz('term.next')"
v-b-tooltip.hover
></button>
</div>
<div class="app-chrome-item">
<button
class="playback-button--small repeat"
v-if="mk.repeatMode == 0"
@click="mk.repeatMode = 1"
:title="$root.getLz('term.enableRepeatOne')"
v-b-tooltip.hover
></button>
<button
class="playback-button--small repeat repeatOne"
@click="mk.repeatMode = 2"
v-else-if="mk.repeatMode == 1"
:title="$root.getLz('term.disableRepeatOne')"
v-b-tooltip.hover
></button>
<button
class="playback-button--small repeat active"
@click="mk.repeatMode = 0"
v-else-if="mk.repeatMode == 2"
:title="$root.getLz('term.disableRepeat')"
v-b-tooltip.hover
></button>
</div>
</div> </div>
</div> <div class="app-chrome-item volume">
<div class="app-sidebar-notification backgroundNotification" v-if="library.backgroundNotification.show"> <div class="input-container">
<div class="message">{{ library.backgroundNotification.message }} ({{ <button
library.backgroundNotification.progress }} / {{ library.backgroundNotification.total }}) class="volume-button--small volume"
@click="muteButtonPressed()"
:class="{'active': this.cfg.audio.volume == 0}"
:title="cfg.audio.muted ? $root.getLz('term.unmute') : $root.getLz('term.mute')"
v-b-tooltip.hover
></button>
<input
type="range"
class=""
@wheel="volumeWheel"
:step="cfg.audio.volumeStep"
min="0"
:max="cfg.audio.maxVolume"
v-model="mk.volume"
v-if="typeof mk.volume != 'undefined'"
@change="checkMuteChange()"
v-b-tooltip.hover
:title="formatVolumeTooltip()"
/>
</div>
</div> </div>
</div>
</div> </div>
</div> <div
class="app-sidebar-notification backgroundNotification"
v-if="library.backgroundNotification.show"
>
<div class="message">
{{ library.backgroundNotification.message }} ({{
library.backgroundNotification.progress
}}
/ {{ library.backgroundNotification.total }})
</div>
</div>
</template>
</div>

View file

@ -35,6 +35,16 @@
v-model="maxVolume"/> v-model="maxVolume"/>
</div> </div>
</div> </div>
<div class="md-option-line">
<div class="md-option-segment">
{{$root.getLz('settings.option.audio.advanced')}}
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.audio.advanced" switch/>
</label>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -8,7 +8,7 @@
<div v-if="reasonShown" class="reasonSP ">{{item?.meta?.reason?.stringForDisplay ?? ''}}</div> <div v-if="reasonShown" class="reasonSP ">{{item?.meta?.reason?.stringForDisplay ?? ''}}</div>
<div style="{'--spcolor': getBgColor()}" <div style="{'--spcolor': getBgColor()}"
class="cd-mediaitem-square" :class="getClasses()" @contextmenu="getContextMenu"> class="cd-mediaitem-square" :class="getClasses()" @contextmenu="getContextMenu">
<template> <template v-if="isVisible">
<div class="artwork-container"> <div class="artwork-container">
<div class="unavailable-overlay" v-if="unavailable"> <div class="unavailable-overlay" v-if="unavailable">
<div class="codicon codicon-circle-slash"></div> <div class="codicon codicon-circle-slash"></div>

View file

@ -2,7 +2,8 @@
<div class="queue-panel"> <div class="queue-panel">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h3 class="queue-header-text">{{app.getLz('term.queue')}}</h3> <h3 class="queue-header-text" v-if="page == 'queue'">{{app.getLz('term.queue')}}</h3>
<h3 class="queue-header-text" v-if="page == 'history'">{{app.getLz('term.history')}}</h3>
</div> </div>
<div class="col-auto flex-center"> <div class="col-auto flex-center">
<button class="autoplay" :style="{'background': app.mk.autoplayEnabled ? 'var(--keyColor)' : ''}" <button class="autoplay" :style="{'background': app.mk.autoplayEnabled ? 'var(--keyColor)' : ''}"
@ -33,6 +34,12 @@
<div class="queue-title text-overflow-elipsis">{{ queueItem.item.attributes.name }}</div> <div class="queue-title text-overflow-elipsis">{{ queueItem.item.attributes.name }}</div>
<div class="queue-subtitle text-overflow-elipsis">{{ queueItem.item.attributes.artistName }} — {{ queueItem.item.attributes.albumName }}</div> <div class="queue-subtitle text-overflow-elipsis">{{ queueItem.item.attributes.artistName }} — {{ queueItem.item.attributes.albumName }}</div>
</div> </div>
<div class="queue-explicit-icon flex-center" v-if="queueItem.item.attributes.contentRating == 'explicit'">
<div class="explicit-icon"></div>
</div>
<div class="col queue-duration-info">
<div class="queue-duration flex-center">{{convertTimeToString(queueItem.item.attributes.durationInMillis)}}</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -167,6 +174,10 @@
this.selected = -1 this.selected = -1
app.mk.queue._queueItems = this.queueItems; app.mk.queue._queueItems = this.queueItems;
app.mk.queue._reindex() app.mk.queue._reindex()
},
convertTimeToString(timeInMilliseconds) {
var seconds = ((timeInMilliseconds % 60000) / 1000).toFixed(0);
return Math.floor(timeInMilliseconds/60000) + ":" + (seconds < 10 ? '0' : '') + seconds;
} }
} }
}); });

View file

@ -1,355 +0,0 @@
<script type="text/x-template" id="spatial-properties">
<div class="modal-fullscreen spatialproperties-panel" @click.self="close()" @contextmenu.self="close()">
<div class="modal-window" v-if="ready">
<div class="modal-header">
<div class="modal-title">{{$root.getLz('spatial.spatialProperties')}}</div>
<button class="close-btn" @click="close()" :aria-label="$root.getLz('action.close')"></button>
</div>
<div class="modal-content">
<template v-if="roomEditType == 'dimensions'">
<div class="row">
<div class="col"><h3>{{$root.getLz('spatial.roomDimensions')}}</h3></div>
<div class="col-auto flex-center">
<button class="md-btn" @click="roomEditType = 'positions'">{{$root.getLz('spatial.setPositions')}}</button>
</div>
</div>
<div class="row">
<div class="col">
<div class="row">
<div class="col-3 flex-center">
{{$root.getLz('spatial.width')}}
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="room_dimensions.width" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="room_dimensions.width" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
{{$root.getLz('spatial.height')}}
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="room_dimensions.height" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="room_dimensions.height" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
{{$root.getLz('spatial.depth')}}
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="room_dimensions.depth" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="room_dimensions.depth" step="1"/>
</div>
</div>
<label v-if="!app.cfg.audio.normalization">
{{$root.getLz('spatial.gain')}}
<input type="number" min="0" @change="setRoom()" style="width: 100%;"
v-model="app.cfg.audio.spatial_properties.gain" step="0.1"/>
</label>
</div>
<div class="col visual-container">
<div class="visual" :style="objectContainerStyle()">
<div class="face" :style="[faceStyle()]"></div>
<div class="face" :style="[faceStyle(), topFaceStyle()]"></div>
</div>
</div>
</div>
</template>
<template v-if="roomEditType == 'positions'">
<div class="row">
<div class="col"><h3>{{$root.getLz('spatial.roomPositions')}}</h3></div>
<div class="col-auto flex-center">
<button class="md-btn" @click="roomEditType = 'dimensions'">{{$root.getLz('spatial.setDimensions')}}</button>
</div>
</div>
<div class="row">
<div class="col">
<div class="row">
<div class="col-3 flex-center">
X ({{$root.getLz('spatial.listener')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="listener_position[0]" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="listener_position[0]" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
Y ({{$root.getLz('spatial.listener')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="listener_position[1]" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="listener_position[1]" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
Z ({{$root.getLz('spatial.listener')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="listener_position[2]" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="listener_position[2]" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
X ({{$root.getLz('spatial.audioSource')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="audio_position[0]" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="audio_position[0]" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
Y ({{$root.getLz('spatial.audioSource')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="audio_position[1]" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="audio_position[1]" step="1"/>
</div>
</div>
<div class="row">
<div class="col-3 flex-center">
Z ({{$root.getLz('spatial.audioSource')}})
</div>
<div class="col flex-center">
<input type="range" class="md-slider" min="0" max="100" @change="setRoom()" style="width: 100%;"
v-model="audio_position[2]" step="1"/>
</div>
<div class="col-3 flex-center">
<input type="number" min="0" @change="setRoom()" style="width: 100%;text-align: center"
v-model="audio_position[2]" step="1"/>
</div>
</div>
</div>
<div class="col visual-container">
<div class="visual">
<div class="face" :style="[faceStyle()]"></div>
<div class="face" :style="[faceStyle(), topFaceStyle()]"></div>
<!-- <div class="listener" :style="[listenerStyle()]">L</div> -->
<!-- <div class="audiosource" :style="[audioSourceStyle()]">A</div> -->
</div>
</div>
</div>
</template>
<div class="row">
<div class="col"><h3>{{$root.getLz('spatial.roomMaterials')}}</h3></div>
</div>
<div class="row">
<div class="col"></div>
<div class="col flex-center">
<label>
{{$root.getLz('spatial.up')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.up">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
</div>
<div class="col"></div>
</div>
<div class="row">
<div class="col flex-center">
<label>
{{$root.getLz('spatial.left')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.left">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
</div>
<div class="col flex-center">
<label>
{{$root.getLz('spatial.front')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.front">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
<label>
{{$root.getLz('spatial.back')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.back">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
</div>
<div class="col flex-center">
<label>
{{$root.getLz('spatial.right')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.right">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
</div>
</div>
<div class="row">
<div class="col"></div>
<div class="col flex-center">
<label>
{{$root.getLz('spatial.down')}}
<select class="md-select" @change="setRoom()"
v-model="room_materials.down">
<option v-for="prop in roomProps" :value="prop">{{ prop }}</option>
</select>
</label>
</div>
<div class="col"></div>
</div>
</div>
</div>
</div>
</script>
<script>
Vue.component('spatial-properties', {
template: '#spatial-properties',
data: function () {
return {
app: this.$root,
room_dimensions: null,
room_materials: null,
listener_position: null,
audio_position: null,
roomEditType: "dimensions",
roomProps: [
'transparent',
'acoustic-ceiling-tiles',
'brick-bare',
'brick-painted',
'concrete-block-coarse',
'concrete-block-painted',
'curtain-heavy',
'fiber-glass-insulation',
'glass-thin',
'glass-thick',
'grass',
'linoleum-on-concrete',
'marble',
'metal',
'parquet-on-concrete',
'plaster-smooth',
'plywood-panel',
'polished-concrete-or-tile',
'sheetrock',
'water-or-ice-surface',
'wood-ceiling',
'wood-panel',
'uniform'
],
visualMultiplier: 4,
ready: false
}
},
props: {},
mounted() {
this.room_dimensions = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.room_dimensions))
this.room_materials = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.room_materials))
this.audio_position = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.audio_position))
this.listener_position = JSON.parse(JSON.stringify(this.$root.cfg.audio.spatial_properties.listener_position))
if (typeof this.app.mk.nowPlayingItem != "undefined") {
this.setRoom()
}
this.ready = true
},
methods: {
listenerStyle() {
let style = {
transform: `rotateX(60deg) rotateZ(-45deg) translateX(${this.listener_position[0]}px) translateY(${this.listener_position[2]}px) translateZ(${100 + +this.listener_position[1]}px)`
}
return style
},
audioSourceStyle() {
let style = {
transform: `rotateX(60deg) rotateZ(-45deg) translateX(${this.audio_position[0]}px) translateY(${this.audio_position[2]}px) translateZ(${100 + +this.audio_position[1]}px)`
}
return style
},
topFaceStyle() {
let style = {
transform: `rotateX(60deg) rotateZ(-45deg) translateZ(${this.room_dimensions.height * this.visualMultiplier}px)`
}
return style
},
objectContainerStyle() {
let scale = 1
if (this.room_dimensions.width * this.visualMultiplier > 300) {
scale = 300 / (this.room_dimensions.width * this.visualMultiplier)
}
let style = {
transform: `scale(${scale})`
}
return style
},
faceStyle() {
let style = {
width: `${this.room_dimensions.width * this.visualMultiplier}px`,
height: `${this.room_dimensions.depth * this.visualMultiplier}px`,
}
return style
},
close() {
this.$root.cfg.audio.spatial_properties.room_dimensions = this.room_dimensions
this.$root.cfg.audio.spatial_properties.room_materials = this.room_materials
this.$root.cfg.audio.spatial_properties.audio_position = this.audio_position
this.$root.cfg.audio.spatial_properties.listener_position = this.listener_position
app.resetState()
},
setRoom() {
window.CiderAudio.audioNodes.spatialNode.setRoomProperties(this.room_dimensions, this.room_materials);
CiderAudio.audioNodes.spatialInput.setPosition(...this.audio_position)
CiderAudio.audioNodes.spatialNode.setListenerPosition(...this.listener_position)
if (!this.app.cfg.audio.normalization) {
window.CiderAudio.audioNodes.gainNode.gain.value = app.cfg.audio.spatial_properties.gain
}
}
}
});
</script>

View file

@ -58,6 +58,12 @@
#LOADER>svg { #LOADER>svg {
width: 128px; width: 128px;
} }
@media (prefers-color-scheme: light) {
#LOADER {
background-color: #eee;
}
}
</style> </style>
</head> </head>
@ -85,6 +91,11 @@
</mini-view> </mini-view>
</div> </div>
</transition> </transition>
<transition name="fsModeSwitch">
<div class="fullscreen-view-container oobe" v-if="appMode == 'oobe'">
<cider-oobe></cider-oobe>
</div>
</transition>
<%- include('app/panels'); %> <%- include('app/panels'); %>
<div class="cursor" v-if="chrome.showCursor"></div> <div class="cursor" v-if="chrome.showCursor"></div>
</div> </div>

View file

@ -198,14 +198,6 @@
return name; return name;
} }
}, },
toggleSpatial: function () {
if (app.cfg.audio.maikiwiAudio.spatial) {
CiderAudio.spatialOn()
CiderAudio.hierarchical_loading();
} else {
CiderAudio.spatialOff()
}
},
toggleMaikiwiSpatial: function () { toggleMaikiwiSpatial: function () {
if (app.cfg.audio.maikiwiAudio.spatial === true) { if (app.cfg.audio.maikiwiAudio.spatial === true) {
CiderAudio.spatialOn() CiderAudio.spatialOn()

View file

@ -54,9 +54,16 @@
class="content" class="content"
v-html="data.attributes.description?.short ?? data.attributes.editorialNotes?.short" v-html="data.attributes.description?.short ?? data.attributes.editorialNotes?.short"
@click="openInfoModal()"></div> @click="openInfoModal()"></div>
<div v-else-if="(data.attributes.description?.standard ?? data.attributes.editorialNotes?.standard) != null" <div v-else-if="((data.attributes.description?.standard ?? data.attributes.editorialNotes?.standard) != null) && (descriptionEditing == false)"
class="content" @mouseover="minClass(false)" @click="editPlaylistDescription()">{{data.attributes.description?.standard ?? data.attributes.editorialNotes?.standard}}</div>
v-html="data.attributes.description?.standard ?? data.attributes.editorialNotes?.standard"></div> <div v-else-if="((data.attributes.description?.standard ?? data.attributes.editorialNotes?.standard) != null) && (descriptionEditing)"
@mouseover="minClass(false)"><input type="text"
spellcheck="false"
class="descriptionEdit"
v-model="data.attributes.description.standard"
@blur="editPlaylist"
@change="editPlaylist"
@keydown.enter="editPlaylist"/></div>
<!-- <button v-if="(data.attributes.description?.short ?? data.attributes.editorialNotes?.short ) != null" class="more-btn" <!-- <button v-if="(data.attributes.description?.short ?? data.attributes.editorialNotes?.short ) != null" class="more-btn"
@click="editorialNotesExpanded = !editorialNotesExpanded"> @click="editorialNotesExpanded = !editorialNotesExpanded">
{{app.getLz('term.showMore')}} {{app.getLz('term.showMore')}}
@ -265,6 +272,7 @@
editorialNotesExpanded: false, editorialNotesExpanded: false,
drag: false, drag: false,
nameEditing: false, nameEditing: false,
descriptionEditing: false,
inLibrary: null, inLibrary: null,
confirm: false, confirm: false,
app: this.$root, app: this.$root,
@ -412,12 +420,24 @@
}, },
editPlaylist() { editPlaylist() {
this.app.editPlaylist(this.data.id, this.data.attributes.name); this.app.editPlaylist(this.data.id, this.data.attributes.name);
this.app.editPlaylistDescription(this.data.id, this.data.attributes.description.standard);
this.app.playlists.listing.forEach(playlist => { this.app.playlists.listing.forEach(playlist => {
if (playlist.id === this.data.id) { if (playlist.id === this.data.id) {
playlist.attributes.name = this.data.attributes.name playlist.attributes.name = this.data.attributes.name
playlist.attributes.description = this.data.attributes.description.standard
} }
}) })
this.nameEditing = false this.nameEditing = false
this.descriptionEditing = false
},
editPlaylistDescription() {
this.app.editPlaylistDescription(this.data.id, this.data.attributes.description.standard);
this.app.playlists.listing.forEach(playlist => {
if (playlist.id === this.data.id) {
playlist.attributes.description = this.data.attributes.description.standard
}
})
this.descriptionEditing = false
}, },
addToLibrary(id) { addToLibrary(id) {
app.mk.addToLibrary(id) app.mk.addToLibrary(id)
@ -450,6 +470,14 @@
}, 100) }, 100)
} }
}, },
editPlaylistDescription() {
if (this.data.attributes.canEdit && this.data.type === "library-playlists") {
this.descriptionEditing = true
setTimeout(() => {
document.querySelector(".descriptionEdit").focus()
}, 100)
}
},
buildContextMenu(index) { buildContextMenu(index) {
let self = this let self = this
if (!this.data.attributes.canEdit) { if (!this.data.attributes.canEdit) {

View file

@ -1,7 +1,7 @@
<script type="text/x-template" id="cider-collection-list"> <script type="text/x-template" id="cider-collection-list">
<div class="content-inner collection-page"> <div class="content-inner collection-page">
<h3 class="header-text" v-observe-visibility="{callback: headerVisibility}">{{ title }}</h3> <h3 class="header-text" v-observe-visibility="{callback: headerVisibility}">{{ title }}</h3>
<div v-if="data['data'] != 'null'" class="well itemContainer"> <div v-if="data['data'] != 'null'" class="well itemContainer" :class="getClasses()">
<template v-for="(item, key) in data.data"> <template v-for="(item, key) in data.data">
<template v-if="item.type == 'artists'"> <template v-if="item.type == 'artists'">
<mediaitem-square :item="item"></mediaitem-square> <mediaitem-square :item="item"></mediaitem-square>
@ -57,6 +57,13 @@
} }
}, },
methods: { methods: {
getClasses() {
if(this.commonKind != "song") {
return "collection-list-square";
}else{
return "";
}
},
getKind(item) { getKind(item) {
if (typeof item.kind != "undefined") { if (typeof item.kind != "undefined") {
this.commonKind = item.kind; this.commonKind = item.kind;

View file

@ -1,58 +1,70 @@
<template v-if="page == 'library-recentlyadded'"> <script type="text/x-template" id="cider-recentlyadded">
<div class="content-inner"> <div class="content-inner">
<div class="row"> <h1 class="header-text">{{$root.getLz('term.recentlyAdded')}}</h1>
<div class="col" style="padding:0;"> <div class="well itemContainer" v-if="itemSize == 'normal'">
<h1 class="header-text">{{$root.getLz('term.recentlyAdded')}}</h1> <mediaitem-square v-for="item in items" :item="item"></mediaitem-square>
</div>
<div class="col-auto">
<button v-if="library.albums.downloadState == 2" @click="getLibraryAlbumsFull(true, 0)"
class="reload-btn" :aria-label="app.getLz('menubar.options.reload')"><%- include('../svg/redo.svg') %></button>
</div>
</div> </div>
<div class="row"> <div class="well itemContainer" v-else="itemSize == 'compact'">
<div class="col" style="padding:0;"> <mediaitem-list-item :show-meta-data="true" :show-library-status="false" v-for="item in items" :item="item"></mediaitem-list-item>
<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="$root.getLz('term.search') + '...'"
@input="searchLibraryAlbums"
v-model="library.albums.search" class="search-input">
</div>
</div>
<div class="col-auto flex-center">
<div class="row">
<div class="col">
<select class="md-select" v-model="library.albums.sortOrder[0]"
@change="searchLibraryAlbums(0)">
<optgroup :label="$root.getLz('term.sortOrder')">
<option value="asc">{{$root.getLz('term.sortOrder.ascending')}}</option>
<option value="desc">{{$root.getLz('term.sortOrder.descending')}}</option>
</optgroup>
</select>
</div>
<div class="col">
<select class="md-select" v-model="library.albums.viewAs">
<optgroup :label="$root.getLz('term.viewAs')">
<option value="covers">{{$root.getLz('term.viewAs.coverArt')}}</option>
<option value="list">{{$root.getLz('term.viewAs.list')}}</option>
</optgroup>
</select>
</div>
</div>
</div>
</div> </div>
<div class="well"> <div class="well itemContainer" v-show="loading">
<div class="albums-square-container"> <div class="spinner"></div>
<mediaitem-square v-if="library.albums.viewAs == 'covers'" :item="item"
v-for="item in library.albums.displayListing">
</mediaitem-square>
</div>
<mediaitem-list-item v-if="library.albums.viewAs == 'list'" :show-duration="false" :show-meta-data="true"
:show-library-status="false" :item="item"
v-for="item in library.albums.displayListing">
</mediaitem-list-item>
</div> </div>
<button v-if="nextUrl && !loading" style="opacity:0;height: 32px;" v-observe-visibility="{callback: visibilityChanged}">{{$root.getLz('term.showMore')}}
</button>
</div> </div>
</template> </script>
<script>
Vue.component("cider-recentlyadded", {
template: "#cider-recentlyadded",
computed: {
items() {
return this.$store.state.pageState['recentlyAdded'].items;
},
nextUrl() {
return this.$store.state.pageState['recentlyAdded'].nextUrl;
},
itemSize() {
return this.$store.state.pageState['recentlyAdded'].size
}
},
data: function () {
return {
loading: false,
firstRoute: `/v1/me/library/recently-added?l=${app.mklang}&platform=web&include[library-albums]=artists&include[library-artists]=catalog&fields[artists]=url&fields%5Balbums%5D=artistName%2CartistUrl%2Cartwork%2CcontentRating%2CeditorialArtwork%2Cname%2CplayParams%2CreleaseDate%2Curl&includeOnly=catalog%2Cartists&limit=25`
}
},
async mounted() {
if(this.$store.state.pageState['recentlyAdded'].items.length !== 0) return
const firstResult = await app.mk.api.v3.music(this.firstRoute)
this.$store.state.pageState["recentlyAdded"].items = firstResult.data.data
this.$store.state.pageState["recentlyAdded"].nextUrl = firstResult.data.next
},
beforeDestroy() {
// this.$store.state.pageState["recently-added"].scrollPosY = $("#app-content").scrollTop()
},
methods: {
visibilityChanged: function(isVisible, entry) {
if (isVisible && !this.loading) {
this.getNextData();
}
},
async getNextData() {
if (this.$store.state.pageState["recentlyAdded"].nextUrl) {
this.loading = true;
const nextResult = await app.mk.api.v3.music(this.$store.state.pageState["recentlyAdded"].nextUrl)
this.$store.state.pageState["recentlyAdded"].items = this.$store.state.pageState["recentlyAdded"].items.concat(nextResult.data.data)
if (nextResult.data.next) {
this.$store.state.pageState["recentlyAdded"].nextUrl = nextResult.data.next
} else {
this.$store.state.pageState["recentlyAdded"].nextUrl = null
}
this.loading = false;
}
return
}
}
});
</script>

View file

@ -0,0 +1,143 @@
<script type="text/x-template" id="cider-oobe">
<div class="content-inner oobe">
<!-- before_we_start-->
<transition name="wpfade">
<div class="oobe-view" v-if="screen == 'before_we_start'">
<div class="oobe-header">
{{ getLz("oobe.amupsell.title") }}
</div>
<div class="oobe-body text">{{ getLz("oobe.amupsell.text") }}</div>
<div class="oobe-footer">
<div class="btn-group">
<div class="md-btn" @click="screen = 'welcome'">{{ getLz("oobe.next") }}</div>
</div>
</div>
</div>
</transition>
<!-- Welcome -->
<transition name="wpfade">
<div class="oobe-view" v-if="screen == 'welcome'">
<div class="oobe-header">
{{ getLz("oobe.intro.title") }}
</div>
<div class="oobe-body text">{{ getLz("oobe.intro.text") }}</div>
<div class="oobe-footer">
<div class="btn-group">
<div class="md-btn" @click="screen = 'before_we_start'">{{ getLz("oobe.previous") }}</div>
<div class="md-btn" @click="screen = 'general'">{{ getLz("oobe.next") }}</div>
</div>
</div>
</div>
</transition>
<!-- General -->
<transition name="wpfade">
<div class="oobe-view" v-if="screen == 'general'">
<div class="oobe-header">
{{ getLz("oobe.general.title") }}
</div>
<div class="oobe-body text">{{ getLz("oobe.general.text") }}</div>
<div class="oobe-footer">
<div class="btn-group">
<div class="md-btn" @click="screen = 'welcome'">{{ getLz("oobe.previous") }}</div>
<div class="md-btn" @click="screen = 'visual'">{{ getLz("oobe.next") }}</div>
</div>
</div>
</div>
</transition>
<!-- Visual -->
<transition name="wpfade">
<div class="oobe-view" v-if="screen == 'visual'">
<div class="oobe-header">
{{ getLz("oobe.visual.title") }}
</div>
<div class="oobe-body visual">
<b-row>
<b-col>
<div class="card bg-dark text-white stylePicker">
<div class="card-body">
<img class="visualPreview" src="./assets/oobe/ss1.png" alt="TEMP">
</div>
<div class="card-footer">
Mojave
</div>
</div>
</b-col>
<b-col>
<div class="card bg-dark text-white stylePicker">
<div class="card-body">
<img class="visualPreview" src="./assets/oobe/ss2.png" alt="TEMP">
</div>
<div class="card-footer">
Maverick
</div>
</div>
</b-col>
</b-row>
<div class="blurb">{{getLz("oobe.visual.layout.text")}}</div>
</div>
<div class="oobe-footer">
<div class="btn-group">
<div class="md-btn" @click="screen = 'general'">{{ getLz("oobe.previous") }}</div>
<div class="md-btn" @click="screen = 'audio'">{{ getLz("oobe.next") }}</div>
</div>
</div>
</div>
</transition>
<!-- Audio -->
<transition name="wpfade">
<div class="oobe-view" v-if="screen == 'audio'">
<div class="oobe-header">
{{ getLz("oobe.audio.title") }}
</div>
<div class="oobe-body">
<div class="blurb">{{ getLz("oobe.audio.text") }}</div>
<div class="md-option-container">
<div class="settings-option-body">
<div class="md-option-line">
<div class="md-option-segment">
{{getLz('settings.option.audio.enableAdvancedFunctionality')}}
<br>
<small>{{getLz('settings.option.audio.enableAdvancedFunctionality.description')}}</small>
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="$root.cfg.advanced.AudioContext"
switch/>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="oobe-footer">
<div class="btn-group">
<div class="md-btn" @click="screen = 'visual'">{{ getLz("oobe.previous") }}</div>
<div class="md-btn">{{ getLz("oobe.next") }}</div>
</div>
</div>
</div>
</transition>
</div>
</script>
<script>
Vue.component('cider-oobe', {
template: '#cider-oobe',
data: function () {
return {
screen: "before_we_start"
}
},
async mounted() {
},
methods: {
getLz() {
return this.$root.getLz.apply(this.$root, arguments);
}
}
});
</script>

View file

@ -1,82 +1,105 @@
<script type="text/x-template" id="cider-search"> <script type="text/x-template" id="cider-search">
<div class="content-inner search-page"> <div class="content-inner search-page">
<div class="btn-group searchToggle">
<button
@click="searchType = 'catalog'"
class="md-btn md-btn-small" :class="{'md-btn-primary': searchType == 'catalog'}">{{ $root.getLz("term.appleMusic") }}</button>
<button
@click="searchType = 'library';"
class="md-btn md-btn-small" :class="{'md-btn-primary': searchType == 'library'}">{{ $root.getLz("term.library") }}</button>
</div>
<div v-if="search != null && search != [] && search.term != ''"> <div v-if="search != null && search != [] && search.term != ''">
<h3>{{app.getLz('term.topResult')}}</h3> <template v-if="searchType == 'catalog'">
<mediaitem-scroller-horizontal <h3>{{app.getLz('term.topResult')}}</h3>
:items="search.results[search.results.meta.results.order[0]]['data']"></mediaitem-scroller-horizontal> <mediaitem-scroller-horizontal
<div class="row"> :items="search.results[search.results.meta.results.order[0]]['data']"></mediaitem-scroller-horizontal>
<div v-else style="text-align: center"> <div class="row">
<h3>{{app.getLz('error.noResults')}}</h3> <div v-else style="text-align: center">
<p>{{app.getLz('error.noResults.description')}}</p> <h3>{{app.getLz('error.noResults')}}</h3>
</div> <p>{{app.getLz('error.noResults.description')}}</p>
<div class="col" v-if="search.results.song"> </div>
<div class="row"> <div class="col" v-if="search.results.song">
<div class="col"> <div class="row">
<h3>{{app.getLz('term.songs')}}</h3> <div class="col">
<h3>{{app.getLz('term.songs')}}</h3>
</div>
<div class="col-auto flex-center"
@click="app.showSearchView(app.search.term, 'song', app.friendlyTypes('song'))"
v-if="search.results.song.data.length >= 12">
<button class="cd-btn-seeall">{{app.getLz('term.seeAll')}}</button>
</div>
</div> </div>
<div class="col-auto flex-center" <div class="mediaitem-list-item__grid">
@click="app.showSearchView(app.search.term, 'song', app.friendlyTypes('song'))" <listitem-horizontal :items="search.results.song.data.limit(12)">
v-if="search.results.song.data.length >= 12"> </listitem-horizontal>
<button class="cd-btn-seeall">{{app.getLz('term.seeAll')}}</button>
</div> </div>
</div> </div>
<div class="mediaitem-list-item__grid">
<listitem-horizontal :items="search.results.song.data.limit(12)">
</listitem-horizontal>
</div>
</div> </div>
</div>
<template v-if="search.results['meta'] != null"> <template v-if="search.results['meta'] != null">
<template <template
v-for="section in search.results.meta.results.order" v-if="section != 'song' && section != 'top'"> v-for="section in search.results.meta.results.order" v-if="section != 'song' && section != 'top'">
<div class="row">
<div class="col">
<h3>{{ app.friendlyTypes(section) }}</h3>
</div>
<div class="col-auto flex-center" v-if="search.results[section].data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showSearchView(app.search.term, section, app.friendlyTypes(section))">{{app.getLz('term.seeAll')}}
</button>
</div>
</div>
<template v-if="!app.friendlyTypes(section).includes('Video')">
<mediaitem-scroller-horizontal-large
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-else>
<mediaitem-scroller-horizontal-mvview
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-mvview>
</template>
</template>
</template>
<template v-if="search.resultsSocial.playlist">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h3>{{ app.friendlyTypes(section) }}</h3> <h3>{{app.getLz('term.sharedPlaylists')}}</h3>
</div> </div>
<div class="col-auto flex-center" v-if="search.results[section].data.length >= 10"> <div class="col-auto flex-center" v-if="search.resultsSocial.playlist.data.length >= 10">
<button class="cd-btn-seeall" <button class="cd-btn-seeall"
@click="app.showSearchView(app.search.term, section, app.friendlyTypes(section))">{{app.getLz('term.seeAll')}} @click="app.showCollection(search.resultsSocial.playlist, 'Shared Playlists', 'default')">{{app.getLz('term.seeAll')}}
</button> </button>
</div> </div>
</div> </div>
<template v-if="!app.friendlyTypes(section).includes('Video')"> <mediaitem-scroller-horizontal-large
<mediaitem-scroller-horizontal-large :items="search.resultsSocial.playlist.data.limit(10)"></mediaitem-scroller-horizontal-large>
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-large> </template>
</template> <template v-if="search.resultsSocial.profile">
<template v-else> <div class="row">
<mediaitem-scroller-horizontal-mvview <div class="col">
:items="search.results[section].data.limit(10)"></mediaitem-scroller-horizontal-mvview> <h3>{{app.getLz('term.people')}}</h3>
</template> </div>
<div class="col-auto flex-center" v-if="search.resultsSocial.profile.data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showCollection(search.resultsSocial.profile, 'People', 'default')">{{app.getLz('term.seeAll')}}
</button>
</div>
</div>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.profile.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template> </template>
</template> </template>
<template v-if="search.resultsSocial.playlist"> <template v-else>
<div class="row"> <h1>{{ $root.getLz("term.library") }}</h1>
<div class="col"> <div v-for="(section, key) in $root.search.resultsLibrary">
<h3>{{app.getLz('term.sharedPlaylists')}}</h3> <h3>{{app.friendlyTypes(key)}}</h3>
<div class="mediaitem-list-item__grid" v-if="key.includes('songs')">
<listitem-horizontal :items="section.data"></listitem-horizontal>
</div> </div>
<div class="col-auto flex-center" v-if="search.resultsSocial.playlist.data.length >= 10"> <div class="well" v-else>
<button class="cd-btn-seeall" <mediaitem-scroller-horizontal-large
@click="app.showCollection(search.resultsSocial.playlist, 'Shared Playlists', 'default')">{{app.getLz('term.seeAll')}} :items="section.data"></mediaitem-scroller-horizontal-large>
</button>
</div> </div>
</div> </div>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.playlist.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template>
<template v-if="search.resultsSocial.profile">
<div class="row">
<div class="col">
<h3>{{app.getLz('term.people')}}</h3>
</div>
<div class="col-auto flex-center" v-if="search.resultsSocial.profile.data.length >= 10">
<button class="cd-btn-seeall"
@click="app.showCollection(search.resultsSocial.profile, 'People', 'default')">{{app.getLz('term.seeAll')}}
</button>
</div>
</div>
<mediaitem-scroller-horizontal-large
:items="search.resultsSocial.profile.data.limit(10)"></mediaitem-scroller-horizontal-large>
</template> </template>
</div> </div>
<div v-else> <div v-else>
@ -111,6 +134,7 @@
recentlyPlayed: [], recentlyPlayed: [],
categoriesView: [], categoriesView: [],
categoriesReady: false, categoriesReady: false,
searchType: "catalog",
} }
}, },
methods: { methods: {

View file

@ -329,7 +329,7 @@
<label> <label>
<input type="checkbox" v-model="app.cfg.audio.normalization" <input type="checkbox" v-model="app.cfg.audio.normalization"
v-on:change="toggleNormalization" v-on:change="toggleNormalization"
:disabled="app.cfg.audio.spatial === true || app.cfg.audio.maikiwiAudio.spatial === true || app.cfg.audio.maikiwiAudio.ciderPPE === true || app.cfg.audio.maikiwiAudio.atmosphereRealizer1 === true || app.cfg.audio.maikiwiAudio.atmosphereRealizer2 === true" :disabled="app.cfg.audio.maikiwiAudio.spatial === true || app.cfg.audio.maikiwiAudio.ciderPPE === true || app.cfg.audio.maikiwiAudio.atmosphereRealizer1 === true || app.cfg.audio.maikiwiAudio.atmosphereRealizer2 === true"
switch/> switch/>
</label> </label>
</div> </div>
@ -385,8 +385,8 @@
<div class="md-option-segment md-option-segment_auto"> <div class="md-option-segment md-option-segment_auto">
<label> <label>
<select class="md-select" v-model="$root.cfg.visual.directives.windowLayout"> <select class="md-select" v-model="$root.cfg.visual.directives.windowLayout">
<option value="default">Cupertino</option> <option value="default">Maverick</option>
<option value="twopanel">Redmond</option> <option value="twopanel">Mojave</option>
</select> </select>
</label> </label>
</div> </div>
@ -1138,6 +1138,18 @@
</div> </div>
</div> </div>
<div class="md-option-line">
<div class="md-option-segment">
Collapsable Sidebar
</div>
<div class="md-option-segment md-option-segment_auto">
<label>
<input type="checkbox" v-model="app.cfg.advanced.experiments.includes('collapseSidebar')"
@click="app.cfg.advanced.experiments.includes('collapseSidebar') ? removeExperiment('collapseSidebar') : addExperiment('collapseSidebar')"
switch/>
</label>
</div>
</div>
<div class="md-option-line"> <div class="md-option-line">
<div class="md-option-segment"> <div class="md-option-segment">
@ -1381,7 +1393,7 @@
if (app.cfg.audio.normalization === true) { if (app.cfg.audio.normalization === true) {
CiderAudio.normalizerOn() CiderAudio.normalizerOn()
} }
if (app.cfg.audio.spatial === true) { if (app.cfg.audio.maikiwiAudio.spatial === true) {
CiderAudio.spatialOn() CiderAudio.spatialOn()
CiderAudio.hierarchical_loading(); CiderAudio.hierarchical_loading();
} }
@ -1392,7 +1404,7 @@
if (app.cfg.audio.normalization === true) { if (app.cfg.audio.normalization === true) {
CiderAudio.normalizerOn() CiderAudio.normalizerOn()
} }
if (app.cfg.audio.spatial === true) { if (app.cfg.audio.maikiwiAudio.spatial === true) {
CiderAudio.spatialOn() CiderAudio.spatialOn()
CiderAudio.hierarchical_loading(); CiderAudio.hierarchical_loading();
} }

View file

@ -9,6 +9,7 @@
"sourceMap": true, "sourceMap": true,
"outDir": "./build", "outDir": "./build",
"baseUrl": ".", "baseUrl": ".",
"resolveJsonModule": true,
"paths": { "paths": {
"*": ["node_modules/*"] "*": ["node_modules/*"]
}, },

View file

@ -1,7 +1,7 @@
{ {
"electronVersion": "18.3.0", "electronVersion": "19.0.1",
"electronDownload": { "electronDownload": {
"version": "18.3.0+wvcus", "version": "19.0.1+wvcus",
"mirror": "https://github.com/castlabs/electron-releases/releases/download/v" "mirror": "https://github.com/castlabs/electron-releases/releases/download/v"
}, },
"appId": "cider", "appId": "cider",