diff --git a/src/i18n/el_GR.jsonc b/src/i18n/el_GR.jsonc
index 84e1762d..c277c8aa 100644
--- a/src/i18n/el_GR.jsonc
+++ b/src/i18n/el_GR.jsonc
@@ -1,6 +1,9 @@
{
// App info
"app.name": "Cider",
+
+ "date.format": "${d} ${m}, ${y}",
+
// Dialogs
"dialog.cancel": "Ακύρωση",
"dialog.ok": "ΟΚ",
@@ -9,8 +12,8 @@
"notification.updatingLibrarySongs": "Ενημέρωση βιβλιοθήκης τραγουδιών...",
"notification.updatingLibraryAlbums": "Ενημέρωση βιβλιοθήκης άλμπουμ...",
"notification.updatingLibraryArtists": "Ενημέρωση βιβλιοθήκης καλλιτεχνών...",
-
// Terms
+ "term.appleInc": "Apple Inc.",
"term.appleMusic": "Apple Music",
"term.applePodcasts": "Apple Podcasts",
"term.itunes": "iTunes",
@@ -60,6 +63,7 @@
"term.viewAs": "Προβολή Ως",
"term.viewAs.coverArt": "Εξώφυλλο",
"term.viewAs.list": "Λίστα",
+ "term.size": "Μέγεθος",
"term.size.normal": "Κανονικό",
"term.size.compact": "Συμπαγή",
"term.enable": "Ενεργοποίηση",
@@ -67,17 +71,58 @@
"term.enabled": "Ενεργοποιημένο",
"term.disabled": "Απενεργοποιημένο",
"term.connect": "Σύνδεση",
+ "term.connecting": "Γίνεται Σύνδεση",
+ "term.disconnect": "Αποσύνδεση",
+ "term.authed": "Επικυρωμένο",
+ "term.confirm": "Σίγουρα;",
+ "term.more": "Περισσότερα",
+ "term.less": "Λιγότερα",
+ "term.showMore": "Εμφάνιση περισσότερων",
+ "term.showLess": "Εμφάνιση λιγότερων",
+ "term.topSongs" : "Κορυφαία Τραγούδια",
+ "term.latestReleases": "Τελευταίες Κυκλοφορίες",
+ "term.time.added": "Προστέθηκε",
+ "term.time.released": "Κυκλοφόρησε",
+ "term.time.updated": "Ενημερώθηκε",
+ "term.fullscreenView": "Πλήρης οθόνη",
+ "term.defaultView": "Κανονική οθόνη",
+ "term.spacializedAudioSetting": "Χωρική Ρύθμιση Ήχου",
+ "term.clearAll": "Εκκαθάριση Όλων",
+ "term.recentStations": "Πρόσφατοι Σταθμοί",
+ "term.language": "Γλώσσα",
+ "term.noLyrics": "Φόρτωση... / Δεν βρέθηκαν στίχοι./ Ορχηστικό.",
+ "term.copyright": "Copyright",
+ "term.rightsReserved": "Όλα τα δικαιώματα διατηρούνται.",
+ "term.sponsor": "Χορήγησε αυτό το έργο",
+ "term.ciderTeam": "Ομάδα Cider",
+ "term.developer": "Προγραμματιστής",
+ "term.socialTeam": "Κοινωνική Ομάδα",
+ "term.contributors": "Συνεισφέροντες",
// Home
"home.title": "Αρχική",
"home.recentlyPlayed": "Έπαιξαν Πρόσφατα",
"home.recentlyAdded": "Πρόσφατες Προσθήκες",
"home.artistsFeed": "Ροή των Καλλιτεχνών σου",
+ "home.artistsFeed.noArtist": "Ακολούθησε μερικούς καλλιτέχνες πρώτα και οι τελευταίες κυκλοφορίες τους θα εμφανίζονται εδώ",
"home.madeForYou": "Δημιουργήθηκε Για Εσάς",
"home.friendsListeningTo": "Οι Φίλοι σου Ακούν",
"home.followedArtists": "Καλλιτέχνες που Ακολουθείτε",
// Errors
"error.appleMusicSubRequired": "Το Apple Music απαιτεί μια συνδρομή.",
+ "error.connectionError": "Δεν είναι δυνατή η σύνδεση με το Apple Music.",
+ "error.noResults": "Κανένα αποτέλεσμα.",
+ "error.noResults.description": "Δοκιμάστε μια νέα αναζήτηση.",
+
+ //Podcasts
+ "podcast.followOnCider": "Ακολούθηση στο Cider",
+ "podcast.followedOnCider": "Ακολουθείτε στο Cider",
+ "podcast.subscribeOnItunes": "Συνδρομή στο iTunes",
+ "podcast.subscribedOnItunes": "Συνδρομητής στο iTunes",
+ "podcast.itunesStore": "iTunes Store",
+ "podcast.episodes": "Επεισόδια",
+ "podcast.playEpisode": "Αναπαραγωγή Επεισοδίου",
+ "podcast.website": "Ιστότοπος Podcast",
// Actions
"action.addToLibrary": "Προσθήκη στη Βιβλιοθήκη",
@@ -92,6 +137,7 @@
"action.removeFromQueue.success": "Αφαιρέθηκε από την Ουρά",
"action.removeFromQueue.error": "Σφάλμα Αφαίρεσης από την Ουρά",
"action.addToPlaylist": "Προσθήκη σε Λίστα",
+ "action.removeFromPlaylist": "Αφαίρεση από Λίστα",
"action.addToFavorites": "Προσθήκη στα Αγαπημένα",
"action.follow": "Ακολούθηση",
"action.follow.success": "Ακολουθήθηκε",
@@ -112,6 +158,7 @@
"action.dislike": "Δεν μου αρέσει",
"action.undoDislike": "Αναίρεση \"Δεν μου αρέσει\"",
"action.showWebRemoteQR": "Εμφάνιση Web Remote QR",
+
// Settings - Audio
"settings.header.audio": "Ήχος",
"settings.header.audio.description": "Προσαρμογή ρυθμίσεων ήχου για το Cider.",
@@ -140,9 +187,11 @@
"settings.header.visual.animatedArtworkQuality.low": "Χαμηλή",
"settings.header.visual.animatedArtworkQuality.medium": "Μέτρια",
"settings.header.visual.animatedArtworkQuality.high": "Υψηλή",
- "settings.header.visual.animatedArtworkQuality.extreme": "Πολύ Υψηλή",
+ "settings.header.visual.animatedArtworkQuality.veryHigh": "Πολύ Υψηλή",
+ "settings.header.visual.animatedArtworkQuality.extreme": "Ακραία",
"settings.option.visual.animatedWindowBackground": "Κινούμενο Φόντο Παραθύρου", // Toggle
"settings.option.visual.hardwareAcceleration": "Επιτάχυνση Υλικού", // Dropdown
+ "settings.option.visual.hardwareAcceleration.description": "Απαιτεί επανεκκίνηση",
"settings.header.visual.hardwareAcceleration.default": "Προεπιλογή",
"settings.header.visual.hardwareAcceleration.webGPU": "WebGPU",
// Refer to term.disabled for the disabled option
@@ -168,6 +217,9 @@
"settings.header.connectivity.discordRPC.appleMusic": "Εμφάνιση ως 'Apple Music'",
"settings.option.connectivity.discordRPC.clearOnPause": "Εκκαθάριση του Discord Rich Presence στην Παύση", // Toggle
"settings.option.connectivity.lastfmScrobble": "LastFM Scrobbling", // Option to Connect
+ "settings.option.connectivity.lastfmScrobble.delay": "Καθυστέρηση LastFM Scrobble (%)",
+ "settings.option.connectivity.lastfmScrobble.nowPlaying": "Ενεργοποίηση LastFM \"Now Playing\"",
+ "settings.option.connectivity.lastfmScrobble.removeFeatured": "Αφαίρεση καλλιτεχνών feature από τον τίτλο του τραγουδιού (LastFM)",
// Refer to term.connect for the connect button
// Settings - Experimental
@@ -176,7 +228,32 @@
"settings.option.experimental.compactUI": "Συμπαγής Διεπαφή", // Toggle
// Refer to term.disabled & term.enabled
+ // Spatialization Menu
+ "spatial.spatialProperties" : "Χωρικές Ιδιότητες",
+ "spatial.width" : "Πλάτος",
+ "spatial.height" : "Ύψος",
+ "spatial.depth" : "Βάθος",
+ "spatial.roomMaterials" : "Υλικά Δωματίου",
+ "spatial.roomDimensions" : "Διαστάσεις Δωματίου",
+ "spatial.roomPositions" : "Θέσεις Δωματίου",
+ "spatial.setDimensions" : "Ορισμός Διαστάσεων",
+ "spatial.setPositions" : "Ορισμός Θέσεων",
+ "spatial.up" : "Πάνω",
+ "spatial.front" : "Πρόσοψη",
+ "spatial.left" : "Αριστερά",
+ "spatial.right" : "Δεξιά",
+ "spatial.back" : "Πίσω Όψη",
+ "spatial.down" : "Κάτω",
+ "spatial.listener" : "Ακροατής",
+ "spatial.audioSource" : "Πηγή Ήχου",
+
+ // Settings - Unfinished
+ "settings.header.unfinished": "Ημιτελής",
+
// Web Remote
"remote.web.title": "Cider Remote",
- "remote.web.description": "Σαρώστε τον κωδικό QR για σύζευξη του Cider με το κινητό σας"
-}
+ "remote.web.description": "Σαρώστε τον κωδικό QR για σύζευξη του Cider με το κινητό σας",
+
+ //About
+ "about.thanks": "Μεγάλα ευχαριστώ στην Ομάδα Cider Collective και σε όλους τους συνεισφέροντές μας."
+}
\ No newline at end of file
diff --git a/src/i18n/en_HODOR.jsonc b/src/i18n/en_HODOR.jsonc
new file mode 100644
index 00000000..6796ca9e
--- /dev/null
+++ b/src/i18n/en_HODOR.jsonc
@@ -0,0 +1,261 @@
+{ // Base File
+ // App info
+ "app.name": "HODOR",
+
+ "date.format": "${m} ${d}, ${y}",
+
+ // Dialogs
+ "dialog.cancel": "HODOR",
+ "dialog.ok": "HODOR",
+
+ // Notification
+ "notification.updatingLibrarySongs": "HODOR HODOR HODOR...",
+ "notification.updatingLibraryAlbums": "HODOR HODOR HODOR...",
+ "notification.updatingLibraryArtists": "HODOR HODOR HODOR...",
+ // Terms
+ "term.appleInc": "HODOR Inc.",
+ "term.appleMusic": "HODOR HODOR",
+ "term.applePodcasts": "HODOR HODOR",
+ "term.itunes": "HODOR",
+ "term.github": "HODOR",
+ "term.discord": "HODOR",
+ "term.learnMore": "HODOR HODOR",
+ "term.accountSettings": "HODOR HODOR",
+ "term.logout": "HODOR",
+ "term.login": "HODOR",
+ "term.about": "HODOR",
+ "term.privateSession": "HODOR HODOR",
+ "term.queue": "HODOR",
+ "term.search": "HODOR",
+ "term.library": "HODOR",
+ "term.listenNow": "HODOR HODOR",
+ "term.browse": "HODOR",
+ "term.radio": "HODOR",
+ "term.recentlyAdded": "HODOR HODOR",
+ "term.songs": "HODOR",
+ "term.albums": "HODOR",
+ "term.artists": "HODOR",
+ "term.podcasts": "HODOR",
+ "term.playlists": "HODOR",
+ "term.playlist": "HODOR",
+ "term.play": "HODOR",
+ "term.pause": "HODOR",
+ "term.previous": "HODOR",
+ "term.next": "HODOR",
+ "term.shuffle": "HODOR",
+ "term.repeat": "HODOR",
+ "term.volume": "HODOR",
+ "term.mute": "HODOR",
+ "term.unmute": "HODOR",
+ "term.share": "HODOR",
+ "term.settings": "HODOR",
+ "term.seeAll": "HODOR HODOR",
+ "term.sortBy": "HODOR HODOR",
+ "term.sortBy.album": "HODOR",
+ "term.sortBy.artist": "HODOR",
+ "term.sortBy.name": "HODOR",
+ "term.sortBy.genre": "HODOR",
+ "term.sortBy.releaseDate": "HODOR HODOR",
+ "term.sortBy.duration": "HODOR",
+ "term.sortOrder": "HODOR-HODOR",
+ "term.sortOrder.ascending": "HODOR",
+ "term.sortOrder.descending": "HODOR",
+ "term.viewAs": "HODOR HODOR",
+ "term.viewAs.coverArt": "HODOR HODOR",
+ "term.viewAs.list": "HODOR",
+ "term.size": "HODOR",
+ "term.size.normal": "HODOR",
+ "term.size.compact": "HODOR",
+ "term.enable": "HODOR",
+ "term.disable": "HODOR",
+ "term.enabled": "HODOR",
+ "term.disabled": "HODOR",
+ "term.connect": "HODOR",
+ "term.connecting": "HODOR",
+ "term.disconnect": "HODOR",
+ "term.authed": "HODOR",
+ "term.confirm": "HODOR ?",
+ "term.more": "HODOR",
+ "term.less": "HODOR",
+ "term.showMore": "HODOR HODOR",
+ "term.showLess": "HODOR HODOR",
+ "term.topSongs" : "HODOR HODOR",
+ "term.latestReleases": "HODOR HODOR",
+ "term.time.added": "HODOR",
+ "term.time.released": "HODOR",
+ "term.time.updated": "HODOR",
+ "term.fullscreenView": "HODOR HODOR",
+ "term.defaultView": "HODOR HODOR",
+ "term.spacializedAudioSetting": "HODOR HODOR HODOR",
+ "term.clearAll": "HODOR HODOR",
+ "term.recentStations": "HODOR HODOR",
+ "term.language": "HODOR",
+ "term.noLyrics": "HODOR... / HODOR HODOR HODOR./ HODOR.",
+ "term.copyright": "HODOR",
+ "term.rightsReserved": "HODOR HODOR HODOR.",
+ "term.sponsor": "HODOR HODOR HODOR",
+ "term.ciderTeam": "HODOR HODOR",
+ "term.developer": "HODOR",
+ "term.socialTeam": "HODOR HODOR",
+ "term.contributors": "HODOR",
+ "term.equalizer": "HODOR",
+ "term.reset": "HODOR",
+
+ // Home
+ "home.title": "HODOR",
+ "home.recentlyPlayed": "HODOR HODOR",
+ "home.recentlyAdded": "HODOR HODOR",
+ "home.artistsFeed": "HODOR HODOR HODOR",
+ "home.artistsFeed.noArtist": "HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR",
+ "home.madeForYou": "HODOR HODOR HODOR",
+ "home.friendsListeningTo": "HODOR HODOR HODOR",
+ "home.followedArtists": "HODOR HODOR",
+ // Errors
+ "error.appleMusicSubRequired": "HODOR HODOR HODOR HODOR HODOR.",
+ "error.connectionError": "HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "error.noResults": "HODOR HODOR.",
+ "error.noResults.description": "HODOR HODOR HODOR HODOR.",
+
+ //Podcasts
+ "podcast.followOnCider": "HODOR HODOR HODOR",
+ "podcast.followedOnCider": "HODOR HODOR HODOR",
+ "podcast.subscribeOnItunes": "HODOR HODOR HODOR",
+ "podcast.subscribedOnItunes": "HODOR HODOR HODOR",
+ "podcast.itunesStore": "HODOR HODOR",
+ "podcast.episodes": "HODOR",
+ "podcast.playEpisode": "HODOR HODOR",
+ "podcast.website": "HODOR HODOR",
+
+ // Actions
+ "action.addToLibrary": "HODOR HODOR HODOR",
+ "action.addToLibrary.success": "HODOR HODOR HODOR",
+ "action.addToLibrary.error": "HODOR HODOR HODOR LiHODORbrary",
+ "action.removeFromLibrary": "HODOR HODOR HODOR",
+ "action.removeFromLibrary.success": "HODOR HODOR HODOR",
+ "action.addToQueue": "HODOR HODOR HODOR",
+ "action.addToQueue.success": "HODOR HODOR HODOR",
+ "action.addToQueue.error": "HODOR HODOR HODOR HODOR",
+ "action.removeFromQueue": "HODOR HODOR HODOR",
+ "action.removeFromQueue.success": "HODOR HODOR HODOR",
+ "action.removeFromQueue.error": "HODOR HODOR HODOR HODOR",
+ "action.addToPlaylist": "HODOR HODOR HODOR",
+ "action.removeFromPlaylist": "HODOR HODOR HODOR",
+ "action.addToFavorites": "HODOR HODOR HODOR",
+ "action.follow": "HODOR",
+ "action.follow.success": "HODOR",
+ "action.follow.error": "HODOR HODOR",
+ "action.unfollow": "HODOR",
+ "action.unfollow.success": "HODOR",
+ "action.unfollow.error": "HODOR HODOR",
+ "action.playNext": "HODOR HODOR",
+ "action.playLater": "HODOR HODOR",
+ "action.startRadio": "HODOR HODOR",
+ "action.goToArtist": "HODOR HODOR HODOR",
+ "action.goToAlbum": "HODOR HODOR HODOR",
+ "action.moveToTop": "HODOR HODOR HODOR",
+ "action.share": "HODOR",
+ "action.rename": "HODOR",
+ "action.love": "HODOR",
+ "action.unlove": "HODOR",
+ "action.dislike": "HODOR",
+ "action.undoDislike": "HODOR HODOR",
+ "action.showWebRemoteQR": "HODOR HODOR HODOR HODOR",
+
+ // Settings - Audio
+ "settings.header.audio": "HODOR",
+ "settings.header.audio.description": "HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.audio.quality": "HODOR HODOR", // Dropdown
+ "settings.header.audio.quality.high": "HODOR.",
+ "settings.header.audio.quality.low": "HODOR!",
+ "settings.header.audio.quality.auto": "HODOR",
+ "settings.option.audio.seamlessTransition": "HODOR HODOR HODOR", // Toggle
+ "settings.option.audio.enableAdvancedFunctionality": "HODOR HODOR HODOR", // Toggle
+ "settings.option.audio.enableAdvancedFunctionality.description": "HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR , HODOR HODOR HODOR, HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "HODOR HODOR", // Toggle
+ "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.audio.enableAdvancedFunctionality.audioSpatialization": "HODOR HODOR", // Toggle
+ "settings.option.audio.enableAdvancedFunctionality.audioSpatialization.description": "HODOR HODOR HODOR HODOR HODOR HODOR 3-HODOR (HODOR: HODOR HODOR HODOR HODOR HODOR)",
+ // Settings - Visual
+ "settings.header.visual": "HODOR",
+ "settings.header.visual.description": "HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.visual.windowBackgroundStyle": "HODOR HODOR HODOR", // Toggle
+ "settings.header.visual.windowBackgroundStyle.none": "HODOR",
+ "settings.header.visual.windowBackgroundStyle.artwork": "HODOR",
+ "settings.option.visual.animatedArtwork": "HODOR HODOR", // Dropdown
+ "settings.header.visual.animatedArtwork.always": "HODOR",
+ "settings.header.visual.animatedArtwork.limited": "HODOR HODOR HODOR HODOR HODOR HODOR",
+ "settings.header.visual.animatedArtwork.disable": "HODOR HODOR",
+ "settings.option.visual.animatedArtworkQuality": "HODOR HODOR HODOR", // Dropdown
+ "settings.header.visual.animatedArtworkQuality.low": "HODOR..",
+ "settings.header.visual.animatedArtworkQuality.medium": "HODOR.",
+ "settings.header.visual.animatedArtworkQuality.high": "HODOR!",
+ "settings.header.visual.animatedArtworkQuality.veryHigh": "HODOR HODOR!",
+ "settings.header.visual.animatedArtworkQuality.extreme": "HODOOOR!!",
+ "settings.option.visual.animatedWindowBackground": "HODOR HODOR HODOR", // Toggle
+ "settings.option.visual.hardwareAcceleration": "HODOR HODOR", // Dropdown
+ "settings.option.visual.hardwareAcceleration.description": "HODOR HODOR",
+ "settings.header.visual.hardwareAcceleration.default": "HODOR.",
+ "settings.header.visual.hardwareAcceleration.webGPU": "HODOR!!",
+ // Refer to term.disabled for the disabled option
+ "settings.option.visual.showPersonalInfo": "HODOR HODOR HODOR?", // Toggle
+ // Settings - General (Reserved)
+ "settings.header.general": "HODOR",
+ "settings.header.general.description": "HODOR HODOR HODOR HODOR HODOR HODOR.",
+
+ // Settings - Lyrics
+ "settings.header.lyrics": "HODOR",
+ "settings.header.lyrics.description": "HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.lyrics.enableMusixmatch": "HODOR HODOR HODOR", // Toggle
+ "settings.option.lyrics.enableMusixmatchKaraoke": "HODOR HODOR HODOR (HODOR HODOR)", // Toggle
+ "settings.option.lyrics.musixmatchPreferredLanguage": "HODOR HODOR HODOR HODOR", // Dropdown
+ "settings.option.lyrics.enableYoutubeLyrics": "HODOR HODOR HODOR HODOR HODOR HODOR", // Toggle
+
+ // Settings - Connectivity
+ "settings.header.connectivity": "HODOR",
+ "settings.header.connectivity.description": "HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.connectivity.discordRPC": "HODOR HODOR HODOR", // Dropdown
+ // Refer to term.disabled for the disabled option
+ "settings.header.connectivity.discordRPC.cider": "HODOR HODOR 'HODOR'",
+ "settings.header.connectivity.discordRPC.appleMusic": "HODOR HODOR 'HODOR HODOR'",
+ "settings.option.connectivity.discordRPC.clearOnPause": "HODOR HODOR HODOR HODOR HODOR HODOR", // Toggle
+ "settings.option.connectivity.lastfmScrobble": "HODOR HODOR", // Option to Connect
+ "settings.option.connectivity.lastfmScrobble.delay": "HODOR HODOR HODOR (%)",
+ "settings.option.connectivity.lastfmScrobble.nowPlaying": "HODOR HODOR HODOR HODOR",
+ "settings.option.connectivity.lastfmScrobble.removeFeatured": "HODOR HODOR HODOR HODOR HODOR HODOR (HODOR)",
+ // Refer to term.connect for the connect button
+
+ // Settings - Experimental
+ "settings.header.experimental": "HODOR",
+ "settings.header.experimental.description": "HODOR HODOR HODOR HODOR HODOR HODOR.",
+ "settings.option.experimental.compactUI": "HODOR UI", // Toggle
+ // Refer to term.disabled & term.enabled
+
+ // Spatialization Menu
+ "spatial.spatialProperties" : "HODOR HODOR",
+ "spatial.width" : "HODOR",
+ "spatial.height" : "HODOR",
+ "spatial.depth" : "HODOR",
+ "spatial.roomMaterials" : "HODOR HODOR",
+ "spatial.roomDimensions" : "HODOR HODOR",
+ "spatial.roomPositions" : "HODOR HODOR",
+ "spatial.setDimensions" : "HODOR HODOR",
+ "spatial.setPositions" : "HODOR HODOR",
+ "spatial.up" : "HODOR",
+ "spatial.front" : "HODOR",
+ "spatial.left" : "HODOR",
+ "spatial.right" : "HODOR",
+ "spatial.back" : "HODOR",
+ "spatial.down" : "HODOR",
+ "spatial.listener" : "HODOR",
+ "spatial.audioSource" : "HODOR HODOR",
+
+ // Settings - Unfinished
+ "settings.header.unfinished": "HODOR",
+
+ // Web Remote
+ "remote.web.title": "HODOR HODOR",
+ "remote.web.description": "HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR",
+
+ //About
+ "about.thanks": "HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR HODOR."
+}
\ No newline at end of file
diff --git a/src/i18n/en_US.jsonc b/src/i18n/en_US.jsonc
index cffdda4a..fa095aff 100644
--- a/src/i18n/en_US.jsonc
+++ b/src/i18n/en_US.jsonc
@@ -98,6 +98,8 @@
"term.developer": "Developer",
"term.socialTeam": "Social Team",
"term.contributors": "Contributors",
+ "term.equalizer": "Equalizer",
+ "term.reset": "Reset",
// Home
"home.title": "Home",
diff --git a/src/main/base/app.ts b/src/main/base/app.ts
index b79f9963..beb61a23 100644
--- a/src/main/base/app.ts
+++ b/src/main/base/app.ts
@@ -50,7 +50,7 @@ export class AppEvents {
/***********************************************************************************************************************
* Commandline arguments
**********************************************************************************************************************/
- switch (store.get("visual.hw_acceleration")) {
+ switch (store.visual.hw_acceleration) {
default:
case "default":
electron.app.commandLine.appendSwitch('enable-accelerated-mjpeg-decode')
@@ -75,6 +75,10 @@ export class AppEvents {
break;
}
+ if (process.platform === "linux") {
+ electron.app.commandLine.appendSwitch('disable-features', 'MediaSessionService');
+ }
+
/***********************************************************************************************************************
* Protocols
**********************************************************************************************************************/
diff --git a/src/main/base/plugins.ts b/src/main/base/plugins.ts
index 637f6bd2..92663ab9 100644
--- a/src/main/base/plugins.ts
+++ b/src/main/base/plugins.ts
@@ -5,10 +5,11 @@ import * as electron from 'electron'
export default class PluginHandler {
private basePluginsPath = path.join(__dirname, '../plugins');
private userPluginsPath = path.join(electron.app.getPath('userData'), 'plugins');
- private pluginsList: any = {};
-
- constructor() {
+ private readonly pluginsList: any = {};
+ private readonly _store: any;
+ constructor(config: any) {
+ this._store = config;
this.pluginsList = this.getPlugins();
}
@@ -23,7 +24,7 @@ export default class PluginHandler {
if (plugins[file] || plugin.name in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else {
- plugins[file] = new plugin(electron.app);
+ plugins[file] = new plugin(electron.app, this._store);
}
}
});
@@ -38,7 +39,7 @@ export default class PluginHandler {
if (plugins[file] || plugin in plugins) {
console.log(`[${plugin.name}] Plugin already loaded / Duplicate Class Name`);
} else {
- plugins[file] = new plugin(electron.app);
+ plugins[file] = new plugin(electron.app, this._store);
}
}
});
diff --git a/src/main/base/store.ts b/src/main/base/store.ts
index bc495822..9079d06b 100644
--- a/src/main/base/store.ts
+++ b/src/main/base/store.ts
@@ -2,7 +2,7 @@ import * as Store from 'electron-store';
import * as electron from "electron";
export class ConfigStore {
- public store: Store | undefined;
+ private _store: Store;
private defaults: any = {
"general": {
@@ -27,7 +27,7 @@ export class ConfigStore {
"volume": 1,
"lastVolume": 1,
"muted": false,
- "quality": "990",
+ "quality": "256",
"seamless_audio": true,
"normalization": false,
"spatial": false,
@@ -49,6 +49,11 @@ export class ConfigStore {
"down": 'acoustic-ceiling-tiles',
"up": 'acoustic-ceiling-tiles',
}
+ },
+ "equalizer": {
+ 'frequencies': [32, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000],
+ 'gain': [0,0,0,0,0,0,0,0,0,0],
+ 'Q' : [1,1,1,1,1,1,1,1,1,1]
}
},
"visual": {
@@ -84,14 +89,26 @@ export class ConfigStore {
private migrations: any = {}
constructor() {
- this.store = new Store({
+ this._store = new Store({
name: 'cider-config',
defaults: this.defaults,
migrations: this.migrations,
});
- this.store.set(this.mergeStore(this.defaults, this.store.store))
- this.ipcHandler(this.store);
+ this._store.set(this.mergeStore(this.defaults, this._store.store))
+ this.ipcHandler(this._store);
+ }
+
+ get store() {
+ return this._store.store;
+ }
+
+ get(key: string) {
+ return this._store.get(key);
+ }
+
+ set(key: string, value: any) {
+ this._store.set(key, value);
}
/**
diff --git a/src/main/base/win.ts b/src/main/base/win.ts
index 5ce72d6d..da304567 100644
--- a/src/main/base/win.ts
+++ b/src/main/base/win.ts
@@ -15,10 +15,10 @@ import {wsapi} from "./wsapi";
import * as jsonc from "jsonc";
export class Win {
- win: any | undefined = null;
- app: any | undefined = null;
- store: any | undefined = null;
- devMode: boolean = !electron.app.isPackaged;
+ private win: any | undefined = null;
+ private app: any | undefined = null;
+ private store: any | undefined = null;
+ private devMode: boolean = !electron.app.isPackaged;
constructor(app: electron.App, store: any) {
this.app = app;
diff --git a/src/main/index.ts b/src/main/index.ts
index 72391f5b..6862aa18 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -13,7 +13,7 @@ import PluginHandler from "./base/plugins";
const config = new ConfigStore();
const App = new AppEvents(config.store);
const Cider = new Win(electron.app, config.store)
-const plug = new PluginHandler();
+const plug = new PluginHandler(config.store);
let win: Electron.BrowserWindow;
@@ -34,7 +34,7 @@ electron.app.on('ready', () => {
win = await Cider.createWindow()
App.bwCreated(win);
/// please dont change this for plugins to get proper and fully initialized Win objects
- plug.callPlugins('onReady', Cider);
+ plug.callPlugins('onReady', win);
win.on("ready-to-show", () => {
win.show();
});
diff --git a/src/main/plugins/Extras/examplePlugin.ts b/src/main/plugins/Extras/examplePlugin.ts
index 78a194a9..e96045fb 100644
--- a/src/main/plugins/Extras/examplePlugin.ts
+++ b/src/main/plugins/Extras/examplePlugin.ts
@@ -1,10 +1,11 @@
let i = 1, k = 1;
export default class ExamplePlugin {
- /**
- * Private variables for interaction in plugins
- */
- private _win: any;
- private _app: any;
+ /**
+ * Private variables for interaction in plugins
+ */
+ private _win: any;
+ private _app: any;
+ private _store: any;
/**
* Base Plugin Details (Eventually implemented into a GUI in settings)
@@ -17,16 +18,17 @@ export default class ExamplePlugin {
/**
* Runs on plugin load (Currently run on application start)
*/
- constructor(app: any) {
- this._app = app;
- console.log('Example plugin loaded');
- }
+ constructor(app: any, store: any) {
+ this._app = app;
+ this._store = store;
+ console.log('Example plugin loaded');
+ }
/**
* Runs on app ready
*/
onReady(win: any): void {
- this._win = win;
+ this._win = win;
console.log('Example plugin ready');
}
@@ -42,7 +44,7 @@ export default class ExamplePlugin {
* @param attributes Music Attributes (attributes.state = current state)
*/
onPlaybackStateDidChange(attributes: object): void {
- console.log('onPlaybackStateDidChange has been called ' + i +' times');
+ console.log('onPlaybackStateDidChange has been called ' + i + ' times');
i++
}
@@ -51,7 +53,7 @@ export default class ExamplePlugin {
* @param attributes Music Attributes
*/
onNowPlayingItemDidChange(attributes: object): void {
- console.log('onNowPlayingDidChange has been called ' + k +' times');
+ console.log('onNowPlayingDidChange has been called ' + k + ' times');
k++
}
diff --git a/src/main/plugins/discordrpc.ts b/src/main/plugins/discordrpc.ts
index 77ce5482..43d4b849 100644
--- a/src/main/plugins/discordrpc.ts
+++ b/src/main/plugins/discordrpc.ts
@@ -1,28 +1,30 @@
-import * as electron from 'electron';
import * as DiscordRPC from 'discord-rpc'
+
export default class DiscordRPCPlugin {
- /**
- * Private variables for interaction in plugins
- */
- private _win: any;
- private _app: any;
+ /**
+ * Private variables for interaction in plugins
+ */
+ private _win: Electron.BrowserWindow | undefined;
+ private _app: any;
+ private _store: any;
private _discord: any;
+
private connect(clientId: any) {
- this._discord = { isConnected: false };
- if (this._win.store.store.general.discord_rpc == 0 || this._discord.isConnected) return;
+ this._discord = {isConnected: false};
+ if (this._store.general.discord_rpc == 0 || this._discord.isConnected) return;
DiscordRPC.register(clientId) // Apparently needed for ask to join, join, spectate etc.
- const client = new DiscordRPC.Client({ transport: "ipc" });
- this._discord = Object.assign(client, { error: false, activityCache: null, isConnected: false });
+ const client = new DiscordRPC.Client({transport: "ipc"});
+ this._discord = Object.assign(client, {error: false, activityCache: null, isConnected: false});
// Login to Discord
- this._discord.login({ clientId })
+ this._discord.login({clientId})
.then(() => {
this._discord.isConnected = true;
})
- .catch((e : any) => console.error(`[DiscordRPC][connect] ${e}`));
+ .catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`));
- this._discord.on('ready', () => {
+ this._discord.on('ready', () => {
console.log(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${client.user.username} (${client.user.id})`);
})
@@ -34,19 +36,19 @@ export default class DiscordRPCPlugin {
});
}
- /**
+ /**
* Disconnects from Discord RPC
*/
private disconnect() {
- if (this._win.store.store.general.discord_rpc == 0 || !this._discord.isConnected) return;
-
+ if (this._store.general.discord_rpc == 0 || !this._discord.isConnected) return;
+
try {
this._discord.destroy().then(() => {
this._discord.isConnected = false;
console.log('[DiscordRPC][disconnect] Disconnected from discord.')
- }).catch((e : any) => console.error(`[DiscordRPC][disconnect] ${e}`));
+ }).catch((e: any) => console.error(`[DiscordRPC][disconnect] ${e}`));
} catch (err) {
- console.error(err)
+ console.error(err)
}
}
@@ -54,54 +56,57 @@ export default class DiscordRPCPlugin {
* Sets the activity of the client
* @param {object} attributes
*/
- private updateActivity(attributes : any) {
- if (this._win.store.store.general.discord_rpc == 0) return;
+ private updateActivity(attributes: any) {
+ if (this._store.general.discord_rpc == 0) return;
if (!this._discord.isConnected) {
- this._discord.clearActivity().catch((e : any) => console.error(`[DiscordRPC][updateActivity] ${e}`));
+ this._discord.clearActivity().catch((e: any) => console.error(`[DiscordRPC][updateActivity] ${e}`));
return;
}
// console.log('[DiscordRPC][updateActivity] Updating Discord Activity.')
const listenURL = `https://cider.sh/p?s&id=${attributes.playParams.id}` // cider://play/s/[id] (for song)
- //console.log(attributes)
+ //console.log(attributes)
- interface ActObject {
+ interface ActObject extends DiscordRPC.Presence {
details?: any,
state?: any,
startTimestamp?: any,
endTimestamp?: any,
- largeImageKey? : any,
+ largeImageKey?: any,
largeImageText?: any,
smallImageKey?: any,
smallImageText?: any,
instance: true,
buttons?: [
- { label: "Listen on Cider", url?: any },
+ {
+ label: string,
+ url: string
+ }
]
- }
+ }
- let ActivityObject : ActObject | null = {
+ let ActivityObject: ActObject | null = {
details: attributes.name,
state: `by ${attributes.artistName}`,
startTimestamp: attributes.startTime,
endTimestamp: attributes.endTime,
- largeImageKey : (attributes.artwork.url.replace('{w}', '1024').replace('{h}', '1024')) ?? 'cider',
+ largeImageKey: (attributes.artwork.url.replace('{w}', '1024').replace('{h}', '1024')) ?? 'cider',
largeImageText: attributes.albumName,
smallImageKey: (attributes.status ? 'play' : 'pause'),
smallImageText: (attributes.status ? 'Playing' : 'Paused'),
instance: true,
buttons: [
- { label: "Listen on Cider", url: listenURL },
+ {label: "Listen on Cider", url: listenURL},
]
};
if (ActivityObject.largeImageKey == "" || ActivityObject.largeImageKey == null) {
- ActivityObject.largeImageKey = (this._win.store.store.general.discord_rpc == 1) ? "cider" : "logo"
+ ActivityObject.largeImageKey = (this._store.general.discord_rpc == 1) ? "cider" : "logo"
}
// Remove the pause/play icon and test for clear activity on pause
- if (this._win.store.store.general.discordClearActivityOnPause == 1) {
+ if (this._store.general.discordClearActivityOnPause == 1) {
delete ActivityObject.smallImageKey
delete ActivityObject.smallImageText
}
@@ -128,13 +133,11 @@ export default class DiscordRPCPlugin {
}
-
-
// Check if its pausing (false) or playing (true)
if (!attributes.status) {
- if (this._win.store.store.general.discordClearActivityOnPause == 1) {
- this._discord.clearActivity().catch((e : any) => console.error(`[DiscordRPC][clearActivity] ${e}`));
- ActivityObject = null
+ if (this._store.general.discordClearActivityOnPause == 1) {
+ this._discord.clearActivity().catch((e: any) => console.error(`[DiscordRPC][clearActivity] ${e}`));
+ ActivityObject = null
} else {
delete ActivityObject.startTimestamp
delete ActivityObject.endTimestamp
@@ -168,16 +171,17 @@ export default class DiscordRPCPlugin {
/**
* Runs on plugin load (Currently run on application start)
*/
- constructor(app: any) {
- this._app = app;
- }
+ constructor(app: any, store: any) {
+ this._app = app;
+ this._store = store
+ }
/**
* Runs on app ready
*/
onReady(win: any): void {
- this._win = win;
- this.connect((this._win.store.store.general.discord_rpc == 1) ? '911790844204437504' : '886578863147192350');
+ this._win = win;
+ this.connect((this._store.general.discord_rpc == 1) ? '911790844204437504' : '886578863147192350');
// electron.ipcMain.on("forceUpdateRPC", (event, attributes : object) => {
// this.updateActivity(attributes)
// });
diff --git a/src/main/plugins/lastfm.ts b/src/main/plugins/lastfm.ts
index 78fdfa90..d586b857 100644
--- a/src/main/plugins/lastfm.ts
+++ b/src/main/plugins/lastfm.ts
@@ -1,7 +1,6 @@
import * as electron from 'electron';
import * as fs from 'fs';
import {resolve} from 'path';
-//@ts-ignore
export default class LastFMPlugin {
private sessionPath = resolve(electron.app.getPath('userData'), 'session.json');
@@ -15,6 +14,7 @@ export default class LastFMPlugin {
private _win: any;
private _app: any;
private _lastfm: any;
+ private _store: any;
private authenticateFromFile() {
let sessionData = require(this.sessionPath)
@@ -26,12 +26,12 @@ export default class LastFMPlugin {
authenticate() {
try {
- if (this._win.store.store.lastfm.auth_token) {
- this._win.store.store.lastfm.enabled = true;
+ if (this._store.lastfm.auth_token) {
+ this._store.lastfm.enabled = true;
}
- if (!this._win.store.store.lastfm.enabled || !this._win.store.store.lastfm.auth_token) {
- this._win.store.store.lastfm.enabled = false;
+ if (!this._store.lastfm.enabled || !this._store.lastfm.auth_token) {
+ this._store.lastfm.enabled = false;
return
}
/// dont move this require to top , app wont load
@@ -47,8 +47,8 @@ export default class LastFMPlugin {
if (err) {
console.error("[LastFM][Session] Session file couldn't be opened or doesn't exist,", err)
console.log("[LastFM][Auth] Beginning authentication from configuration")
- console.log("[LastFM][tk]", this._win.store.store.lastfm.auth_token)
- this._lastfm.authenticate(this._win.store.store.lastfm.auth_token, (err: any, session: any) => {
+ console.log("[LastFM][tk]", this._store.lastfm.auth_token)
+ this._lastfm.authenticate(this._store.lastfm.auth_token, (err: any, session: any) => {
if (err) {
throw err;
}
@@ -78,7 +78,7 @@ export default class LastFMPlugin {
}
private async scrobbleSong(attributes: any) {
- await new Promise(resolve => setTimeout(resolve, Math.round(attributes.durationInMillis * (this._win.store.store.lastfm.scrobble_after / 100))));
+ await new Promise(resolve => setTimeout(resolve, Math.round(attributes.durationInMillis * (this._store.lastfm.scrobble_after / 100))));
const currentAttributes = attributes;
if (!this._lastfm || this._lastfm.cachedAttributes === attributes) {
@@ -117,7 +117,7 @@ export default class LastFMPlugin {
}
private filterArtistName(artist: any) {
- if (!this._win.store.store.lastfm.enabledRemoveFeaturingArtists) return artist;
+ if (!this._store.lastfm.enabledRemoveFeaturingArtists) return artist;
artist = artist.split(' ');
if (artist.includes('&')) {
@@ -135,7 +135,7 @@ export default class LastFMPlugin {
}
private updateNowPlayingSong(attributes: any) {
- if (!this._lastfm || this._lastfm.cachedNowPlayingAttributes === attributes || !this._win.store.store.lastfm.NowPlaying) {
+ if (!this._lastfm || this._lastfm.cachedNowPlayingAttributes === attributes || !this._store.lastfm.NowPlaying) {
return
}
@@ -177,8 +177,9 @@ export default class LastFMPlugin {
/**
* Runs on plugin load (Currently run on application start)
*/
- constructor(app: any) {
+ constructor(app: any, store: any) {
this._app = app;
+ this._store = store
electron.app.on('second-instance', (_e: any, argv: any) => {
// Checks if first instance is authorized and if second instance has protocol args
argv.forEach((value: any) => {
@@ -187,8 +188,8 @@ export default class LastFMPlugin {
let authURI = String(argv).split('/auth/')[1];
if (authURI.startsWith('lastfm')) { // If we wanted more auth options
const authKey = authURI.split('lastfm?token=')[1];
- this._win.store.store.lastfm.enabled = true;
- this._win.store.store.lastfm.auth_token = authKey;
+ this._store.lastfm.enabled = true;
+ this._store.lastfm.auth_token = authKey;
console.log(authKey);
this._win.win.webContents.send('LastfmAuthenticated', authKey);
this.authenticate();
@@ -203,8 +204,8 @@ export default class LastFMPlugin {
let authURI = String(arg).split('/auth/')[1];
if (authURI.startsWith('lastfm')) { // If we wanted more auth options
const authKey = authURI.split('lastfm?token=')[1];
- this._win.store.store.lastfm.enabled = true;
- this._win.store.store.lastfm.auth_token = authKey;
+ this._store.lastfm.enabled = true;
+ this._store.lastfm.auth_token = authKey;
this._win.win.webContents.send('LastfmAuthenticated', authKey);
console.log(authKey);
this.authenticate();
diff --git a/src/main/plugins/mpris.ts b/src/main/plugins/mpris.ts
new file mode 100644
index 00000000..03cdb544
--- /dev/null
+++ b/src/main/plugins/mpris.ts
@@ -0,0 +1,196 @@
+// @ts-ignore
+import * as Player from 'mpris-service';
+
+export default class MPRIS {
+ /**
+ * Private variables for interaction in plugins
+ */
+ private _win: any;
+ private _app: any;
+
+ /**
+ * Base Plugin Details (Eventually implemented into a GUI in settings)
+ */
+ public name: string = 'MPRIS Service';
+ public description: string = 'Handles MPRIS service calls for Linux systems.';
+ public version: string = '1.0.0';
+ public author: string = 'Core';
+
+ /**
+ * MPRIS Service
+ */
+ private mpris: any;
+ private mprisEvents: Object = {
+ "playpause": "pausePlay",
+ "play": "pausePlay",
+ "pause": "pausePlay",
+ "next": "nextTrack",
+ "previous": "previousTrack",
+ }
+
+ /*******************************************************************************************
+ * Private Methods
+ * ****************************************************************************************/
+
+ /**
+ * Runs a media event
+ * @param type - pausePlay, nextTrack, PreviousTrack
+ * @private
+ */
+ private runMediaEvent(type: string) {
+ if (this._win) {
+ this._win.webContents.executeJavaScript(`MusicKitInterop.${type}()`).catch(console.error)
+ }
+ }
+
+ /**
+ * Blocks non-linux systems from running this plugin
+ * @private
+ */
+ private static linuxOnly(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
+ if (process.platform !== 'linux') {
+ descriptor.value = function () {
+ return
+ }
+ }
+
+ }
+
+
+ /**
+ * Connects to MPRIS Service
+ */
+ @MPRIS.linuxOnly
+ private connect() {
+ this.mpris = Player({
+ name: 'Cider',
+ identity: 'Cider',
+ supportedUriSchemes: [],
+ supportedMimeTypes: [],
+ supportedInterfaces: ['player']
+ });
+ this.mpris = Object.assign(this.mpris, {
+ canQuit: true,
+ canControl: true,
+ canPause: true,
+ canPlay: true,
+ canGoNext: true,
+ active: true
+ })
+
+
+ const pos_atr = {durationInMillis: 0};
+ this.mpris.getPosition = function () {
+ const durationInMicro = pos_atr.durationInMillis * 1000;
+ const percentage = parseFloat("0") || 0;
+ return durationInMicro * percentage;
+ }
+
+ for (const [key, value] of Object.entries(this.mprisEvents)) {
+ this.mpris.on(key, () => {
+ this.runMediaEvent(value)
+ });
+ }
+ }
+
+ /**
+ * Update MPRIS Player Attributes
+ */
+ @MPRIS.linuxOnly
+ private updatePlayer(attributes: any) {
+
+ const MetaData = {
+ 'mpris:trackid': this.mpris.objectPath(`track/${attributes.playParams.id.replace(/[.]+/g, "")}`),
+ 'mpris:length': attributes.durationInMillis * 1000, // In microseconds
+ 'mpris:artUrl': (attributes.artwork.url.replace('/{w}x{h}bb', '/512x512bb')).replace('/2000x2000bb', '/35x35bb'),
+ 'xesam:title': `${attributes.name}`,
+ 'xesam:album': `${attributes.albumName}`,
+ 'xesam:artist': [`${attributes.artistName}`,],
+ 'xesam:genre': attributes.genreNames
+ }
+
+ if (this.mpris.metadata["mpris:trackid"] === MetaData["mpris:trackid"]) {
+ return
+ }
+
+ this.mpris.metadata = MetaData
+
+ }
+
+ /**
+ * Update MPRIS Player State
+ * @private
+ * @param attributes
+ */
+ @MPRIS.linuxOnly
+ private updatePlayerState(attributes: any) {
+
+ let status = 'Stopped';
+ if (attributes.status) {
+ status = 'Playing';
+ } else if (attributes.status === false) {
+ status = 'Paused';
+ }
+
+ if (this.mpris.playbackStatus === status) {
+ return
+ }
+ this.mpris.playbackStatus = status;
+ }
+
+ /**
+ * Clear state
+ * @private
+ */
+ private clearState() {
+ this.mpris.metadata = {'mpris:trackid': '/org/mpris/MediaPlayer2/TrackList/NoTrack'}
+ this.mpris.playbackStatus = 'Stopped';
+ }
+
+
+ /*******************************************************************************************
+ * Public Methods
+ * ****************************************************************************************/
+
+ /**
+ * Runs on plugin load (Currently run on application start)
+ */
+ constructor(app: any, _store: any) {
+ this._app = app;
+ console.log(`[${this.name}] plugin loaded`);
+ }
+
+ /**
+ * Runs on app ready
+ */
+ onReady(win: any): void {
+ this._win = win;
+ console.log(`[${this.name}] plugin ready`);
+ this.connect()
+ }
+
+ /**
+ * Runs on app stop
+ */
+ onBeforeQuit(): void {
+ console.log(`[${this.name}] plugin stopped`);
+ this.clearState()
+ }
+
+ /**
+ * Runs on playback State Change
+ * @param attributes Music Attributes (attributes.state = current state)
+ */
+ onPlaybackStateDidChange(attributes: object): void {
+ this.updatePlayerState(attributes)
+ }
+
+ /**
+ * Runs on song change
+ * @param attributes Music Attributes
+ */
+ onNowPlayingItemDidChange(attributes: object): void {
+ this.updatePlayer(attributes);
+ }
+
+}
diff --git a/src/renderer/assets/logocut.png b/src/renderer/assets/logocut.png
index 3f1e9b8b..29e744c4 100644
Binary files a/src/renderer/assets/logocut.png and b/src/renderer/assets/logocut.png differ
diff --git a/src/renderer/audio/audio.js b/src/renderer/audio/audio.js
index 64118b7c..aa2ebd16 100644
--- a/src/renderer/audio/audio.js
+++ b/src/renderer/audio/audio.js
@@ -5,6 +5,7 @@ var CiderAudio = {
gainNode : null,
spatialNode : null,
spatialInput: null,
+ audioBands : null,
},
init: function (cb = function () { }) {
//AudioOutputs.fInit = true;
@@ -42,6 +43,7 @@ var CiderAudio = {
if (app.cfg.audio.spatial){
CiderAudio.spatialOn()
}
+ CiderAudio.equalizer()
},
normalizerOn: function (){},
normalizerOff: function (){
@@ -49,7 +51,7 @@ var CiderAudio = {
},
spatialOn: function (){
try{
- CiderAudio.audioNodes.gainNode.connect(CiderAudio.context.destination);} catch(e){}
+ CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.context.destination);} catch(e){}
CiderAudio.audioNodes.spatialNode = new ResonanceAudio(CiderAudio.context);
CiderAudio.audioNodes.spatialNode.output.connect(CiderAudio.context.destination);
let roomDimensions = {
@@ -90,6 +92,46 @@ var CiderAudio = {
}
);
}
+ },
+ equalizer: function (){
+ let BANDS = app.cfg.audio.equalizer.frequencies;
+ let GAIN = app.cfg.audio.equalizer.gain;
+ let Q = app.cfg.audio.equalizer.Q;
+ CiderAudio.audioNodes.audioBands = {};
+
+ BANDS.forEach((band, i) => {
+ const filter = CiderAudio.context.createBiquadFilter();
+
+ CiderAudio.audioNodes.audioBands[i] = filter;
+
+ if (i === 0) {
+ // The first filter, includes all lower frequencies
+ filter.type = "lowshelf";
+ } else if (i === BANDS.length - 1) {
+ // The last filter, includes all higher frequencies
+ filter.type = "highshelf";
+ } else {
+ filter.type = "peaking";
+ }
+ filter.frequency.value = BANDS[i];
+ filter.gain.value = GAIN[i];
+ filter.Q.value = Q[i];
+ if (i == 0){
+ if (app.cfg.audio.spatial) {
+ CiderAudio.audioNodes.spatialNode.output.disconnect(CiderAudio.context.destination);
+ CiderAudio.audioNodes.spatialNode.output.connect(filter);
+ } else {
+ CiderAudio.audioNodes.gainNode.disconnect(CiderAudio.context.destination);
+ CiderAudio.audioNodes.gainNode.connect(filter);
+ }
+ } else if (i === BANDS.length - 1) {
+ CiderAudio.audioNodes.audioBands[i - 1].connect(filter);
+ } else {
+ CiderAudio.audioNodes.audioBands[i - 1].connect(filter);
+ filter.connect(CiderAudio.context.destination);
+ }
+
+ });
}
}
diff --git a/src/renderer/index.js b/src/renderer/index.js
index 1c470073..13168409 100644
--- a/src/renderer/index.js
+++ b/src/renderer/index.js
@@ -297,7 +297,8 @@ const app = new Vue({
modals: {
addToPlaylist: false,
spatialProperties: false,
- qrcode: false
+ qrcode: false,
+ equalizer: false,
},
socialBadges: {
badgeMap: {},
diff --git a/src/renderer/style.less b/src/renderer/style.less
index 8ceefcb2..3c81ca02 100644
--- a/src/renderer/style.less
+++ b/src/renderer/style.less
@@ -551,6 +551,8 @@ input[type=range].web-slider::-webkit-slider-runnable-track {
display: flex;
font-size: 14px;
flex-direction: column;
+ text-align: center;
+ margin-right: 35px ;
}
.app-sidebar-button > .sidebar-user-text .fullname {
@@ -5699,4 +5701,86 @@ body.no-gpu {
overflow-y: hidden;
}
+.equalizer-panel {
+ .modal-window {
+ height: 330px;
+ max-height: 330px;
+ width: 630px;
+ max-width: 630px;
+ overflow: hidden;
+
+ .info-header {
+ padding-left: 12px;
+ }
+
+ .visual-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ }
+
+
+ .modal-header {
+ padding: 16px;
+ position: relative;
+ overflow: hidden;
+
+ .modal-title {
+ text-align: center;
+ }
+
+ .close-btn {
+ width: 50px;
+ height: 100%;
+ background-image: var(--gfx-closeBtn);
+ background-position: center;
+ background-repeat: no-repeat;
+ -webkit-app-region: no-drag;
+ appearance: none;
+ border: 0;
+ background-color: transparent;
+ position: absolute;
+ top: 0;
+ right: 0;
+
+ &:hover {
+ background-color: rgb(196, 43, 28)
+ }
+ }
+ }
+ .modal-content{
+ display: block;
+ .input-container{
+ display: inline-grid;
+ width: 52px;
+ justify-items: center;
+ font-size: 0.7em;
+ }
+ .freq-header{
+ margin-bottom: 2px;
+ }
+
+ .reset-button{
+ width: 50%;
+ margin-left: 25%;
+ }
+ input.eq-slider {
+ -webkit-appearance: slider-vertical;
+ width: 5%;
+ }
+ input[type="number"]{
+ padding: unset;
+ width: 55px;
+ }
+ .header input.eq-slider {
+ -webkit-appearance: slider-vertical;
+ width: 5%;
+ opacity: 0;
+ }
+ }
+ }
+}
+
+
@import url("less/compact.less");
diff --git a/src/renderer/views/components/equalizer.ejs b/src/renderer/views/components/equalizer.ejs
new file mode 100644
index 00000000..41cb9843
--- /dev/null
+++ b/src/renderer/views/components/equalizer.ejs
@@ -0,0 +1,123 @@
+
+
+
\ No newline at end of file
diff --git a/src/renderer/views/main.ejs b/src/renderer/views/main.ejs
index 5337cb17..5b2f56dd 100644
--- a/src/renderer/views/main.ejs
+++ b/src/renderer/views/main.ejs
@@ -264,6 +264,10 @@
+