diff --git a/package.json b/package.json index 70e22263..9c22e95d 100644 --- a/package.json +++ b/package.json @@ -39,10 +39,10 @@ "@sentry/electron": "^3.0.7", "@sentry/integrations": "^6.19.6", "adm-zip": "0.4.10", - "airtunes2": "git+https://github.com/vapormusic/node_airtunes2.git", + "airtunes2": "git+https://github.com/vapormusic/node_airtunes2.git#hap", "castv2-client": "^1.2.0", "chokidar": "^3.5.3", - "discord-rpc": "^4.0.1", + "discord-auto-rpc": "^1.0.16", "dns-js": "git+https://github.com/ciderapp/node-dns-js.git", "ejs": "^3.1.6", "electron-fetch": "^1.7.4", @@ -82,7 +82,7 @@ "electron-builder-notarize-pkg": "^1.2.0", "electron-webpack": "^2.8.2", "musickit-typescript": "^1.2.4", - "typescript": "^4.6.3", + "typescript": "^4.6.4", "vue-devtools": "^5.1.4", "webpack": "~5.72.0" }, @@ -109,9 +109,9 @@ } ], "build": { - "electronVersion": "18.1.0", + "electronVersion": "18.2.1", "electronDownload": { - "version": "18.1.0+wvcus", + "version": "18.2.1+wvcus", "mirror": "https://github.com/castlabs/electron-releases/releases/download/v" }, "appId": "cider", diff --git a/src/i18n/README.md b/src/i18n/README.md index 4246957d..b81a8498 100644 --- a/src/i18n/README.md +++ b/src/i18n/README.md @@ -297,5 +297,48 @@ Update 28/04/2022 21:45 UTC * `settings.option.general.resumetabs.description`: Added for `en_US` * `settings.option.general.resumetabs.dynamic`: Added for `en_US` * `settings.option.general.resumetabs.dynamic.description`: Added for `en_US` +* `term.dynamic`: Added for `en_US` + +Update 29/04/2022 00:00 UTC + + * `menubar.options.about`: Added for `en_US` + * `menubar.options.settings`: Added for `en_US` + * `menubar.options.quit`: Added for `en_US` + * `menubar.options.view`: Added for `en_US` + * `menubar.options.reload`: Added for `en_US` + * `menubar.options.forcereload`: Added for `en_US` + * `menubar.options.toggledevtools`: Added for `en_US` + * `menubar.options.window`: Added for `en_US` + * `menubar.options.minimize`: Added for `en_US` + * `menubar.options.toggleprivate`: Added for `en_US` + * `menubar.options.webremote`: Added for `en_US` + * `menubar.options.audio`: Added for `en_US` + * `menubar.options.plugins`: Added for `en_US` + * `menubar.options.control`: Added for `en_US` + * `menubar.options.next`: "Added for `en_US` + * `menubar.options.previous`: Added for `en_US` + * `menubar.options.volumeup`: Added for `en_US` + * `menubar.options.volumedown`: Added for `en_US` + * `menubar.options.browse`: Added for `en_US` + * `menubar.options.artists`: Added for `en_US` + * `menubar.options.search`: Added for `en_US` + * `menubar.options.albums`: Added for `en_US` + * `menubar.options.cast`: Added for `en_US` + * `menubar.options.account`: Added for `en_US` + * `menubar.options.accountsettings`: Added for `en_US` + * `menubar.options.signout`: Added for `en_US` + * `menubar.options.support`: Added for `en_US` + * `menubar.options.discord`: Added for `en_US` + * `menubar.options.github`: Added for `en_US` + * `menubar.options.report`: Added for `en_US` + * `menubar.options.bug`: Added for `en_US` + * `menubar.options.feature`: Added for `en_US` + * `menubar.options.trans`: Added for `en_US` + * `menubar.options.license`: Added for `en_US` + * `menubar.options.conf`: Added for `en_US` +Update 08/05/2022 00:29 UTC + +* `settings.option.visual.theme.github.available`: Added for `en_US`, +* `settings.option.visual.theme.github.applied`: Added for `en_US`, diff --git a/src/i18n/el_GR.json b/src/i18n/el_GR.json index eda64396..2c7843c6 100644 --- a/src/i18n/el_GR.json +++ b/src/i18n/el_GR.json @@ -10,6 +10,7 @@ "notification.updatingLibrarySongs": "Ενημέρωση βιβλιοθήκης τραγουδιών...", "notification.updatingLibraryAlbums": "Ενημέρωση βιβλιοθήκης άλμπουμ...", "notification.updatingLibraryArtists": "Ενημέρωση βιβλιοθήκης καλλιτεχνών...", + "term.variables": "Μεταβλητές", "term.appleInc": "Apple Inc.", "term.appleMusic": "Apple Music", "term.applePodcasts": "Apple Podcasts", @@ -20,9 +21,13 @@ "term.accountSettings": "Ρυθμίσεις λογαριασμού", "term.logout": "Αποσύνδεση", "term.login": "Σύνδεση", + "term.cast": "Μετάδοση", "term.about": "Σχετικά με", "term.privateSession": "Ιδιωτική περίοδος λειτουργίας", + "term.disablePrivateSession": "Απενεργ. ιδιωτικής περ. λειτουργίας", "term.queue": "Ουρά", + "term.lyrics": "Στίχοι", + "term.miniplayer": "MiniPlayer", "term.history": "Ιστορικό", "term.search": "Εύρεση", "term.library": "Βιβλιοθήκη", @@ -68,6 +73,7 @@ "term.viewAs": "Προβολή ως", "term.viewAs.coverArt": "Εξώφυλλο", "term.viewAs.list": "Λίστα", + "term.dynamic": "Δυναμικό", "term.size": "Μέγεθος", "term.size.normal": "Κανονικό", "term.size.compact": "Συμπαγή", @@ -89,12 +95,26 @@ "term.time.added": "Προστέθηκε", "term.time.released": "Κυκλοφόρησε", "term.time.updated": "Ενημερώθηκε", + "term.time.days": "μέρες", + "term.time.day": { + "one": "μέρα", + "other": "μέρες" + }, "term.time.hours": "ώρες", - "term.time.hour": "ώρα", + "term.time.hour": { + "one": "ώρα", + "other": "ώρες" + }, "term.time.minutes": "λεπτά", - "term.time.minute": "λεπτό", + "term.time.minute": { + "one": "λεπτό", + "other": "λεπτά" + }, "term.time.seconds": "δευτερόλεπτα", - "term.time.second": "δευτερόλεπτο", + "term.time.second": { + "one": "δευτερόλεπτο", + "other": "δευτερόλεπτα" + }, "term.fullscreenView": "Πλήρης οθόνη", "term.defaultView": "Κανονική οθόνη", "term.audioSettings": "Ρυθμίσεις ήχου", @@ -114,7 +134,8 @@ "term.contributors": "Συνεισφέροντες", "term.equalizer": "Ισοσταθμιστής", "term.reset": "Επαναφορά", - "term.tracks": { + "term.tracks": "τραγούδια", + "term.track": { "one": "τραγούδι", "other": "τραγούδια" }, @@ -145,6 +166,7 @@ "term.noVideos": "Δεν βρέθηκαν βίντεο", "term.plugin": "Πρόσθετα", "term.pluginMenu": "Μενού πρόσθετων", + "term.pluginMenu.none": "Δεν υπάρχουν πρόσθετα", "term.replay": "Replay", "term.uniqueAlbums": "Μοναδικά άλμπουμ", "term.uniqueArtists": "Μοναδικοί καλλιτέχνες", @@ -156,10 +178,12 @@ "term.plays": "Αναπαραγωγές", "term.topGenres": "Κορυφαία είδη", "term.confirmLogout": "Θέλετε σίγουρα να αποσυνδεθείτε;", + "term.creditDesignedBy": "Σχεδιάστηκε από ${authorUsername}", + "term.discNumber": "Δίσκος ${discNumber}", "home.title": "Αρχική", "home.recentlyPlayed": "Έπαιξαν πρόσφατα", "home.recentlyAdded": "Πρόσφατες προσθήκες", - "home.artistsFeed": "Ροή των καλλιτεχνών σου", + "home.artistsFeed": "Ροή των καλλιτεχνών σας", "home.artistsFeed.noArtist": "Ακολούθησε μερικούς καλλιτέχνες πρώτα και οι τελευταίες κυκλοφορίες τους θα εμφανίζονται εδώ", "home.madeForYou": "Δημιουργήθηκε για εσάς", "home.friendsListeningTo": "Οι φίλοι σου ακούν", @@ -176,6 +200,9 @@ "podcast.episodes": "Επεισόδια", "podcast.playEpisode": "Αναπαραγωγή επεισοδίου", "podcast.website": "Ιστότοπος Podcast", + "action.edit": "Επεξεργασία", + "action.done": "Τέλος", + "action.editTracklist": "Επεξεργασία λίστας τραγουδιών", "action.addToLibrary": "Προσθήκη στη βιβλιοθήκη", "action.addToLibrary.success": "Προστέθηκε στη βιβλιοθήκη", "action.addToLibrary.error": "Σφάλμα Προσθήκης στη βιβλιοθήκης", @@ -202,6 +229,8 @@ "action.startRadio": "Έναρξη ραδιοφώνου", "action.goToArtist": "Μετάβαση σε καλλιτέχνη", "action.goToAlbum": "Μετάβαση σε άλμπουμ", + "action.showInPlaylist": "Εμφάνιση στη λίστα αναπαραγωγής", + "action.showInAppleMusic": "Εμφάνιση στο Apple Music", "action.moveToTop": "Μετακίνηση στη κορυφή", "action.share": "Κοινή χρήση", "action.rename": "Μετονομασία", @@ -209,7 +238,7 @@ "action.unlove": "Αναίρεση \"Μου αρέσει\"", "action.dislike": "Δεν μου αρέσει", "action.undoDislike": "Αναίρεση \"Δεν μου αρέσει\"", - "action.showWebRemoteQR": "Εμφάνιση Web Remote QR", + "action.showWebRemoteQR": "Web Remote", "action.playTracksNext": "Αναπαραγωγή ${app.selectedMediaItems.length} τραγουδιών ως επόμενων", "action.playTracksLater": "Αναπαραγωγή ${app.selectedMediaItems.length} τραγουδιών αργότερα", "action.removeTracks": "Αφαίρεση ${self.selectedItems.length} τραγουδιών από την ουρά", @@ -218,13 +247,23 @@ "action.showAlbum": "Εμφάνιση ολόκληρου άλμπουμ", "action.tray.minimize": "Ελαχιστοποίηση στη γωνία γραμμής εργασιών", "action.tray.quit": "Έξοδος", - + "action.tray.show": "Εμφάνιση Cider", "action.update": "Ενημέρωση", "action.install": "Εγκατάσταση", "action.copy": "Αντιγραφή", "action.newpreset": "Νέα προρύθμιση...", "action.deletepreset": "Διαγραφή προρύθμισης", "action.open": "Άνοιγμα", + "action.relaunch.confirm": "Θέλετε να επανεκκινήσετε τον Cider;", + "action.cast.chromecast": "Chromecast", + "action.cast.todevices": "Μετάδοση σε συσκευές", + "action.cast.stop": "Διακοπή μετάδοσης σε όλες τις συσκευές", + "action.cast.airplay": "AirPlay", + "action.cast.airplay.underdevelopment": "Το AirPlay είναι ακόμη υπό ανάπτυξη", + "action.cast.scan": "Σάρωση", + "action.cast.scanning": "Γίνεται σάρωση...", + "action.createNew": "Δημιουργία νέας...", + "action.openArtworkInBrowser": "Άνοιγμα εξώφυλλου στον περιηγητή", "settings.header.general": "Γενικά", "settings.header.general.description": "Προσαρμογή γενικών ρυθμίσεων για το Cider.", "settings.option.general.language": "Γλώσσα", @@ -234,6 +273,10 @@ "settings.option.general.resumebehavior.locally.description": "Το Cider θα συνεχίσει την τελευταία συνεδρία σας αυτής της συσκευής.", "settings.option.general.resumebehavior.history": "Ιστορικό", "settings.option.general.resumebehavior.history.description": "Το Cider θα βάλει στην ουρά το τελευταίο τραγούδι από το συνολικό ιστορικό Apple Music, όλων των συσκευών σας.", + "settings.option.general.resumetabs": "Άνοιγμα καρτέλας στην εκκίνηση", + "settings.option.general.resumetabs.description": "Μπορείτε να επιλέξετε ποια καρτέλα θα εμφανίζεται όταν ανοίγετε τον Cider.", + "settings.option.general.resumetabs.dynamic": "Δυναμικό", + "settings.option.general.resumetabs.dynamic.description": "Ο Cider θα ανοίγει την καρτέλα που χρησιμοποιήσατε τελευταία.", "settings.option.general.language.main": "Γλώσσες", "settings.option.general.language.fun": "Γλώσσες για πλάκα", "settings.option.general.language.unsorted": "Αταξινόμητες", @@ -242,10 +285,34 @@ "settings.option.general.updateCider.branch.description": "Επιλέξτε τον κλάδο στον οποίο θα γίνεται η ενημέρωση του Cider", "settings.option.general.updateCider.branch.main": "Σταθερό", "settings.option.general.updateCider.branch.develop": "Αναπτυξιακό", + "settings.option.general.customizeSidebar": "Προσαρμογή στοιχείων πλευρικής μπάρας", + "settings.option.general.customizeSidebar.customize": "Προσαρμογή", + "settings.option.general.keybindings": "Συνδυασμοί πλήκτρων", + "settings.notyf.general.keybindings.update.success": "Ο συνδιασμός ενημερώθηκε με επιτυχία", + "settings.prompt.general.keybindings.update.success": "Ο συνδιασμός ενημερώθηκε με επιτυχία. Πατήστε ΟΚ για επανεκκίνηση του Cider", + "settings.option.general.keybindings.open": "Άνοιγμα", + "settings.description.search": "Αναζήτηση", + "settings.description.albums": "Άλμπουμ βιβλιοθήκης", + "settings.description.artists": "Καλλιτέχνες βιβλιοθήκης", + "settings.description.browse": "Περιήγηση", + "settings.description.private": "Εναλλαγή ιδιωτικής περιόδου λειτουργίας", + "settings.description.remote": "Web Remote", + "settings.description.audio": "Ρυθμίσεις ήχου", + "settings.description.plugins": "Μενού πρόσθετων", + "settings.description.cast": "Μετάδοση σε συσκευές", + "settings.description.settings": "Ρυθμίσεις", + "settings.description.developer": "Εργαλεία προγραμματιστή", + "settings.notyf.updateCider.update-not-available": "Δεν υπάρχει διαθέσιμη ενημέρωση", + "settings.notyf.updateCider.update-downloaded": "Έγινε λήψη της ενημέρωσης, επανεκκίνησε για εφαρμογή", + "settings.notyf.updateCider.update-error": "Σφάλμα ενημέρωσης του Cider", + "settings.notyf.updateCider.update-timeout": "Λήξη χρόνου ενημέρωσης", "settings.header.audio": "Ήχος", "settings.header.audio.description": "Προσαρμογή ρυθμίσεων ήχου για το Cider.", "settings.option.audio.volumeStep": "Βήματα έντασης", "settings.option.audio.maxVolume": "Μέγιστη ένταση", + "settings.option.audio.changePlaybackRate": "Αλλαγή ταχύτητας αναπαραγωγής", + "settings.option.audio.playbackRate": "Ταχύτητα αναπαραγωγής", + "settings.option.audio.playbackRate.change": "Αλλαγή", "settings.option.audio.quality": "Ποιότητα ήχου", "settings.header.audio.quality.hireslossless": "Lossless υψηλής ανάλυσης", "settings.header.audio.quality.hireslossless.description": "έως και 24-bit/192 kHz", @@ -258,18 +325,34 @@ "settings.option.audio.seamlessTransition": "Αδιάκοπη μετάβαση ήχου", "settings.option.audio.enableAdvancedFunctionality": "Ενεργοποίηση προηγμένης λειτουργικότητας", "settings.option.audio.enableAdvancedFunctionality.description": "Ενεργοποιώντας τη λειτουργικότητα AudioContext θα επιτρέψει σε επεκταμένες δυνατότητες ήχου όπως Κανονικοποίηση Έντασης Ήχου, Ισοσταθμιστές και Οπτικοποιητές, ωστόσο σε κάποια συστήματα μπορεί να προκαλέσει τραύλισμα ήχου.", + "settings.warn.audio.enableAdvancedFunctionality.lowcores": "Η συσκευή σου ίσως να μη μπορέσει να χειρηστεί αυτές τις δυνατότητες. Σίγουρα θέλετε να συνεχίσετε;", "settings.option.audio.audioLab": "Cider Audio Lab", - "settings.option.audio.audioLab.description": "Περιέχει μια ποικιλία από τροποποιήσεις ήχου που έγιναν από την ομάδα προγραμματιστών του Cider", + "settings.option.audio.audioLab.description": "Μια ποικιλία τροποποιήσεων ήχου από την ομάδα προγραμματιστών του Cider.", + "settings.option.audio.audioLab.subheader": "Σχεδιάστηκε από τους προγραμματιστές του Cider", "settings.warn.audioLab.withoutAF": "Το AudioContext (προηγμένη λειτουργικότητα) απαιτείται για την ενεργοποίηση του Cider Audio Laboratory.", - "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "Ένταση Analog Warmth", - "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.description": "Αλλάζει την ένταση της επεξεργασίας του Analog Warmth Module.", + "settings.option.audio.enableAdvancedFunctionality.analogWarmth": "Αναλογική ζεστασιά", + "settings.option.audio.enableAdvancedFunctionality.analogWarmth.description": "Προσομοιώνει την αναλογική ζεστασιά του Korg Nutube 6P1", + "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "Ένταση αναλογικής ζεστασιάς", + "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.description": "Αλλάζει την ένταση της επεξεργασίας της αναλογικής ζεστασιάς.", + "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.smooth": "Λείο", + "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.warm": "Θερμό", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer": "Ατμόσφαιρα ήχου", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "Εφαρμόζει μια διαφορετική ατμόσφαιρα στον ήχο.", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode": "Λειτουργία ατμόσφαιρας ήχου", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "Αλλάζει τη λειτουργία ατμόσφαιρας ήχου.", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.natural1": "Φυσική (Κανονικό)", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.natural2": "Φυσική (Υψηλό)", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.natural3": "Φυσική (Υψηλότερο)", + "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Επεξεργαστής ήχου Cider (CAP)", + "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Βελτιώνει την αντιληπτή ποιότητα ήχου χρησιμοποιώντας αλγορίθμους.", "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "Το CAP δεν είναι συμβατό με τη Χωρικοποίηση Ήχου. Παρακαλούμε απενεργοποιήστε τη Χωρικοποίηση Ήχου για να συνεχίσετε.", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "Ένταση CAP", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "Αλλάζει την ένταση της επεξεργασίας του ήχου. (Η επιθετική επεξεργασία μπορεί να αποφέρει ανεπιθύμητα αποτελέσματα)", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "Κανονική", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "Επιθετική", - "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Κανονικοποίηση Έντασης Ήχου", - "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Κανονικοποιεί την ένταση για μεμονωμένα κομμάτια για μια πιο ομοιόμορφη εμπειρία ακρόασης.", + "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "Κανονικοποίηση έντασης ήχου", + "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "Κανονικοποιεί την ένταση για μεμονωμένα κομμάτια για μια πιο ομοιόμορφη εμπειρία ακρόασης. (Δεν λειτουργεί σε κομμάτια που ανέβηκαν από χρήστες)", + "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "Διαχειρίζεται από το Audio Lab", "settings.option.audio.enableAdvancedFunctionality.audioSpatialization": "Χωρικοποίηση Ήχου", "settings.option.audio.enableAdvancedFunctionality.audioSpatialization.description": "Πιο τρισδιάστατος και χωρικοποιημένος ήχος (σημείωση: Αυτό δεν είναι Dolby Atmos)", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization": "Χωρικοποίηση Cider", @@ -281,15 +364,16 @@ "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "Η Χωρικοποίηση δεν είναι συμβατή με το CAP. Παρακαλούμε απενεργοποιήστε το CAP για να συνεχίσετε.", "settings.header.visual": "Οπτικά", "settings.header.visual.description": "Προσαρμογή οπτικών ρυθμίσεων για το Cider.", - "settings.option.visual.windowBackgroundStyle": "Στυλ Φόντου Παραθύρου", + "settings.option.visual.windowStyle": "Στυλ παραθύρου", + "settings.option.visual.windowBackgroundStyle": "Στυλ φόντου παραθύρου", "settings.header.visual.windowBackgroundStyle.none": "Κανένα", "settings.header.visual.windowBackgroundStyle.artwork": "Εξώφυλλο", "settings.header.visual.windowBackgroundStyle.image": "Εικόνα", - "settings.option.visual.animatedArtwork": "Κινούμενο Εξώφυλλο", + "settings.option.visual.animatedArtwork": "Κινούμενο εξώφυλλο", "settings.header.visual.animatedArtwork.always": "Πάντα", "settings.header.visual.animatedArtwork.limited": "Περιορισμός σε σελίδες και ειδικές καταχωρήσεις", "settings.header.visual.animatedArtwork.disable": "Απενεργοποιημένο παντού", - "settings.option.visual.animatedArtworkQuality": "Ποιότητα Κινούμενου Εξωφύλλου", + "settings.option.visual.animatedArtworkQuality": "Ποιότητα κινούμενου εξωφύλλου", "settings.header.visual.animatedArtworkQuality.low": "Χαμηλή", "settings.header.visual.animatedArtworkQuality.medium": "Μέτρια", "settings.header.visual.animatedArtworkQuality.high": "Υψηλή", @@ -300,30 +384,52 @@ "settings.option.visual.hardwareAcceleration.description": "Απαιτεί επανεκκίνηση", "settings.header.visual.hardwareAcceleration.default": "Προεπιλογή", "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", + "settings.option.visual.uiscale": "Κλίμακα διεπαφής χρήστη", "settings.header.visual.theme": "Θέμα", "settings.option.visual.theme.github.download": "Εγκατάσταση από σύνδεσμο GitHub", + "settings.option.visual.theme.github.openfolder": "Άνοιγμα φακέλου θεμάτων", "settings.option.visual.theme.github.explore": "Εξερεύνηση θεμάτων GitHub", "settings.header.visual.theme.github.page": "Θέματα από το GitHub", "settings.option.visual.theme.github.install.confirm": "Θέλετε σίγουρα να εγκαταστήσετε το θέμα {{ repo }};", "settings.prompt.visual.theme.github.URL": "Εισάγετε τον σύνδεσμο του θέματος που θέλετε να εγκαταστήσετε", "settings.notyf.visual.theme.install.success": "Το θέμα εγκαταστάθηκε με επιτυχία", "settings.notyf.visual.theme.install.error": "Αποτυχία εγκατάστασης του θέματος", + "settings.header.visual.plugin": "Πρόσθετο", + "settings.option.visual.plugin.github.download": "Εγκατάσταση από σύνδεσμο GitHub", + "settings.option.visual.plugin.github.explore": "Εξερεύνηση πρόσθετων GitHub", + "settings.header.visual.plugin.github.page": "Πρόσθετα από το GitHub", + "settings.option.visual.plugin.github.install.confirm": "Θέλετε σίγουρα να εγκαταστήσετε το πρόσθετο {{ repo }};", + "settings.prompt.visual.plugin.github.URL": "Εισάγετε τον σύνδεσμο του πρόσθετου που θέλετε να εγκαταστήσετε", + "settings.prompt.visual.plugin.github.success": "Το πρόσθετο εγκαταστάθηκε με επιτυχία, πατήστε ΟΚ για επανεκκίνηση του Cider", + "settings.notyf.visual.plugin.install.success": "Το πρόσθετο εγκαταστάθηκε με επιτυχία", + "settings.notyf.visual.plugin.install.error": "Αποτυχία εγκατάστασης του πρόσθετου", "settings.option.visual.theme.default": "Cider", "settings.option.visual.theme.dark": "Σκοτεινό", "settings.option.visual.showPersonalInfo": "Εμφάνιση προσωπικών στοιχείων", + "settings.header.window": "Παράθυρο", + "settings.header.window.description": "Προσαρμογή ρυθμίσεων παραθύρου για το Cider.", + "settings.option.window.openOnStartup": "Άνοιγμα του Cider στην εκκίνηση", + "settings.option.window.openOnStartup.hidden": "Άνοιγμα κρυμμένο", + "settings.option.window.useNativeTitleBar": "Χρήση γραμμής τίτλου του συστήμστος", + "settings.option.window.windowControlStyle": "Στυλ ελέγχου παραθύρου", + "settings.option.window.windowControlStyle.right": "Δεξιά", + "settings.option.window.windowControlStyle.left": "Αριστερά", "settings.header.lyrics": "Στίχοι", "settings.header.lyrics.description": "Προσαρμογή ρυθμίσεων στίχων για το Cider.", - "settings.option.lyrics.enableMusixmatch": "Ενεργοποίηση Στίχων Musixmatch", - "settings.option.lyrics.enableMusixmatchKaraoke": "Ενεργοποίηση Λειτουργίας Καραόκε (Musixmatch μόνο)", - "settings.option.lyrics.musixmatchPreferredLanguage": "Προτιμώμενη Γλώσσα Μετάφρασης Musixmatch", - "settings.option.lyrics.enableYoutubeLyrics": "Ενεργοποίηση Στίχων Youtube για Μουσικά Βίντεο", - "settings.header.connectivity": "Σύνδεση", - "settings.header.connectivity.description": "Προσαρμογή ρυθμίσεων σύνδεσης για το Cider.", + "settings.option.lyrics.enableMusixmatch": "Ενεργοποίηση στίχων Musixmatch", + "settings.option.lyrics.enableMusixmatchKaraoke": "Ενεργοποίηση λειτουργίας καραόκε (Musixmatch μόνο)", + "settings.option.lyrics.musixmatchPreferredLanguage": "Προτιμώμενη γλώσσα μετάφρασης Musixmatch", + "settings.option.lyrics.enableYoutubeLyrics": "Ενεργοποίηση στίχων YouTube για μουσικά βίντεο", + "settings.option.lyrics.enableQQLyrics": "Ενεργοποίηση στίχων QQ", + "settings.header.connectivity": "Συνδεσιμότητα", + "settings.header.connectivity.description": "Προσαρμογή ρυθμίσεων συνδεσιμότητας για το Cider.", + "settings.option.connectivity.playbackNotifications": "Ειδοποιήσεις αναπαραγωγής", "settings.option.connectivity.discordRPC": "Discord Rich Presence", - "settings.option.connectivity.playbackNotifications": "Ειδοποιήσεις Αναπαραγωγής", - "settings.header.connectivity.discordRPC.cider": "Εμφάνιση ως 'Cider'", - "settings.header.connectivity.discordRPC.appleMusic": "Εμφάνιση ως 'Apple Music'", - "settings.option.connectivity.discordRPC.clearOnPause": "Εκκαθάριση του Discord Rich Presence στην Παύση", + "settings.option.connectivity.discordRPC.clientName": "Τίτλος", + "settings.option.connectivity.discordRPC.clearOnPause": "Εκκαθάριση του Discord Rich Presence στην παύση", + "settings.option.connectivity.discordRPC.hideButtons": "Απόκρυψη κουμπιών του Discord Rich Presence", + "settings.option.connectivity.discordRPC.detailsFormat": "Δομή λεπτομεριών", + "settings.option.connectivity.discordRPC.stateFormat": "Δομή κατάστασης", "settings.option.connectivity.lastfmScrobble": "LastFM Scrobbling", "settings.option.connectivity.lastfmScrobble.delay": "Καθυστέρηση LastFM Scrobble (%)", "settings.option.connectivity.lastfmScrobble.nowPlaying": "Ενεργοποίηση LastFM \"Now Playing\"", @@ -334,32 +440,52 @@ "settings.option.debug.openAppData": "Άνοιγμα του φακέλου Cider", "settings.header.experimental": "Πειραματικές", "settings.header.experimental.description": "Προσαρμογή πειραματικών ρυθμίσεων για το Cider.", - "settings.option.experimental.compactUI": "Συμπαγής Διεπαφή", - "settings.option.experimental.closeButtonBehaviour": "Συμπεριφορά Κουμπιού Εξόδου", - "settings.option.experimental.closeButtonBehaviour.quit": "Έξοδος του Cider", - "settings.option.experimental.closeButtonBehaviour.minimizeTaskbar": "Ελαχιστοποίηση στη γραμμή εργασιών", - "settings.option.experimental.closeButtonBehaviour.minimizeTray": "Ελαχιστοποίηση στη γωνία γραμμής εργασιών", + "settings.option.experimental.reinstallwidevine": "Επανεγκατάσταση του WidevineCDM", + "settings.option.experimental.reinstallwidevine.confirm": "Θέλετε σίγουρα να επανεγκαταστήσετε το Widevine;", + "settings.option.experimental.unknownPlugin": "Άγνωστες πηγές", + "settings.option.experimental.unknownPlugin.description": "Να επιτρέπεται η εγκατάσταση πρόσθετων από repo εκτός του Cider Plugin Repository", + "settings.option.experimental.compactUI": "Συμπαγής διεπαφή", + "settings.option.window.close_button_hide": "Απόκρυψη εφαρμογής με το πάτημα του κουμπιού κλεισίματος", + "settings.option.experimental.inline_playlists": "Ενσωμάτωση λιστών αναπαραγωγής και άλμπουμ", + "settings.option.advanced.playlistTrackMapping": "Χαρτογράφηση κομματιών λίστών αναπαραγωγής", + "settings.option.advanced.playlistTrackMapping.description": "Ενεργοποιεί τη βαθιά σάρωση των λιστών αναπαραγωγής για να προσδιορίσει ποια κομμάτια βρίσκονται σε ποιες λίστες αναπαραγωγής. Οι χρόνοι δημιουργίας της προσωρινής μνήμης των λιστών αναπαραγωγής μπορεί να αυξηθούν σημαντικά.", + "settings.option.visual.transparent": "Διαφανές πλαίσιο", + "settings.option.visual.transparent.description": "(χρειάζεται υποστήριξη θέματος, απαιτεί επανεκκίνηση)", + "settings.header.advanced": "Για προχωρημένους", + "settings.header.connect": "Συγχρονισμός", + "settings.option.connect.link_account": "Ενεργοποίηση συγχρονισμού με Cider Connect", + "settings.option.connect.link_account.description": "Η σύνδεση του λογαριασμού σας Discord με το Cider Connect σάς επιτρέπει να αποθηκεύετε δεδομένα χρήστη, συμπεριλαμβανομένων των Ρυθμίσεων, των EQ, και άλλα. (Υπό ανάπτυξη)", "spatial.notTurnedOn": "Η Χωρικοποίηση Ήχου είναι απενεργοποιημένη. Για χρήση, παρακαλούμε ενεργοποιήστε την πρώτα.", - "spatial.spatialProperties": "Χωρικές Ιδιότητες", + "spatial.spatialProperties": "Χωρικές ιδιότητες", "spatial.width": "Πλάτος", "spatial.height": "Ύψος", "spatial.depth": "Βάθος", "spatial.gain": "Απολαβή", - "spatial.roomMaterials": "Υλικά Δωματίου", - "spatial.roomDimensions": "Διαστάσεις Δωματίου", - "spatial.roomPositions": "Θέσεις Δωματίου", - "spatial.setDimensions": "Ορισμός Διαστάσεων", - "spatial.setPositions": "Ορισμός Θέσεων", + "spatial.roomMaterials": "Υλικά δωματίου", + "spatial.roomDimensions": "Διαστάσεις δωματίου", + "spatial.roomPositions": "Θέσεις δωματίου", + "spatial.setDimensions": "Ορισμός διαστάσεων", + "spatial.setPositions": "Ορισμός θέσεων", "spatial.up": "Πάνω", "spatial.front": "Πρόσοψη", "spatial.left": "Αριστερά", "spatial.right": "Δεξιά", - "spatial.back": "Πίσω Όψη", + "spatial.back": "Πίσω όψη", "spatial.down": "Κάτω", "spatial.listener": "Ακροατής", - "spatial.audioSource": "Πηγή Ήχου", + "spatial.audioSource": "Πηγή ήχου", "settings.header.unfinished": "Ημιτελής", "remote.web.title": "Cider Remote", "remote.web.description": "Σαρώστε τον κωδικό QR για σύζευξη του Cider με το κινητό σας", + "share.platform.twitter.tweet": "Ακούστε το {{song}} στο Apple Music.\n\n{{url}}\n\n#AppleMusic #Cider", + "share.platform.twitter": "Twitter", + "share.platform.facebook": "Facebook", + "share.platform.reddit": "Reddit", + "share.platform.telegram": "Telegram", + "share.platform.whatsapp": "WhatsApp", + "share.platform.messenger": "Messenger", + "share.platform.email": "Email", + "share.platform.songLink": "Αντιγραφή με song.link", + "share.platform.clipboard": "Αντιγραφή συνδέσμου", "about.thanks": "Μεγάλα ευχαριστώ στην Ομάδα Cider Collective και σε όλους τους συνεισφέροντές μας." -} \ No newline at end of file +} diff --git a/src/i18n/en_OWO.json b/src/i18n/en_OWO.json index 940d93fd..5e03b54d 100644 --- a/src/i18n/en_OWO.json +++ b/src/i18n/en_OWO.json @@ -73,6 +73,7 @@ "term.viewAs": "View As", "term.viewAs.coverArt": "Cuvw Awt", "term.viewAs.list": "Wist", + "term.dynamic": "Dynyamic", "term.size": "Size", "term.size.normal": "Nyowmaw", "term.size.compact": "Compact", @@ -199,6 +200,9 @@ "podcast.episodes": "Episodes", "podcast.playEpisode": "Pway Episode", "podcast.website": "Podcast Website", + "action.edit": "Edit", + "action.done": "Donye", + "action.editTracklist": "Edit Twackwist", "action.addToLibrary": "Add to Wibwawy", "action.addToLibrary.success": "Added to Wibwawy", "action.addToLibrary.error": "Ewwow Adding to Wibwawy", @@ -269,6 +273,10 @@ "settings.option.general.resumebehavior.locally.description": "Cidew wiww wesume youw wast session on this machinye.", "settings.option.general.resumebehavior.history": "Histowy", "settings.option.general.resumebehavior.history.description": "Cidew wiww queue the wast song fwom youw uvwaww Appwe Music histowy, acwoss devices.", + "settings.option.general.resumetabs": "Open Tab on Waunch", + "settings.option.general.resumetabs.description": "You can choose what tab you want to open when you waunch Cidew.", + "settings.option.general.resumetabs.dynamic": "Dynyamic", + "settings.option.general.resumetabs.dynamic.description": "Cidew wiww open the tab that you wast used.", "settings.option.general.language.main": "Wanguages", "settings.option.general.language.fun": "Fun Wanguages", "settings.option.general.language.unsorted": "Unsowted", @@ -280,6 +288,8 @@ "settings.option.general.customizeSidebar": "Customize Sidebaw Items", "settings.option.general.customizeSidebar.customize": "Customize", "settings.option.general.keybindings": "Keybindings", + "settings.notyf.general.keybindings.update.success": "Keybind updated successfuwwy", + "settings.prompt.general.keybindings.update.success": "Keybind updated successfuwwy. Pwess OK to wewaunch Cidew", "settings.option.general.keybindings.open": "Open", "settings.description.search": "Seawch", "settings.description.albums": "Wibwawy Awbums", diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json index 78413968..2b1e581b 100644 --- a/src/i18n/en_US.json +++ b/src/i18n/en_US.json @@ -73,6 +73,7 @@ "term.viewAs": "View As", "term.viewAs.coverArt": "Cover Art", "term.viewAs.list": "List", + "term.dynamic": "Dynamic", "term.size": "Size", "term.size.normal": "Normal", "term.size.compact": "Compact", @@ -263,6 +264,42 @@ "action.cast.scanning": "Scanning...", "action.createNew": "Create New...", "action.openArtworkInBrowser": "Open artwork in browser", + "menubar.options.about": "About", + "menubar.options.settings": "Settings", + "menubar.options.quit": "Quit Cider", + "menubar.options.view": "View ", + "menubar.options.reload": "Reload", + "menubar.options.forcereload": "Force Reload", + "menubar.options.toggledevtools": "Toggle Developer Tools", + "menubar.options.window": "Window", + "menubar.options.minimize": "Minimize", + "menubar.options.toggleprivate": "Toggle Private Session", + "menubar.options.webremote": "Web Remote", + "menubar.options.audio": "Audio Settings", + "menubar.options.plugins": "Plu-gins Menu", + "menubar.options.controls": "Controls", + "menubar.options.next": "Next", + "menubar.options.playpause": "Play/Pause", + "menubar.options.previous": "Previous", + "menubar.options.volumeup": "Volume Up", + "menubar.options.volumedown": "Volume Down", + "menubar.options.browse": "Browse", + "menubar.options.artists": "Artists", + "menubar.options.search": "Search", + "menubar.options.albums": "Albums", + "menubar.options.cast": "Cast To Devices", + "menubar.options.account": "Account", + "menubar.options.accountsettings": "Account Settings", + "menubar.options.signout": "Sign Out", + "menubar.options.support": "Support", + "menubar.options.discord": "Discord", + "menubar.options.github": "GitHub Wiki", + "menubar.options.report": "Report a...", + "menubar.options.bug": "Bug", + "menubar.options.feature": "Feature Request", + "menubar.options.trans": "Translation Report/Request", + "menubar.options.license": "View License", + "menubar.options.conf": "Open Configuration File in Editor", "settings.header.general": "General", "settings.header.general.description": "Adjust the general settings for Cider.", "settings.option.general.language": "Language", @@ -329,6 +366,7 @@ "settings.option.audio.audioLab.description": "An assortment of in-house developed audio effects for Cider.", "settings.option.audio.audioLab.subheader": "Designed by Cider Acoustic Technologies in California", "settings.warn.audioLab.withoutAF": "AudioContext (Advanced Functionality) is required to enable Cider Audio Laboratory.", + "settings.warn.enableAdvancedFunctionality": "AudioContext (Advanced Functionality) is required to enable this feature.", "settings.option.audio.enableAdvancedFunctionality.analogWarmth": "Analog Warmth", "settings.option.audio.enableAdvancedFunctionality.analogWarmth.description": "Simulates the analog warmth modelled after the Korg Nutube 6P1", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "Analog Warmth intensity", @@ -391,6 +429,13 @@ "settings.header.visual.theme.github.page": "Themes from GitHub", "settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?", "settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install", + "settings.prompt.visual.theme.uninstallTheme": "Are you sure you want to uninstall {{ theme }}?", + "settings.option.visual.theme.checkForUpdates": "Check for updates", + "settings.option.visual.theme.manageStyles": "Manage Styles", + "settings.option.visual.theme.uninstall": "Uninstall", + "settings.option.visual.theme.viewInfo": "View Info", + "settings.option.visual.theme.github.available": "Available", + "settings.option.visual.theme.github.applied": "Applied", "settings.notyf.visual.theme.install.success": "Theme installed successfully", "settings.notyf.visual.theme.install.error": "Theme installation failed", "settings.header.visual.plugin": "Plugin", diff --git a/src/i18n/es_ES.json b/src/i18n/es_ES.json index e10bab53..808004fd 100644 --- a/src/i18n/es_ES.json +++ b/src/i18n/es_ES.json @@ -73,6 +73,7 @@ "term.viewAs": "Ver como", "term.viewAs.coverArt": "Portada", "term.viewAs.list": "Lista", + "term.dynamic": "Dinámico", "term.size": "Tamaño", "term.size.normal": "Normal", "term.size.compact": "Compacto", @@ -199,6 +200,9 @@ "podcast.episodes": "Episodios", "podcast.playEpisode": "Reproducir Episodio", "podcast.website": "Sitio web de Podcasts", + "action.edit": "Editar", + "action.done": "Hecho", + "action.editTracklist": "Edit Tracklist", "action.addToLibrary": "Agregar a la Biblioteca", "action.addToLibrary.success": "Agregado a la Biblioteca", "action.addToLibrary.error": "Error al agregar a la Biblioteca", @@ -260,6 +264,42 @@ "action.cast.scanning": "Escaneando...", "action.createNew": "Crear Nuevo...", "action.openArtworkInBrowser": "Abrir Ilustración en el navegador", + "menubar.options.about": "Acerca de", + "menubar.options.settings": "Ajustes", + "menubar.options.quit": "Salir", + "menubar.options.view": "Ver", + "menubar.options.reload": "Recargar", + "menubar.options.forcereload": "Forzar Recarga", + "menubar.options.toggledevtools": "Herramientas de Desarrollo", + "menubar.options.window": "Ventana", + "menubar.options.minimize": "Minimizar", + "menubar.options.toggleprivate": "Cambiar Sesión Privada", + "menubar.options.webremote": "Web Remoto", + "menubar.options.audio": "Configuraciones de Audio", + "menubar.options.plugins": "Menu de Plu-gins", + "menubar.options.controls": "Controles", + "menubar.options.next": "Siguiente", + "menubar.options.playpause": "Reproducir/Pausar", + "menubar.options.previous": "Anterior", + "menubar.options.volumeup": "Subir Volumen", + "menubar.options.volumedown": "Bajar Volumen", + "menubar.options.browse": "Explorar", + "menubar.options.artists": "Artistas", + "menubar.options.search": "Buscar", + "menubar.options.albums": "Álbumes", + "menubar.options.cast": "Transmitir a Dispositivos", + "menubar.options.account": "Cuenta", + "menubar.options.accountsettings": "Ajustes de Cuenta", + "menubar.options.signout": "Cerrar Sesión", + "menubar.options.support": "Soporte", + "menubar.options.discord": "Discord", + "menubar.options.github": "GitHub Wiki", + "menubar.options.report": "Reportar a...", + "menubar.options.bug": "Bug", + "menubar.options.feature": "Solicitud de características", + "menubar.options.trans": "Solicitud de Informe/Traducción", + "menubar.options.license": "Ver licencia", + "menubar.options.conf": "Abrir archivo de configuración en el editor", "settings.header.general": "General", "settings.header.general.description": "Ajuste la configuración general de Cider.", "settings.option.general.language": "Idioma", @@ -269,6 +309,10 @@ "settings.option.general.resumebehavior.locally.description": "Cider reanudará su última sesión en esta PC.", "settings.option.general.resumebehavior.history": "Histórico", "settings.option.general.resumebehavior.history.description": "Cider pondrá en cola la última canción de su historial general de Apple Music, en todos sus dispositivos.", + "settings.option.general.resumetabs" : "Abrir pestaña al iniciar", + "settings.option.general.resumetabs.description" : "Puede elegir qué pestaña desea abrir cuando inicie Cider.", + "settings.option.general.resumetabs.dynamic" : "Dinámico", + "settings.option.general.resumetabs.dynamic.description" : "Cider abrirá la pestaña que utilizó por última vez.", "settings.option.general.language.main": "Idiomas", "settings.option.general.language.fun": "Idiomas Fun (Parodias)", "settings.option.general.language.unsorted": "Sin Clasificar", @@ -280,6 +324,8 @@ "settings.option.general.customizeSidebar": "Personalizar elementos de la barra lateral", "settings.option.general.customizeSidebar.customize": "Personalizar", "settings.option.general.keybindings": "Combinaciones de Teclas", + "settings.notyf.general.keybindings.update.success": "Combinación de teclas actualizada correctamente", + "settings.prompt.general.keybindings.update.success": "La combinación de teclas se actualizó correctamente. Pulsa OK para reiniciar Cider", "settings.option.general.keybindings.open": "Abrir", "settings.description.search": "Buscar", "settings.description.albums": "Álbumes de la biblioteca", @@ -320,6 +366,7 @@ "settings.option.audio.audioLab.description": "Una variedad de efectos de audio desarrollados internamente para Cider.", "settings.option.audio.audioLab.subheader": "Designed by Cider Acoustic Technologies in California", "settings.warn.audioLab.withoutAF": "Se requiere AudioContext (funcionalidad avanzada) para habilitar Laboratorio de audio de Cider.", + "settings.warn.enableAdvancedFunctionality": "AudioContext (funcionalidad avanzada) es necesaria para habilitar esta característica.", "settings.option.audio.enableAdvancedFunctionality.analogWarmth": "Calidez analógica", "settings.option.audio.enableAdvancedFunctionality.analogWarmth.description": "Simula la calidez analógica inspirada en el Korg Nutube 6P1", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "Intensidad de calidez analógica", @@ -382,6 +429,13 @@ "settings.header.visual.theme.github.page": "Temas de GitHub", "settings.option.visual.theme.github.install.confirm": "¿Está seguro de que desea instalar {{ repo }}?", "settings.prompt.visual.theme.github.URL": "Introduce la URL del tema que quieres instalar", + "settings.prompt.visual.theme.uninstallTheme": "¿Estas seguro que lo quieres desinstalar {{ theme }}?", + "settings.option.visual.theme.checkForUpdates": "Buscar actualizaciones", + "settings.option.visual.theme.manageStyles": "Gestionar Estilos", + "settings.option.visual.theme.uninstall": "Desinstalar", + "settings.option.visual.theme.viewInfo": "Ver Información", + "settings.option.visual.theme.github.available": "Disponible", + "settings.option.visual.theme.github.applied": "Aplicado", "settings.notyf.visual.theme.install.success": "Tema Instalado Correctamente", "settings.notyf.visual.theme.install.error": "La Instalación del Tema Falló", "settings.header.visual.plugin": "Plugin", @@ -410,6 +464,7 @@ "settings.option.lyrics.enableMusixmatchKaraoke": "Habilitar el modo Karaoke (solo Musixmatch)", "settings.option.lyrics.musixmatchPreferredLanguage": "Idioma preferido de traducción de Musixmatch", "settings.option.lyrics.enableYoutubeLyrics": "Habilitar letras de YouTube para videos musicales", + "settings.option.lyrics.enableQQLyrics": "Habilitar letras de QQ", "settings.header.connectivity": "Conectividad", "settings.header.connectivity.description": "Ajuste la configuración de conectividad para Cider.", "settings.option.connectivity.playbackNotifications": "Notificaciones de Reproducción", @@ -442,7 +497,9 @@ "settings.option.visual.transparent.description": "Marco transparente (necesita compatibilidad con temas, requiere reiniciar)", "settings.header.advanced": "Avanzado", "settings.header.connect": "Conectar", - "spatial.notTurnedOn": "La espacialización de audio está deshabilitada. Para usar, habilítelo primero.", + "settings.option.connect.link_account": "Habilitar sincronización con Cider Connect", + "settings.option.connect.link_account.description": "Vincular su cuenta de Discord con Cider Connect le permite almacenar datos de usuario que incluyen configuraciones, ecualizadores y eventualmente más, una vez que haya terminado. (Trabajo en progreso)", + "spatial.notTurnedOn": "La Espacialización de audio está deshabilitada. Para usar, habilítelo primero.", "spatial.spatialProperties": "Propiedades de Espacialización", "spatial.width": "Ancho", "spatial.height": "Alto", diff --git a/src/i18n/hu_HU.json b/src/i18n/hu_HU.json index f1c47c8b..8108143d 100644 --- a/src/i18n/hu_HU.json +++ b/src/i18n/hu_HU.json @@ -378,6 +378,8 @@ "settings.header.visual.theme.github.page": "Témák a GitHub-ról", "settings.option.visual.theme.github.install.confirm": "Biztosan szeretnéd telepíteni a(z) {{ repo }} témát?", "settings.prompt.visual.theme.github.URL": "Add meg a telepítendő téma URL-jét", + "settings.option.visual.theme.github.available": "Elérhető", + "settings.option.visual.theme.github.applied": "Alkalmazva", "settings.notyf.visual.theme.install.success": "Téma sikeresen telepítve", "settings.notyf.visual.theme.install.error": "Sikertelen volt a téma telepítése", "settings.header.visual.plugin": "Plugin", @@ -406,6 +408,7 @@ "settings.option.lyrics.enableMusixmatchKaraoke": "Karaoke mód bekapcsolása (Csak MusixMatch)", "settings.option.lyrics.musixmatchPreferredLanguage": "MusixMatch fordítás nyelve", "settings.option.lyrics.enableYoutubeLyrics": "YouTube dalszövegek engedélyezése a zenei videóknál", + "settings.option.lyrics.enableQQLyrics": "QQLyrics dalszövegek engedélyezése", "settings.header.connectivity": "Csatlakozások", "settings.header.connectivity.description": "A Cider csatlakozás beállításainak módosítása.", "settings.option.connectivity.playbackNotifications": "Lejátszási értesítések", diff --git a/src/i18n/source/en_US.json b/src/i18n/source/en_US.json index 71d7a3d8..2b1e581b 100644 --- a/src/i18n/source/en_US.json +++ b/src/i18n/source/en_US.json @@ -264,6 +264,42 @@ "action.cast.scanning": "Scanning...", "action.createNew": "Create New...", "action.openArtworkInBrowser": "Open artwork in browser", + "menubar.options.about": "About", + "menubar.options.settings": "Settings", + "menubar.options.quit": "Quit Cider", + "menubar.options.view": "View ", + "menubar.options.reload": "Reload", + "menubar.options.forcereload": "Force Reload", + "menubar.options.toggledevtools": "Toggle Developer Tools", + "menubar.options.window": "Window", + "menubar.options.minimize": "Minimize", + "menubar.options.toggleprivate": "Toggle Private Session", + "menubar.options.webremote": "Web Remote", + "menubar.options.audio": "Audio Settings", + "menubar.options.plugins": "Plu-gins Menu", + "menubar.options.controls": "Controls", + "menubar.options.next": "Next", + "menubar.options.playpause": "Play/Pause", + "menubar.options.previous": "Previous", + "menubar.options.volumeup": "Volume Up", + "menubar.options.volumedown": "Volume Down", + "menubar.options.browse": "Browse", + "menubar.options.artists": "Artists", + "menubar.options.search": "Search", + "menubar.options.albums": "Albums", + "menubar.options.cast": "Cast To Devices", + "menubar.options.account": "Account", + "menubar.options.accountsettings": "Account Settings", + "menubar.options.signout": "Sign Out", + "menubar.options.support": "Support", + "menubar.options.discord": "Discord", + "menubar.options.github": "GitHub Wiki", + "menubar.options.report": "Report a...", + "menubar.options.bug": "Bug", + "menubar.options.feature": "Feature Request", + "menubar.options.trans": "Translation Report/Request", + "menubar.options.license": "View License", + "menubar.options.conf": "Open Configuration File in Editor", "settings.header.general": "General", "settings.header.general.description": "Adjust the general settings for Cider.", "settings.option.general.language": "Language", @@ -330,6 +366,7 @@ "settings.option.audio.audioLab.description": "An assortment of in-house developed audio effects for Cider.", "settings.option.audio.audioLab.subheader": "Designed by Cider Acoustic Technologies in California", "settings.warn.audioLab.withoutAF": "AudioContext (Advanced Functionality) is required to enable Cider Audio Laboratory.", + "settings.warn.enableAdvancedFunctionality": "AudioContext (Advanced Functionality) is required to enable this feature.", "settings.option.audio.enableAdvancedFunctionality.analogWarmth": "Analog Warmth", "settings.option.audio.enableAdvancedFunctionality.analogWarmth.description": "Simulates the analog warmth modelled after the Korg Nutube 6P1", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "Analog Warmth intensity", @@ -392,6 +429,13 @@ "settings.header.visual.theme.github.page": "Themes from GitHub", "settings.option.visual.theme.github.install.confirm": "Are you sure you want to install {{ repo }}?", "settings.prompt.visual.theme.github.URL": "Enter the URL of the theme you want to install", + "settings.prompt.visual.theme.uninstallTheme": "Are you sure you want to uninstall {{ theme }}?", + "settings.option.visual.theme.checkForUpdates": "Check for updates", + "settings.option.visual.theme.manageStyles": "Manage Styles", + "settings.option.visual.theme.uninstall": "Uninstall", + "settings.option.visual.theme.viewInfo": "View Info", + "settings.option.visual.theme.github.available": "Available", + "settings.option.visual.theme.github.applied": "Applied", "settings.notyf.visual.theme.install.success": "Theme installed successfully", "settings.notyf.visual.theme.install.error": "Theme installation failed", "settings.header.visual.plugin": "Plugin", diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json index 356b70c1..ec6570d0 100644 --- a/src/i18n/zh_CN.json +++ b/src/i18n/zh_CN.json @@ -22,6 +22,7 @@ "term.login": "登录", "term.about": "关于", "term.privateSession": "私人聆听", + "term.lyrics": "歌词", "term.queue": "待播清单", "term.history": "历史记录", "term.miniplayer": "迷你播放器", @@ -115,7 +116,11 @@ "term.contributors": "贡献者", "term.equalizer": "均衡器", "term.reset": "重置", - "term.tracks": "首歌曲", + "term.track": { + "one": "首歌曲", + "other": "首歌曲" + }, + "term.tracks": "歌曲", "term.videos": "音乐视频", "term.menu": "菜单", "term.check": "检查", @@ -161,6 +166,9 @@ "podcast.episodes": "单集", "podcast.playEpisode": "播放单集", "podcast.website": "Podcast 网站", + "action.edit": "编辑", + "action.done": "完成", + "action.editTracklist": "编辑歌曲清单", "action.addToLibrary": "加入资料库", "action.addToLibrary.success": "成功加入资料库", "action.addToLibrary.error": "加入资料库的过程发生了错误", @@ -187,6 +195,7 @@ "action.startRadio": "开始电台", "action.goToArtist": "前往艺人", "action.goToAlbum": "前往专辑", + "action.showInAppleMusic": "显示于 Apple Music", "action.moveToTop": "移到顶部", "action.share": "分享歌曲", "action.rename": "重命名", @@ -203,11 +212,19 @@ "action.showAlbum": "显示专辑", "action.tray.minimize": "最小化", "action.tray.quit": "退出", - "action.update": "更新", "action.copy": "复制", "action.newpreset": "新建默认...", "action.deletepreset": "删除默认", + "action.open": "打开", + "action.cast.chromecast": "Chromecast", + "action.cast.todevices": "投射到设备", + "action.cast.stop": "停止投射到所有设备", + "action.cast.airplay": "AirPlay", + "action.cast.airplay.underdevelopment": "AirPlay 仍处于开发阶段中,敬请期待。", + "action.cast.scan": "搜索", + "action.cast.scanning": "搜索中...", + "action.createNew": "添加...", "settings.header.general": "通用", "settings.header.general.description": "调整 Cider 的通用设置", "settings.option.audio.volumeStep": "音量改变量", @@ -218,6 +235,10 @@ "settings.option.general.resumebehavior.locally.description": "Cider 将还原你在这台电脑上的最后一次操作。", "settings.option.general.resumebehavior.history": "历史", "settings.option.general.resumebehavior.history.description": "Cider 将跨设备将你的整个 Apple Music 历史记录中的最后一首歌曲排队入列。", + "settings.option.general.resumetabs": "启动时打开的选项页面", + "settings.option.general.resumetabs.description": "你可以选择启动 Cider 时要默认打开的页面。", + "settings.option.general.resumetabs.dynamic": "动态", + "settings.option.general.resumetabs.dynamic.description": "Cider 将自动打开你上次停留的页面。", "settings.option.general.language": "语言", "settings.option.general.language.main": "语言", "settings.option.general.language.fun": "恶搞语言", @@ -229,6 +250,8 @@ "settings.option.general.updateCider.branch.develop": "测试(Develop)", "settings.option.general.customizeSidebar": "自定义侧边栏的功能", "settings.option.general.customizeSidebar.customize": "自定义", + "settings.option.general.keybindings": "快捷操作键", + "settings.option.general.keybindings.open": "打开", "settings.notyf.updateCider.update-not-available": "没有可用的更新", "settings.notyf.updateCider.update-downloaded": "更新已成功下载,重启后进行更新", "settings.notyf.updateCider.update-error": "更新时,发生错误", @@ -250,20 +273,27 @@ "settings.option.audio.audioLab": "Cider 音频实验室", "settings.option.audio.audioLab.description": "包含由 Cider 开发团队进行的各种音频优化功能。", "settings.warn.audioLab.withoutAF": "使用 Cider 音频实验室需要打开进阶音频功能才可使用。" , -"settings.option.audio.enableAdvancedFunctionality.analogWarmth": "模拟温暖", + "settings.option.audio.enableAdvancedFunctionality.analogWarmth": "模拟温暖", "settings.option.audio.enableAdvancedFunctionality.analogWarmth.description": "以 Korg Nutube 6P1 为蓝本的模拟温暖。", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "模拟温暖强度", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.description": "改变模拟温暖模组处理的强度。", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.smooth": "温和", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity.warm": "温暖", -"settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider 数码增强音频处理™️", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer": "Cider 音乐气氛实现器™️", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizer.description": "以最先进的音频置为蓝本,实现不同的音乐气氛。", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode": "Cider 音乐气氛™️模式", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "更改气氛实现器模块的操作模式。", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.natural1": "自然(标准)", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.natural2": "自然(高)", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.natural3": "自然(增强)", + "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider 数码增强音频处理™️", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "通过人类的听力心理学模型和 AAC 编码特色的即时算法,强化 256 kbps AAC 音频的感知音频质量。", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "数码增强音频处理设置", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "将更改音频处理的激进/振奋程度(增强选项有可能会引起杂讯)。", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "标准", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "增强", "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "音量平衡", -"settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自动将歌曲播放音量调整到相同水平,享受更舒适的聆听体验。", + "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自动将歌曲播放音量调整到相同水平,享受更舒适的聆听体验。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "此功能由音频实验室管理", "settings.option.audio.enableAdvancedFunctionality.audioSpatialization": "空间音频", "settings.option.audio.enableAdvancedFunctionality.audioSpatialization.description": "将音频进行空间化处理来制造一个更立体的聆听体验(注:此功能不是官方的杜比全景声)。", @@ -274,6 +304,7 @@ "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "标准", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "发烧友", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "音频空间无法与 CAP 相容,请关闭 CAP 在进行操作。", + "settings.option.visual.uiscale": "UI界面大小", "settings.header.visual": "外观", "settings.header.visual.description": "调整 Cider 的外观", "settings.option.visual.windowBackgroundStyle": "窗口背景样式", @@ -312,7 +343,7 @@ "settings.notyf.visual.plugin.install.error": "插件安装失败", "settings.option.visual.theme.default": "默认", "settings.option.visual.theme.dark": "午夜黑", - "settings.option.visual.showPersonalInfo": "显示个人资料", + "settings.option.visual.showPersonalInfo": "显示个人信息", "settings.header.window": "窗口", "settings.header.window.description": "调整 Cider 的窗口设置", "settings.option.window.openOnStartup": "开机时,自动运行 Cider ", @@ -327,19 +358,21 @@ "settings.option.lyrics.enableMusixmatchKaraoke": "启用卡拉 OK 模式(仅 Musixmatch)", "settings.option.lyrics.musixmatchPreferredLanguage": "Musixmatch 歌词语言偏好", "settings.option.lyrics.enableYoutubeLyrics": "播放 MV 时使用 YouTube 歌词", + "settings.option.lyrics.enableQQLyrics": "启用 QQ 音乐的歌词", "settings.header.connectivity": "外部连接", "settings.header.connectivity.description": "调整 Cider 与外部应用的交互设置", "settings.option.connectivity.discordRPC": "Discord 动态", "settings.option.connectivity.playbackNotifications": "歌曲播放通知", - "settings.header.connectivity.discordRPC.cider": "显示正在使用 'Cider'", - "settings.header.connectivity.discordRPC.appleMusic": "显示正在使用 'Apple Music'", + "settings.option.connectivity.discordRPC.clientName": "应用程序名称", "settings.option.connectivity.discordRPC.clearOnPause": "暂停时清除 Discord 动态", "settings.option.connectivity.discordRPC.hideButtons": "隐藏 Discord 动态上的按钮", - "settings.option.connectivity.lastfmScrobble": "LastFM Scrobbling 记录", - "settings.option.connectivity.lastfmScrobble.delay": "LastFM Scrobble 延迟 (%)", - "settings.option.connectivity.lastfmScrobble.nowPlaying": "打开 LastFM 正在播放", - "settings.option.connectivity.lastfmScrobble.removeFeatured": "从歌名里去除合作者 (LastFM)", - "settings.option.connectivity.lastfmScrobble.filterLoop": "不记录单曲循环 (LastFM)", + "settings.option.connectivity.discordRPC.detailsFormat": "详细信息格式", + "settings.option.connectivity.discordRPC.stateFormat": "动态格式", + "settings.option.connectivity.lastfmScrobble": "Last.FM 音乐记录", + "settings.option.connectivity.lastfmScrobble.delay": "Last.FM 歌曲追踪延迟 (%)", + "settings.option.connectivity.lastfmScrobble.nowPlaying": "打开 Last.FM 正在聆听", + "settings.option.connectivity.lastfmScrobble.removeFeatured": "从歌名里去除合作者 (Last.FM)", + "settings.option.connectivity.lastfmScrobble.filterLoop": "不记录单曲循环 (Last.FM)", "settings.header.debug": "Debug", "settings.option.debug.copy_log": "拷贝日志至剪贴板", "settings.option.debug.openAppData": "打开 Cider 程序文件夹", diff --git a/src/i18n/zh_TW.json b/src/i18n/zh_TW.json index 8c5083cc..0062f872 100644 --- a/src/i18n/zh_TW.json +++ b/src/i18n/zh_TW.json @@ -151,6 +151,9 @@ "podcast.episodes": "單集", "podcast.playEpisode": "播放單集", "podcast.website": "Podcast 網站", + "action.edit": "編輯", + "action.done": "完成", + "action.editTracklist": "編輯歌曲清單", "action.addToLibrary": "加入到資料庫", "action.addToLibrary.success": "成功加入資料庫", "action.addToLibrary.error": "加入資料庫時,發生錯誤", @@ -215,6 +218,10 @@ "settings.option.general.resumebehavior.locally.description": "Cider 將還原你在這台電腦上的最後一次操作。", "settings.option.general.resumebehavior.history": "歷史", "settings.option.general.resumebehavior.history.description": "Cider 將跨裝置將你的整個 Apple Music 歷史記錄中的最後一首歌曲排隊入列。", + "settings.option.general.resumetabs": "啟動時打開的選項頁面", + "settings.option.general.resumetabs.description": "你可以選擇啟動 Cider 時要預設打開的頁面。", + "settings.option.general.resumetabs.dynamic": "自動", + "settings.option.general.resumetabs.dynamic.description": "Cider 將自動打開你上次停留的頁面。", "settings.option.general.language.main": "語言", "settings.option.general.language.fun": "特殊語言", "settings.option.general.language.unsorted": "未分類", @@ -345,8 +352,8 @@ "settings.option.connectivity.discordRPC.hideButtons": "隱藏 Discord 動態上的按鈕", "settings.option.connectivity.discordRPC.detailsFormat": "詳細資訊格式", "settings.option.connectivity.discordRPC.stateFormat": "狀態格式", - "settings.option.connectivity.lastfmScrobble": "Last.FM Scrobbling 記錄", - "settings.option.connectivity.lastfmScrobble.delay": "Last.FM Scrobble 延遲 (%)", + "settings.option.connectivity.lastfmScrobble": "Last.FM 音樂記錄", + "settings.option.connectivity.lastfmScrobble.delay": "Last.FM 歌曲追蹤延遲 (%)", "settings.option.connectivity.lastfmScrobble.nowPlaying": "開啟 Last.FM 正在聆聽", "settings.option.connectivity.lastfmScrobble.removeFeatured": "從歌名中移除客串藝人 (Last.FM)", "settings.option.connectivity.lastfmScrobble.filterLoop": "不記錄單曲循環 (Last.FM)", diff --git a/src/main/base/app.ts b/src/main/base/app.ts index 3ab49a66..9ae6b6a9 100644 --- a/src/main/base/app.ts +++ b/src/main/base/app.ts @@ -212,7 +212,6 @@ export class AppEvents { * Handles the creation of a new instance of the app */ private InstanceHandler() { - // Detects of an existing instance is running (So if the lock has been achieved, no existing instance has been found) const gotTheLock = app.requestSingleInstanceLock() diff --git a/src/main/base/browserwindow.ts b/src/main/base/browserwindow.ts index afb38607..0c785691 100644 --- a/src/main/base/browserwindow.ts +++ b/src/main/base/browserwindow.ts @@ -4,7 +4,18 @@ import * as windowStateKeeper from "electron-window-state"; import * as express from "express"; import * as getPort from "get-port"; import {search} from "youtube-search-without-api-key"; -import {existsSync, rmSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync} from "fs"; +import { + existsSync, + rmSync, + mkdirSync, + readdirSync, + readFileSync, + writeFileSync, + statSync, + unlinkSync, + rmdirSync, + lstatSync +} from "fs"; import {Stream} from "stream"; import {networkInterfaces} from "os"; import * as mm from 'music-metadata'; @@ -37,6 +48,7 @@ export class BrowserWindow { platform: process.platform, dev: app.isPackaged, osRelease: os.release(), + updatable: !process.windowsStore || !process.mas, components: [ "pages/podcasts", "pages/apple-account-settings", @@ -45,6 +57,7 @@ export class BrowserWindow { "pages/library-artists", "pages/browse", "pages/settings", + "pages/installed-themes", "pages/listen_now", "pages/home", "pages/artist-feed", @@ -95,6 +108,7 @@ export class BrowserWindow { "components/fullscreen", "components/miniplayer", "components/castmenu", + "components/airplay-modal", "components/artist-chip", "components/hello-world", "components/inline-collection-list", @@ -176,6 +190,10 @@ export class BrowserWindow { page: "settings", component: ``, condition: `page == 'settings'` + }, { + page: "installed-themes", + component: ``, + condition: `page == 'installed-themes'` }, { page: "search", component: ``, @@ -254,8 +272,10 @@ export class BrowserWindow { }, }; + public static watcher: any; + StartWatcher(path: string) { - const watcher = watch(path, { + BrowserWindow.watcher = watch(path, { ignored: /[\/\\]\./, persistent: true }); @@ -265,7 +285,7 @@ export class BrowserWindow { } // Declare the listeners of the watcher - watcher + BrowserWindow.watcher .on('add', function (path: string) { // console.log('File', path, 'has been added'); }) @@ -292,6 +312,10 @@ export class BrowserWindow { }); } + async StopWatcher() { + await BrowserWindow.watcher.close(); + } + /** * Creates the browser window * @generator @@ -311,6 +335,8 @@ export class BrowserWindow { }); this.options.width = windowState.width; this.options.height = windowState.height; + this.options.x = windowState.x; + this.options.y = windowState.y; switch (process.platform) { default: @@ -696,6 +722,50 @@ export class BrowserWindow { }; }) + ipcMain.handle("uninstall-theme", async (event, path) => { + await this.StopWatcher() + const themesDir = utils.getPath("themes") + // validate the path is in the themes directory + try { + if (path.startsWith(themesDir)) { + // get last dir in path, can be either / or \ and may have a trailing slash + const themeName = path.split(/[\\\/]/).pop() + if (themeName == "Themes" || themeName == "themes") { + BrowserWindow.win.webContents.send("theme-uninstalled", { + path: path, + status: 3 + }); + return + } + // if path is directory, delete it + if (lstatSync(path).isDirectory()) { + await rmdirSync(path, {recursive: true}); + } else { + // if path is file, delete it + await unlinkSync(path); + } + // return the path + BrowserWindow.win.webContents.send("theme-uninstalled", { + path: path, + status: 0 + }); + } else { + BrowserWindow.win.webContents.send("theme-uninstalled", { + path: path, + status: 1 + }); + } + } catch (e: any) { + BrowserWindow.win.webContents.send("theme-uninstalled", { + path: path, + message: e.message, + status: 2 + }); + } + + this.StartWatcher(utils.getPath('themes')) + }) + ipcMain.handle("reinstall-widevine-cdm", () => { // remove WidevineCDM from appdata folder const widevineCdmPath = join(app.getPath("userData"), "./WidevineCdm"); @@ -811,7 +881,7 @@ export class BrowserWindow { } else if (statSync(join(utils.getPath("themes"), file)).isDirectory()) { let subFiles = readdirSync(join(utils.getPath("themes"), file)); for (let subFile of subFiles) { - if (subFile.endsWith(".less")) { + if (subFile.endsWith("index.less")) { themes.push(join(file, subFile)); } } @@ -830,15 +900,20 @@ export class BrowserWindow { themePath = themePath.slice(0, -10); } if (existsSync(join(themePath, "theme.json"))) { - let themeJson = JSON.parse(readFileSync(join(themePath, "theme.json"), "utf8")); - themeObjects.push({ - name: themeJson.name || themeName, - description: themeJson.description || themeDescription, - path: themePath, - file: theme, - github_repo: themeJson.github_repo || "", - commit: themeJson.commit || "" - }); + try { + let themeJson = JSON.parse(readFileSync(join(themePath, "theme.json"), "utf8")); + themeObjects.push({ + name: themeJson.name || themeName, + description: themeJson.description || themeDescription, + path: themePath, + file: theme, + github_repo: themeJson.github_repo || "", + commit: themeJson.commit || "", + pack: themeJson.pack || false, + }); + } catch (e) { + console.error(e); + } } else { themeObjects.push({ name: themeName, @@ -846,7 +921,8 @@ export class BrowserWindow { path: themePath, file: theme, github_repo: "", - commit: "" + commit: "", + pack: false }); } } @@ -897,6 +973,10 @@ export class BrowserWindow { event.returnValue = process.platform; }); + ipcMain.on("get-port", (event) => { + event.returnValue = this.clientPort; + }); + ipcMain.on("is-dev", (event) => { event.returnValue = this.devMode; }); @@ -965,6 +1045,11 @@ export class BrowserWindow { BrowserWindow.win.setResizable(!lock); }); + // Move window + ipcMain.on("windowmove", (_event, x, y) => { + BrowserWindow.win.setBounds({x, y}); + }); + //Fullscreen ipcMain.on('setFullScreen', (_event, flag) => { BrowserWindow.win.setFullScreen(flag) @@ -1209,16 +1294,27 @@ export class BrowserWindow { shell.openPath(app.getPath('userData')); }); + + //#region Cider Connect ipcMain.on('cc-auth', (_event) => { shell.openExternal(String(utils.getStoreValue('cc_authURL'))); }); - ipcMain.on('cc-logout', (_event) => { + ipcMain.on('cc-logout', (_event) => { //Make sure to update the default store utils.setStoreValue('connectUser', { - auth: null + "auth": null, + "sync": { + themes: false, + plugins: false, + settings: false, + } }); utils.getWindow().reload(); }); + + ipcMain.on('cc-push', (_event) => { + utils.pushStoreToConnect(); + }) /* ********************************************************************************************* * Window Events * **********************************************************************************************/ @@ -1262,7 +1358,7 @@ export class BrowserWindow { BrowserWindow.win.webContents.executeJavaScript(` window.localStorage.setItem("currentTrack", JSON.stringify(app.mk.nowPlayingItem)); window.localStorage.setItem("currentTime", JSON.stringify(app.mk.currentPlaybackTime)); - window.localStorage.setItem("currentQueue", JSON.stringify(app.mk.queue.items)); + window.localStorage.setItem("currentQueue", JSON.stringify(app.mk.queue._unplayedQueueItems)); ipcRenderer.send('stopGCast','');`) BrowserWindow.win.destroy(); } diff --git a/src/main/base/plugins.ts b/src/main/base/plugins.ts index a09ee3e9..d51e8a85 100644 --- a/src/main/base/plugins.ts +++ b/src/main/base/plugins.ts @@ -26,9 +26,9 @@ export class Plugins { } public static getPluginFromMap(plugin: string): any { - if(Plugins.PluginMap[plugin]) { + if (Plugins.PluginMap[plugin]) { return Plugins.PluginMap[plugin]; - }else{ + } else { return plugin; } } diff --git a/src/main/base/store.ts b/src/main/base/store.ts index 1ffbc502..de8590a5 100644 --- a/src/main/base/store.ts +++ b/src/main/base/store.ts @@ -1,7 +1,7 @@ import * as ElectronStore from 'electron-store'; import * as electron from "electron"; import {app} from "electron"; - +import fetch from "electron-fetch"; export class Store { static cfg: ElectronStore; @@ -12,7 +12,7 @@ export class Store { }, "general": { "close_button_hide": false, - "discord_rpc": { + "discordrpc": { "enabled": false, "client": "Cider", "clear_on_pause": true, @@ -52,11 +52,11 @@ export class Store { "keybindings": { "search": [ process.platform == "darwin" ? "Command" : "Control", - "S" + "F" ], "albums": [ process.platform == "darwin" ? "Command" : "Control", - "F" + "S" ], "artists": [ process.platform == "darwin" ? "Command" : "Control", @@ -123,6 +123,8 @@ export class Store { "quality": "HIGH", "seamless_audio": true, "normalization": false, + "dBSPL": false, + "dBSPLcalibration": 90, "maikiwiAudio": { "ciderPPE": false, "ciderPPE_value": "MAIKIWI", @@ -212,17 +214,22 @@ export class Store { }, "connectUser": { "auth": null, + "sync": { + themes: false, + plugins: false, + settings: false, + } }, } private migrations: any = { '>=1.4.3': (store: ElectronStore) => { - if (typeof store.get('general.discord_rpc') == 'number' || typeof store.get('general.discord_rpc') == 'string') { - store.delete('general.discord_rpc'); + if (typeof store.get('general.discordrpc') == 'number' || typeof store.get('general.discordrpc') == 'string') { + store.delete('general.discordrpc'); } }, } private schema: ElectronStore.Schema = { - "general.discord_rpc": { + "general.discordrpc": { type: 'object' }, } @@ -261,6 +268,7 @@ export class Store { return target } + /** * IPC Handler */ @@ -281,5 +289,43 @@ export class Store { Store.cfg.store = store }) } + + + static pushToCloud(): void { + if (Store.cfg.get('connectUser.auth') === null) return; + var syncData = Object(); + if (Store.cfg.get('connectUser.sync.themes')) { + syncData.push({ + themes: Store.cfg.store.themes + }) + } + if (Store.cfg.get('connectUser.sync.plugins')) { + syncData.push({ + plugins: Store.cfg.store.plugins + }) + } + + if (Store.cfg.get('connectUser.sync.settings')) { + syncData.push({ + general: Store.cfg.get('general'), + home: Store.cfg.get('home'), + libraryPrefs: Store.cfg.get('libraryPrefs'), + advanced: Store.cfg.get('advanced'), + }) + } + let postBody = { + id: Store.cfg.get('connectUser.id'), + app: electron.app.getName(), + version: electron.app.isPackaged ? electron.app.getVersion() : 'dev', + syncData: syncData + } + fetch('https://connect.cidercollective.dev/api/v1/setttings/set', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(postBody) + }) + } } diff --git a/src/main/base/utils.ts b/src/main/base/utils.ts index 9889c4ff..e010cfe0 100644 --- a/src/main/base/utils.ts +++ b/src/main/base/utils.ts @@ -80,6 +80,7 @@ export class utils { return Store.cfg.store } + /** * Get the store instance * @returns {Store} @@ -97,6 +98,18 @@ export class utils { Store.cfg.set(key, value) } + /** + * Pushes Store to Connect + * @return Function + */ + static pushStoreToConnect(): Function { + return Store.pushToCloud + } + + + + + /** * Gets the browser window */ @@ -198,4 +211,6 @@ export class utils { autoUpdater.logger = log await autoUpdater.checkForUpdatesAndNotify() } + + } diff --git a/src/main/plugins/chromecast.ts b/src/main/plugins/chromecast.ts index 5702fefc..0cac3506 100644 --- a/src/main/plugins/chromecast.ts +++ b/src/main/plugins/chromecast.ts @@ -28,7 +28,7 @@ export default class ChromecastPlugin { // private GCstream = new Stream.PassThrough(), private connectedHosts: any = {}; private connectedPlayer: any; - // private port = false; + private ciderPort :any = 9000; // private server = false; // private bufcount = 0; // private bufcount2 = 0; @@ -148,7 +148,7 @@ export default class ChromecastPlugin { } let media = { // Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType. - contentId: 'http://' + this.getIp() + ':9000/audio.wav', + contentId: 'http://' + this.getIp() + ':'+ this.ciderPort +'/audio.wav', contentType: 'audio/wav', streamType: 'LIVE', // or LIVE @@ -361,4 +361,12 @@ export default class ChromecastPlugin { } + onRendererReady(): void { + this._win.webContents.executeJavaScript( + `ipcRenderer.sendSync('get-port')` + ).then((result: any) => { + this.ciderPort = result; + }); + } + } \ No newline at end of file diff --git a/src/main/plugins/discordrpc.ts b/src/main/plugins/discordrpc.ts index 740a79b6..c943eb68 100644 --- a/src/main/plugins/discordrpc.ts +++ b/src/main/plugins/discordrpc.ts @@ -1,40 +1,29 @@ -import * as RPC from 'discord-rpc' +import {AutoClient} from 'discord-auto-rpc' import {ipcMain} from "electron"; import fetch from 'electron-fetch' export default class DiscordRPC { - /** - * Private variables for interaction in plugins - */ - private _utils: any; - private _app: any; - private _attributes: any; - private _connection: boolean = false; - /** * Base Plugin Details (Eventually implemented into a GUI in settings) */ public name: string = 'Discord Rich Presence'; public description: string = 'Discord RPC plugin for Cider'; - public version: string = '1.0.0'; + public version: string = '1.1.0'; public author: string = 'vapormusic/Core (Cider Collective)'; + /** + * Private variables for interaction in plugins + */ + private _utils: any; + private _attributes: any; + private ready: boolean = false; + /** * Plugin Initialization */ private _client: any = null; - private _activity: RPC.Presence = { - details: '', - state: '', - largeImageKey: '', - largeImageText: '', - smallImageKey: '', - smallImageText: '', - instance: false - }; - - private _activityCache: RPC.Presence = { + private _activityCache: any = { details: '', state: '', largeImageKey: '', @@ -44,6 +33,73 @@ export default class DiscordRPC { instance: false }; + /******************************************************************************************* + * Public Methods + * ****************************************************************************************/ + + /** + * Runs on plugin load (Currently run on application start) + */ + constructor(utils: any) { + this._utils = utils; + console.debug(`[Plugin][${this.name}] Loading Complete.`); + } + + /** + * Runs on app ready + */ + onReady(_win: any): void { + const self = this + this.connect(); + console.debug(`[Plugin][${this.name}] Ready.`); + ipcMain.on('updateRPCImage', (_event, imageurl) => { + if (!this._utils.getStoreValue("general.privateEnabled")) { + fetch('https://api.cider.sh/v1/images', { + + method: 'POST', + body: JSON.stringify({url: imageurl}), + headers: { + 'Content-Type': 'application/json', + 'User-Agent': _win.webContents.getUserAgent() + }, + }) + .then(res => res.json()) + .then(function (json) { + self._attributes["artwork"]["url"] = json.url + self.setActivity(self._attributes) + }) + } + }) + } + + /** + * Runs on app stop + */ + onBeforeQuit(): void { + console.debug(`[Plugin][${this.name}] Stopped.`); + } + + /** + * Runs on playback State Change + * @param attributes Music Attributes (attributes.status = current state) + */ + onPlaybackStateDidChange(attributes: object): void { + this._attributes = attributes + this.setActivity(attributes) + + } + + /** + * Runs on song change + * @param attributes Music Attributes + */ + onNowPlayingItemDidChange(attributes: object): void { + this._attributes = attributes + this.setActivity(attributes) + + } + + /******************************************************************************************* * Private Methods * ****************************************************************************************/ @@ -53,63 +109,91 @@ export default class DiscordRPC { * @private */ private connect() { - if (!this._utils.getStoreValue("general.discord_rpc.enabled")) { + if (!this._utils.getStoreValue("general.discordrpc.enabled")) { return; } - const clientId = this._utils.getStoreValue("general.discord_rpc.client") === "Cider" ? '911790844204437504' : '886578863147192350'; - - // Apparently needed for ask to join, join, spectate etc. - RPC.register(clientId) + const clientId = this._utils.getStoreValue("general.discordrpc.client") === "Cider" ? '911790844204437504' : '886578863147192350'; // Create the client - this._client = new RPC.Client({transport: "ipc"}); + this._client = new AutoClient({transport: "ipc"}); // Runs on Ready - this._client.on('ready', () => { + this._client.once('ready', () => { console.info(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${this._client.user.id}.`); + + if (this._activityCache && this._activityCache.details && this._activityCache.state) { + console.info(`[DiscordRPC][connect] Restoring activity cache.`); + this._client.setActivity(this._activityCache) + } }) - // Handles Errors - this._client.on('error', (err: any) => { - console.error(`[DiscordRPC] ${err}`); - this.disconnect() - }); - - // If Discord is closed, allow reconnecting - this._client.transport.once('close', () => { - console.info(`[DiscordRPC] Connection closed`); - this.disconnect() - }); - // Login to Discord - this._client.login({clientId}) + this._client.endlessLogin({clientId: clientId}) .then(() => { - this._connection = true; + this.ready = true }) .catch((e: any) => console.error(`[DiscordRPC][connect] ${e}`)); } /** - * Disconnects from Discord RPC + * Sets the activity + * @param attributes Music Attributes */ - private disconnect() { + private setActivity(attributes: any) { if (!this._client) { return } - this._client.destroy().then(() => { - this._connection = false; - console.log('[DiscordRPC][disconnect] Disconnected from discord.') - }).catch((e: any) => console.error(`[DiscordRPC][disconnect] ${e}`)); + // Check if show buttons is (true) or (false) + let activity: Object = { + details: this._utils.getStoreValue("general.discordrpc.details_format"), + state: this._utils.getStoreValue("general.discordrpc.state_format"), + largeImageKey: attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024'), + largeImageText: attributes.albumName, + instance: false // Whether the activity is in a game session + } - // Clean up, allow creating a new connection - this._client = null; + // Filter the activity + activity = this.filterActivity(activity, attributes) + + if (!this.ready) { + this._activityCache = activity + return + } + + // Set the activity + if (!attributes.status && this._utils.getStoreValue("general.discordrpc.clear_on_pause")) { + this._client.clearActivity() + } else if (activity && this._activityCache !== activity) { + this._client.setActivity(activity) + } + this._activityCache = activity; } /** * Filter the Discord activity object */ - private static filterActivity(activity: any, attributes: any): Object { + private filterActivity(activity: any, attributes: any): Object { + + // Add the buttons if people want them + if (!this._utils.getStoreValue("general.discordrpc.hide_buttons")) { + activity.buttons = [ + {label: 'Listen on Cider', url: attributes.url.cider}, + {label: 'View on Apple Music', url: attributes.url.appleMusic} + ] //To change attributes.url => preload/cider-preload.js + } + + // Add the timestamp if its playing + if (attributes.status) { + activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime) + activity.endTimestamp = attributes.endTime + } + + // If the user wants to keep the activity when paused + if (!this._utils.getStoreValue("general.discordrpc.clear_on_pause")) { + activity.smallImageKey = attributes.status ? 'play' : 'pause'; + activity.smallImageText = attributes.status ? 'Playing' : 'Paused'; + } /** * Works with: @@ -173,138 +257,4 @@ export default class DiscordRPC { } return activity } - - /** - * Sets the activity - * @param {activity} activity - */ - private setActivity(activity: any) { - if (!this._connection || !this._client || !activity) { - return - } - - // Filter the activity - activity = DiscordRPC.filterActivity(activity, this._attributes) - - // Set the activity - if (!this._attributes.status && this._utils.getStoreValue("general.discord_rpc.clear_on_pause")) { - this._client.clearActivity() - } else if (this._activity && this._activityCache !== this._activity && this._activity.details) { - this._client.setActivity(activity) - this._activityCache = this._activity; - } - } - - /** - * Sets the activity of the client - * @param {object} attributes - */ - private updateActivity(attributes: any) { - if (!this._utils.getStoreValue("general.discord_rpc.enabled") || this._utils.getStoreValue("general.privateEnabled")) { - return - } else if (!this._client || !this._connection) { - this.connect() - } - - // Check if show buttons is (true) or (false) - this._activity = { - details: this._utils.getStoreValue("general.discord_rpc.details_format"), - state: this._utils.getStoreValue("general.discord_rpc.state_format"), - largeImageKey: attributes?.artwork?.url?.replace('{w}', '1024').replace('{h}', '1024'), - largeImageText: attributes.albumName, - instance: false // Whether the activity is in a game session - } - - // Add the buttons if people want them - if (!this._utils.getStoreValue("general.discord_rpc.hide_buttons")) { - this._activity.buttons = [ - {label: 'Listen on Cider', url: attributes.url.cider}, - {label: 'View on Apple Music', url: attributes.url.appleMusic} - ] //To change attributes.url => preload/cider-preload.js - } - - // Add the timestamp if its playing - if (attributes.status) { - this._activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime) - this._activity.endTimestamp = attributes.endTime - } - - // If the user wants to keep the activity when paused - if (!this._utils.getStoreValue("general.discord_rpc.clear_on_pause")) { - this._activity.smallImageKey = attributes.status ? 'play' : 'pause'; - this._activity.smallImageText = attributes.status ? 'Playing' : 'Paused'; - } - - this.setActivity(this._activity) - } - - /******************************************************************************************* - * Public Methods - * ****************************************************************************************/ - - /** - * Runs on plugin load (Currently run on application start) - */ - constructor(utils: { getStore: () => any; getApp: () => any; }) { - this._utils = utils; - console.debug(`[Plugin][${this.name}] Loading Complete.`); - this._app = utils.getApp(); - } - - /** - * Runs on app ready - */ - onReady(_win: any): void { - let self = this - this.connect(); - console.debug(`[Plugin][${this.name}] Ready.`); - ipcMain.on('updateRPCImage', (_event, imageurl) => { - if (!this._utils.getStoreValue("general.privateEnabled")) { - fetch('https://api.cider.sh/v1/images', { - - method: 'POST', - body: JSON.stringify({url: imageurl}), - headers: { - 'Content-Type': 'application/json', - 'User-Agent': _win.webContents.getUserAgent() - }, - }) - .then(res => res.json()) - .then(function (json) { - self._attributes["artwork"]["url"] = json.url - self.updateActivity(self._attributes) - }) - } - }) - } - - /** - * Runs on app stop - */ - onBeforeQuit(): void { - if (this._client) { - this.disconnect() - } - console.debug(`[Plugin][${this.name}] Stopped.`); - } - - /** - * Runs on playback State Change - * @param attributes Music Attributes (attributes.status = current state) - */ - onPlaybackStateDidChange(attributes: object): void { - this._attributes = attributes - this.updateActivity(attributes) - - } - - /** - * Runs on song change - * @param attributes Music Attributes - */ - onNowPlayingItemDidChange(attributes: object): void { - this._attributes = attributes - this.updateActivity(attributes) - - } } diff --git a/src/main/plugins/menubar.ts b/src/main/plugins/menubar.ts index b47dcabe..a9d73428 100644 --- a/src/main/plugins/menubar.ts +++ b/src/main/plugins/menubar.ts @@ -3,6 +3,7 @@ import {utils} from "../base/utils"; export default class Thumbar { + /** * Base Plugin Details (Eventually implemented into a GUI in settings) */ @@ -16,36 +17,42 @@ export default class Thumbar { * Menubar Assets * @private */ - private isMac: boolean = process.platform === 'darwin'; + private isMac: boolean = process.platform === 'darwin'; private _menuTemplate: any = [ { label: app.getName(), submenu: [ { - label: 'About', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.about'), click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('about')`) }, {type: 'separator'}, { - label: 'Settings', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.settings'), accelerator: utils.getStoreValue("general.keybindings.settings").join('+'), - click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('settings')`) + click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('settings')`), }, + ...(this.isMac ? [ {type: 'separator'}, {role: 'services'}, {type: 'separator'}, {role: 'hide'}, {role: 'hideOthers'}, {role: 'unhide'}, + ]: [ {type: 'separator'}, - {role: 'quit'} - ] + {role: 'quit', label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.quit')}, + ]), + ], }, + + { - label: 'View', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.view'), submenu: [ - {role: 'reload'}, - {role: 'forceReload'}, + {role: 'reload', label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.reload')}, + {role: 'forceReload', label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.forcereload')}, + ...(this.isMac ? [ {role: 'toggleDevTools'}, {type: 'separator'}, {role: 'resetZoom'}, @@ -53,25 +60,28 @@ export default class Thumbar { {role: 'zoomOut'}, {type: 'separator'}, {role: 'togglefullscreen'}, - ] + + ]: []), + ], }, { - label: 'Window', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.window'), submenu: [ - {role: 'minimize'}, + {role: 'minimize', label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.minimize')}, + ...(this.isMac ? [ { label: 'Show', click: () => utils.getWindow().show() }, {role: 'zoom'}, - ...(this.isMac ? [ + {type: 'separator'}, {role: 'front'}, {role: 'close'}, - ] : [ + {role: 'close'}, - ]), + { label: 'Edit', submenu: [ @@ -83,146 +93,148 @@ export default class Thumbar { {role: 'paste'}, ] }, + ] : [] + ), {type: 'separator'}, { - label: 'Toggle Private Session', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.toggleprivate'), accelerator: utils.getStoreValue("general.keybindings.togglePrivateSession").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.cfg.general.privateEnabled = !app.cfg.general.privateEnabled`) }, {type: 'separator'}, { - label: 'Web Remote', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.webremote'), accelerator: utils.getStoreValue("general.keybindings.webRemote").join('+'), sublabel: 'Opens in external window', click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('remote-pair')`) }, { - label: 'Audio Settings', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.audio'), accelerator: utils.getStoreValue("general.keybindings.audioSettings").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.audioSettings = true`) }, { - label: 'Plug-in Menu', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.plugins'), accelerator: utils.getStoreValue("general.keybindings.pluginMenu").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.pluginMenu = true`) } ] }, { - label: 'Controls', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.controls'), submenu: [ { - label: 'Pause / Play', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.playpause'), accelerator: 'Space', click: () => utils.getWindow().webContents.executeJavaScript(`app.SpacePause()`) }, { - label: 'Next', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.next'), accelerator: 'CommandOrControl+Right', click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.next()`) }, { - label: 'Previous', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.previous'), accelerator: 'CommandOrControl+Left', click: () => utils.getWindow().webContents.executeJavaScript(`MusicKitInterop.previous()`) }, {type: 'separator'}, { - label: 'Volume Up', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.volumeup'), accelerator: 'CommandOrControl+Up', click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeUp()`) }, { - label: 'Volume Down', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.volumedown'), accelerator: 'CommandOrControl+Down', click: () => utils.getWindow().webContents.executeJavaScript(`app.volumeDown()`) }, { - label: 'Browse', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.browse'), accelerator: utils.getStoreValue("general.keybindings.browse").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('browse')`) }, {type: 'separator'}, { - label: 'Artists', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.artists'), accelerator: utils.getStoreValue("general.keybindings.artists").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-artists')`) }, { - label: 'Search', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.search'), accelerator: utils.getStoreValue("general.keybindings.search").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('search')`) }, {type: 'separator'}, { - label: 'Album', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.albums'), accelerator: utils.getStoreValue("general.keybindings.albums").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('library-albums')`) }, {type: 'separator'}, { - label: 'Cast To Devices', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.cast'), accelerator: utils.getStoreValue("general.keybindings.castToDevices").join('+'), click: () => utils.getWindow().webContents.executeJavaScript(`app.modals.castMenu = true`) } ] }, { - label: 'Account', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.account'), submenu: [ { - label: 'Account Settings', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.accountsettings'), click: () => utils.getWindow().webContents.executeJavaScript(`app.appRoute('apple-account-settings')`) }, { - label: 'Sign Out', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.signout'), click: () => utils.getWindow().webContents.executeJavaScript(`app.unauthorize()`) } ] }, { - label: 'Support', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.support'), role: 'help', submenu: [ { - label: 'Discord', + label: utils.getLocale('Discord', 'menubar.options.discord'), click: () => shell.openExternal("https://discord.gg/AppleMusic").catch(console.error) }, { - label: 'GitHub Wiki', + label: utils.getLocale('GitHub Wiki', 'menubar.options.github'), click: () => shell.openExternal("https://github.com/ciderapp/Cider/wiki/Troubleshooting").catch(console.error) }, {type: 'separator'}, { - label: 'Report a...', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.report'), submenu: [ { - label: 'Bug', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.bug'), click: () => shell.openExternal("https://github.com/ciderapp/Cider/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+").catch(console.error) }, { - label: 'Feature Request', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.feature'), click: () => shell.openExternal("https://github.com/ciderapp/Cider/discussions/new?category=feature-request").catch(console.error) }, { - label: 'Translation Report/Request', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.trans'), click: () => shell.openExternal("https://github.com/ciderapp/Cider/issues/new?assignees=&labels=%F0%9F%8C%90+Translations&template=translation.yaml&title=%5BTranslation%5D%3A+").catch(console.error) }, ] }, {type: 'separator'}, { - label: 'View License', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.license'), click: () => shell.openExternal("https://github.com/ciderapp/Cider/blob/main/LICENSE").catch(console.error) }, {type: 'separator'}, { - label: 'Toggle Developer Tools', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.toggledevtools'), accelerator: utils.getStoreValue("general.keybindings.openDeveloperTools").join('+'), click: () => utils.getWindow().webContents.openDevTools() }, { - label: 'Open Configuration File in Editor', + label: utils.getLocale(utils.getStoreValue('general.language'), 'menubar.options.conf'), click: () => utils.getStoreInstance().openInEditor() } ] diff --git a/src/main/plugins/raop.ts b/src/main/plugins/raop.ts index e6908375..e99c3de7 100644 --- a/src/main/plugins/raop.ts +++ b/src/main/plugins/raop.ts @@ -89,9 +89,9 @@ export default class RAOP { `; private ondeviceup(name: any, host: any, port: any, addresses: any, text: any) { - if (this.castDevices.findIndex((item: any) => item.name === host && item.port === port && item.addresses === addresses) === -1) { + if (this.castDevices.findIndex((item: any) => item.name == host.replace(".local","") && item.port == port && item.addresses == addresses) === -1) { this.castDevices.push({ - name: host, + name: host.replace(".local",""), host: addresses ? addresses[0] : '', port: port, addresses: addresses, @@ -147,14 +147,28 @@ export default class RAOP { browser.on('ready', browser.discover); browser.on('update', (service: any) => { - if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp') || service.fullname.includes('_airplay._tcp'))) { + if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp') || service.fullname.includes('_airplay._tcp'))) { // console.log(service.txt) this._win.webContents.executeJavaScript(`console.log( "${service.name} ${service.host}:${service.port} ${service.addresses}" )`); - this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt);} + this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt); + } }); + + // const browser2 = this.mdns.createBrowser(this.mdns.tcp('airplay')); + // browser2.on('ready', browser2.discover); + // browser2.on('update', (service: any) => { + // if (service.addresses && service.fullname && (service.fullname.includes('_raop._tcp') || service.fullname.includes('_airplay._tcp'))) { + // // console.log(service.txt) + // this._win.webContents.executeJavaScript(`console.log( + // "${service.name} ${service.host}:${service.port} ${service.addresses}" + // )`); + // this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt); + // } + // }); + }); @@ -167,7 +181,7 @@ export default class RAOP { this.portairplay = ipport; this.device = this.airtunes.add(ipv4, { port: ipport, - volume: 60, + volume: 50, password: sepassword, txt: txt }); @@ -178,6 +192,15 @@ export default class RAOP { this._win.webContents.setAudioMuted(true); this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err)); } + if (status == "need_password"){ + this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`) + } + if (status == "pair_success"){ + this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`) + } + if (status == "pair_failed"){ + this._win.webContents.executeJavaScript(`app.sendAirPlayFailed()`) + } if (status == 'stopped') { this.airtunes.stopAll(() => { console.log('end'); @@ -210,6 +233,12 @@ export default class RAOP { }); + electron.ipcMain.on('setAirPlayPasscode', (event, passcode) => { + if (this.device){ + this.device.setPasscode(passcode) + } + }) + electron.ipcMain.on('writeWAV', (event, leftbuffer, rightbuffer) => { if (this.airtunes != null) { if (this.worker == null) { diff --git a/src/renderer/assets/feather/heart-fill.svg b/src/renderer/assets/feather/heart-fill.svg new file mode 100644 index 00000000..4daaa2c8 --- /dev/null +++ b/src/renderer/assets/feather/heart-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/assets/feather/heart.svg b/src/renderer/assets/feather/heart.svg index a083b7e2..973edba4 100644 --- a/src/renderer/assets/feather/heart.svg +++ b/src/renderer/assets/feather/heart.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/renderer/index.js b/src/renderer/index.js index 8df442f8..6f83b9b1 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -105,7 +105,7 @@ function fallbackinitMusicKit() { }) setTimeout(() => { app.init() - if(app.cfg.visual.window_background_style == "mica" && !app.isDev) { + if (app.cfg.visual.window_background_style == "mica" && !app.isDev) { app.spawnMica() } }, 1000) @@ -134,7 +134,7 @@ document.addEventListener('musickitloaded', function () { function waitForApp() { if (typeof app.init !== "undefined") { app.init() - if(app.cfg.visual.window_background_style == "mica" && !app.isDev) { + if (app.cfg.visual.window_background_style == "mica" && !app.isDev) { app.spawnMica() } } diff --git a/src/renderer/less/elements.css b/src/renderer/less/elements.css index d4547eae..0c54721e 100644 --- a/src/renderer/less/elements.css +++ b/src/renderer/less/elements.css @@ -364,6 +364,21 @@ align-items: center; justify-content: center; } +.cd-mediaitem-list-item .heart-unfilled { + background-image: url("assets/feather/heart.svg"); + height: 12px; + width: 36px; + filter: contrast(0); + background-repeat: no-repeat; +} + +.cd-mediaitem-list-item .heart-filled { + background-image: url("assets/feather/heart-fill.svg"); + height: 12px; + width: 36px; + filter: contrast(0); + background-repeat: no-repeat; +} .cd-mediaitem-list-item .explicit-icon { background-image: url("assets/explicit.svg"); height: 12px; @@ -371,6 +386,9 @@ filter: contrast(0); background-repeat: no-repeat; } +.heart-icon { + display: flex +} @keyframes load-bar { 10% { box-shadow: inset 0 -4px 0; diff --git a/src/renderer/less/elements.less b/src/renderer/less/elements.less index 716b71a4..11412567 100644 --- a/src/renderer/less/elements.less +++ b/src/renderer/less/elements.less @@ -359,7 +359,11 @@ align-items: center; border-radius: var(--mediaItemRadius); position: relative; - + &:hover{ + .heart-icon{ + display: none; + } + } .popular { background-image: url(assets/star.svg); background-repeat: no-repeat; @@ -448,6 +452,22 @@ justify-content: center; } + .heart-unfilled { + -webkit-mask-image: url("assets/feather/heart.svg"); + height: 12px; + width: 12px; + background-repeat: no-repeat; + background-color: #999; + } + + .heart-filled { + -webkit-mask-image: url("assets/feather/heart-fill.svg"); + height: 12px; + width: 12px; + background-repeat: no-repeat; + background-color: #999; + } + .explicit-icon { background-image: url("./assets/explicit.svg"); height: 12px; @@ -456,6 +476,12 @@ background-repeat: no-repeat; } + .heart-icon { + display: flex; + position: absolute; + left: 20px; + } + /* CSS.gg */ @keyframes load-bar { diff --git a/src/renderer/less/helpers.less b/src/renderer/less/helpers.less index db659232..431947c4 100644 --- a/src/renderer/less/helpers.less +++ b/src/renderer/less/helpers.less @@ -70,10 +70,11 @@ .spatialproperties-panel { .modal-window { + &:not(.airplay-modal){ height : 700px; max-height: 700px; width : 800px; - max-width : 800px; + max-width : 800px;} overflow : hidden; .info-header { @@ -352,7 +353,7 @@ &:hover { &::before { - transition: transform .1s ease-in, opacity .1s ease-in; + transition: transform 0s ease-in, opacity 0s ease-in; opacity : 1; transform : scale(1); } diff --git a/src/renderer/less/pages.less b/src/renderer/less/pages.less index 2e03391e..b98b560a 100644 --- a/src/renderer/less/pages.less +++ b/src/renderer/less/pages.less @@ -488,63 +488,79 @@ /* Album / Playlist Page */ .playlist-page { - --bgColor : transparent; - padding : 0px; + --bgColor : transparent; + padding : 0px; //background: linear-gradient(180deg, var(--bgColor) 32px, var(--bgColor) 18px, transparent 60px, transparent 100%); - top : 0; - padding-top : var(--navigationBarHeight); - display:flex; + top : 0; + padding-top : var(--navigationBarHeight); + display : flex; flex-direction: column; - height: 100%; - overflow: hidden; + height : 100%; + overflow : hidden; + + .cd-mediaitem-list-item { + &:hover { + .heart-icon { + display: flex; + } + } + + .heart-icon { + left: -25px; + } + } .editTracksBtn { position: absolute; - top: 20px; - right: 20px; - z-index: 1; + top : 20px; + right : 20px; + z-index : 1; >span { display: flex; - gap: 8px; + gap : 8px; } } .mediaContainer { transition: width 0.5s ease-in-out, height 0.5s ease-in-out; - width: 260px;height:260px; + width : 260px; + height : 260px; } .playlist-body { - padding : 32px; + padding : 32px; // margin-top: -75px; - overflow-y:overlay; - height:100%; - padding:0px; + overflow-y : overlay; + height : 100%; + padding : 0px; background-color: var(--color1); &.scrollbody { .tabs { - display: flex; + display : flex; flex-flow: column; - height: 100%; + height : 100%; .nav-link { - text-transform:capitalize; + text-transform: capitalize; } .tab-content { - height: 100%; + height : 100%; overflow: hidden; - margin:0px; + margin : 0px; + .tab-pane { - height: 100%; - overflow-y: overlay; - overflow-x:hidden; - padding: var(--contentInnerPadding); + height : 100%; + overflow-y : overlay; + overflow-x : hidden; + padding : var(--contentInnerPadding); + padding-inline : 40px; -webkit-mask-image: linear-gradient(180deg, transparent, white 20px); + .well { - margin:0px; + margin: 0px; } } } @@ -563,7 +579,7 @@ background : rgba(0, 0, 0, 0.25); top : var(--navigationBarHeight); transition : opacity 0.1s var(--appleEase); - display: none; + display : none; } .playlist-display { @@ -649,14 +665,14 @@ } .playlist-desc { - transition: height .2s ease-in-out, opacity .2s ease-in-out; + transition : height .2s ease-in-out, opacity .2s ease-in-out; box-sizing : border-box; font-size : 14px; flex-shrink : unset; margin-right: 5px; max-height : 100px; position : relative; - height : 4vh; + height : 4vh; .content { height : 4vh; @@ -750,11 +766,11 @@ } .playlist-time { - font-size: 0.9em; - margin : 6px; - opacity : 0.7; + font-size : 0.9em; + margin : 6px; + opacity : 0.7; transition: height .2s ease-in-out, opacity .2s ease-in-out; - height: 0.9em; + height : 0.9em; } &.inline-playlist { @@ -802,8 +818,8 @@ .pilldim { .nav-pills { - width: max-content; - margin: 0 auto; + width : max-content; + margin : 0 auto; margin-top: 16px; } } @@ -813,26 +829,24 @@ transition: min-height 0.5s ease-in-out; min-height: 200px; - .playlistInfo { - - } + .playlistInfo {} .mediaContainer { transition: width 0.5s ease-in-out, height 0.5s ease-in-out; - width: 128px!important; - height: 128px!important; + width : 128px !important; + height : 128px !important; } .playlist-time { transition: height .2s ease-in-out, opacity .2s ease-in-out; - height: 0px; - opacity: 0; + height : 0px; + opacity : 0; } .playlist-desc { transition: height .2s ease-in-out, opacity .2s ease-in-out; - height: 0px!important; - opacity: 0; + height : 0px !important; + opacity : 0; } } } @@ -920,7 +934,7 @@ pointer-events : none; .header-content { - z-index : 1; + z-index : 1; // margin-top: -16px; } @@ -1112,9 +1126,56 @@ /* Artist Page End */ -// Settings page -.settings-page { - padding: 0px; + +.installed-themes-page { + + .themeContextMenu { + background: transparent; + color : var(--keyColor); + border : 0px; + } + + .list-group-item { + &.addon { + background: rgb(86 86 86 / 20%); + } + &.applied { + background: var(--keyColor-disabled); + pointer-events: none; + } + } + + .repo-header { + font-size : 16px; + position : sticky; + top : 0; + left : 0; + right : 0; + width : 100%; + height : 50px; + z-index : 1; + background : rgba(36, 36, 36, 0.5); + display : flex; + justify-content: center; + align-items : center; + backdrop-filter: var(--glassFilter); + overflow : hidden; + border-bottom : 1px solid rgb(0 0 0 / 18%); + border-top : 1px solid rgb(135 135 135 / 18%); + } + + .style-editor-container { + height : 100%; + flex : 1; + background: var(--color2); + padding : 0px; + overflow-y: overlay; + + .list-group-item { + border-radius: 0px; + } + } + .stylestack-editor { width: 100%; @@ -1125,17 +1186,34 @@ } .themeLabel { - display:flex; + display : flex; align-items: center; } + .handle { + height: 100%; + display: flex; + justify-content: center; + align-items: center; + } + + .list-group-item { + + &:hover { + cursor: grab; + } + &:active { + cursor: grabbing; + } + } + .removeItem { - border: 0px; - background: transparent; - height: 32px; + border : 0px; + background : transparent; + height : 32px; font-weight: bold; - color: var(--textColor); - cursor: pointer; + color : var(--textColor); + cursor : pointer; } .stylesDropdown { @@ -1145,7 +1223,11 @@ } } } +} +// Settings page +.settings-page { + padding: 0px; .nav { width : 90%; @@ -1163,8 +1245,9 @@ .settings-option-body-webview { height: 100%; - width: 100%; + width : 100%; } + .settings-option-body { margin: 16px; } diff --git a/src/renderer/main/events.js b/src/renderer/main/events.js index 0b2e13d3..f2140460 100644 --- a/src/renderer/main/events.js +++ b/src/renderer/main/events.js @@ -22,7 +22,7 @@ const Events = { if (event.keyCode === 82 && event.ctrlKey) { event.preventDefault() bootbox.confirm("Reload Cider?", (res)=>{ - if(res) { + if (res) { window.location.reload() } }) diff --git a/src/renderer/main/gamepad.js b/src/renderer/main/gamepad.js index a902cd5f..abe20631 100644 --- a/src/renderer/main/gamepad.js +++ b/src/renderer/main/gamepad.js @@ -251,9 +251,9 @@ function simulateGamepad () { cursorPos[1] -= cursorSpeed // sounds.Hover.play() - // if(intTabIndex <= 0) { + // if (intTabIndex <= 0) { // intTabIndex = 0 - // }else{ + // } else { // intTabIndex-- // } // $(tabbable[intTabIndex]).focus() @@ -263,9 +263,9 @@ function simulateGamepad () { e.preventDefault() cursorPos[1] += cursorSpeed - // if(intTabIndex < tabbable.length) { + // if (intTabIndex < tabbable.length) { // intTabIndex++ - // }else{ + // } else { // intTabIndex = tabbable.length // } // $(tabbable[intTabIndex]).focus() diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js index 58b49752..9ddb2f88 100644 --- a/src/renderer/main/vueapp.js +++ b/src/renderer/main/vueapp.js @@ -148,6 +148,10 @@ const app = new Vue({ }, tmpHeight: '', tmpWidth: '', + tmpX: '', + tmpY: '', + miniTmpX: '', + miniTmpY: '', tmpVar: [], notification: false, chrome: { @@ -207,6 +211,7 @@ const app = new Vue({ showPlaylist: false, castMenu: false, moreInfo: false, + airplayPW: false, }, socialBadges: { badgeMap: {}, @@ -230,6 +235,7 @@ const app = new Vue({ pages: [], }, moreinfodata: [], + notyf: notyf }, watch: { cfg: { @@ -283,6 +289,9 @@ const app = new Vue({ } } }, + formatVolumeTooltip() { + return this.cfg.audio.dBSPL ? (Number(this.cfg.audio.dBSPLcalibration) + (Math.log10(this.mk.volume) * 20)).toFixed(2) + ' dB SPL' : (Math.log10(this.mk.volume) * 20).toFixed(2) + ' dBFS' + }, mainMenuVisibility(val) { if (val) { (this.mk.isAuthorized) ? this.chrome.menuOpened = !this.chrome.menuOpened : false; @@ -592,9 +601,7 @@ const app = new Vue({ }, async init() { let self = this - if (this.cfg.visual.theme != "default.less" && this.cfg.visual.theme != "") { - this.setTheme(this.cfg.visual.theme) - } + if (this.cfg.visual.styles.length != 0) { await this.reloadStyles() } @@ -702,6 +709,7 @@ const app = new Vue({ let lastItem = window.localStorage.getItem("currentTrack") let time = window.localStorage.getItem("currentTime") let queue = window.localStorage.getItem("currentQueue") + app.mk.queue.position = 0; // Reset queue position. if (lastItem != null) { lastItem = JSON.parse(lastItem) let kind = lastItem.attributes.playParams.kind; @@ -721,7 +729,7 @@ const app = new Vue({ if (queue != null) { queue = JSON.parse(queue) if (queue && queue.length > 0) { - let ids = queue.map(e => (e.playParams ? e.playParams.id : (e.attributes.playParams ? e.attributes.playParams.id : ''))) + let ids = queue.map(e => (e.playParams ? e.playParams.id : (e.item.attributes.playParams ? e.item.attributes.playParams.id : ''))) let i = 0; if (ids.length > 0) { for (let id of ids) { @@ -832,6 +840,14 @@ const app = new Vue({ ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes()); }) + this.mk.addEventListener(MusicKit.Events.queueItemsDidChange, ()=>{ + if (self.$refs.queue) { + setTimeout(()=>{ + self.$refs.queue.updateQueue(); + }, 100) + } + }) + this.mk.addEventListener(MusicKit.Events.nowPlayingItemDidChange, (a) => { if (self.$refs.queue) { self.$refs.queue.updateQueue(); @@ -1118,28 +1134,31 @@ const app = new Vue({ } }) }, - async refreshPlaylists(localOnly = false) { + async refreshPlaylists(localOnly = false, useCachedPlaylists = true) { let self = this let trackMap = this.cfg.advanced.playlistTrackMapping let newListing = [] let trackMapping = {} - const cachedPlaylist = await CiderCache.getCache("library-playlists") - const cachedTrackMapping = await CiderCache.getCache("library-playlists-tracks") + + if (useCachedPlaylists) { + const cachedPlaylist = await CiderCache.getCache("library-playlists") + const cachedTrackMapping = await CiderCache.getCache("library-playlists-tracks") - if (cachedPlaylist) { - console.debug("using cached playlists") - this.playlists.listing = cachedPlaylist - self.sortPlaylists() - } else { - console.debug("playlist has no cache") - } + if (cachedPlaylist) { + console.debug("using cached playlists") + this.playlists.listing = cachedPlaylist + self.sortPlaylists() + } else { + console.debug("playlist has no cache") + } - if (cachedTrackMapping) { - console.debug("using cached track mapping") - this.playlists.trackMapping = cachedTrackMapping - } - if (localOnly) { - return + if (cachedTrackMapping) { + console.debug("using cached track mapping") + this.playlists.trackMapping = cachedTrackMapping + } + if (localOnly) { + return + } } this.library.backgroundNotification.message = "Building playlist cache..." @@ -1147,8 +1166,10 @@ const app = new Vue({ async function deepScan(parent = "p.playlistsroot") { console.debug(`scanning ${parent}`) - const playlistData = await app.mk.api.v3.music(`/v1/me/library/playlist-folders/${parent}/children/`) - await asyncForEach(playlistData.data.data, async (playlist) => { + // const playlistData = await app.mk.api.v3.music(`/v1/me/library/playlist-folders/${parent}/children/`) + const playlistData = await MusicKitTools.v3Continuous({href: `/v1/me/library/playlist-folders/${parent}/children/`}) + console.log(playlistData) + await asyncForEach(playlistData, async (playlist) => { playlist.parent = parent if ( playlist.type != "library-playlist-folders" && @@ -1242,7 +1263,7 @@ const app = new Vue({ } } ).then(res => { - self.refreshPlaylists() + self.refreshPlaylists(false, false) }) }, async editPlaylist(id, name = app.getLz('term.newPlaylist')) { @@ -1257,7 +1278,7 @@ const app = new Vue({ } } ).then(res => { - self.refreshPlaylists() + self.refreshPlaylists(false, false) }) }, copyToClipboard(str) { @@ -1301,7 +1322,7 @@ const app = new Vue({ }) self.sortPlaylists() setTimeout(() => { - app.refreshPlaylists() + app.refreshPlaylists(false, false) }, 8000) }) }, @@ -1318,6 +1339,9 @@ const app = new Vue({ if (found) { self.playlists.listing.splice(self.playlists.listing.indexOf(found), 1) } + setTimeout(() => { + app.refreshPlaylists(false, false); + }, 8000); }) } }, @@ -1671,11 +1695,11 @@ const app = new Vue({ params["meta[albums:tracks]"] = 'popularity' params["fields[albums]"] = "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialNotes,editorialVideo,name,playParams,releaseDate,url,copyright" } - if(kind.includes("playlist") || kind.includes("album")){ + if (kind.includes("playlist") || kind.includes("album")){ app.page = (kind) + "_" + (id); window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}` app.getTypeFromID((kind), (id), (isLibrary), params); - }else{ + } else { app.page = (kind) window.location.hash = `${kind}/${id}${isLibrary ? "/" + isLibrary : ''}` } @@ -2563,7 +2587,7 @@ const app = new Vue({ }) self.sortPlaylists() setTimeout(() => { - app.refreshPlaylists() + app.refreshPlaylists(false, false) }, 13000) }) }, @@ -3830,6 +3854,15 @@ const app = new Vue({ // tracks are found in relationship.data }, + setAirPlayCodeUI() { + this.modals.airplayPW = true + }, + sendAirPlaySuccess(){ + notyf.success('Device paired successfully!'); + }, + sendAirPlayFailed(){ + notyf.error('Device paring failed!'); + }, windowFocus(val) { if (val) { document.querySelectorAll(".animated-artwork-video").forEach(el => { @@ -4009,7 +4042,8 @@ const app = new Vue({ } } - if (app.mk.nowPlayingItem._container["attributes"] && app.mk.nowPlayingItem._container.name != "station") { + const nowPlayingContainer = app.mk.nowPlayingItem._container; + if (nowPlayingContainer && nowPlayingContainer["attributes"] && nowPlayingContainer.name != "station") { menus.normal.items.find(x => x.id == "showInMusic").hidden = false } @@ -4096,13 +4130,19 @@ const app = new Vue({ if (flag) { this.tmpWidth = window.innerWidth; this.tmpHeight = window.innerHeight; + this.tmpX = window.screenX; + this.tmpY = window.screenY; ipcRenderer.send('unmaximize'); ipcRenderer.send('windowmin', 250, 250) + if (this.miniTmpX !== '' && this.miniTmpY !== '') ipcRenderer.send('windowmove', this.miniTmpX, this.miniTmpY) ipcRenderer.send('windowresize', 300, 300, false) app.appMode = 'mini'; } else { + this.miniTmpX = window.screenX; + this.miniTmpY = window.screenY; ipcRenderer.send('windowmin', 844, 410) ipcRenderer.send('windowresize', this.tmpWidth, this.tmpHeight, false) + ipcRenderer.send('windowmove', this.tmpX, this.tmpY) ipcRenderer.send('windowontop', false) //this.cfg.visual.miniplayer_top_toggle = true; app.appMode = 'player'; diff --git a/src/renderer/main/wsapi_interop.js b/src/renderer/main/wsapi_interop.js index d9477a23..4439d956 100644 --- a/src/renderer/main/wsapi_interop.js +++ b/src/renderer/main/wsapi_interop.js @@ -108,11 +108,11 @@ const wsapi = { app.mk.isPlaying ? app.mk.pause() : app.mk.play() }, toggleRepeat() { - if(MusicKit.getInstance().repeatMode == 0) { + if (MusicKit.getInstance().repeatMode == 0) { MusicKit.getInstance().repeatMode = 1 - }else if(MusicKit.getInstance().repeatMode == 1){ + } else if (MusicKit.getInstance().repeatMode == 1){ MusicKit.getInstance().repeatMode = 2 - }else{ + } else { MusicKit.getInstance().repeatMode = 0 } }, diff --git a/src/renderer/style.css b/src/renderer/style.css index 180d1296..3448d228 100644 --- a/src/renderer/style.css +++ b/src/renderer/style.css @@ -7936,6 +7936,15 @@ fieldset:disabled .btn { filter: contrast(0); background-repeat: no-repeat; } +.cd-mediaitem-list-item .playIcon{ + width: 44px; + margin-left: 11px; +} +.cd-mediaitem-list-item .heart-icon { + position: absolute; + filter: contrast(0); + background-repeat: no-repeat; +} @keyframes load-bar { 10% { box-shadow: inset 0 -4px 0; @@ -12935,6 +12944,7 @@ body[platform='darwin'] #window-controls-container { } body[platform='darwin'] .app-chrome .app-chrome-item > .app-mainmenu { opacity: 0; + width: 52px; pointer-events: none; -webkit-app-region: drag; } diff --git a/src/renderer/style.less b/src/renderer/style.less index 13e7de3b..b24ce657 100644 --- a/src/renderer/style.less +++ b/src/renderer/style.less @@ -1207,6 +1207,14 @@ body[platform="darwin"] .app-chrome .app-chrome-item > .window-controls > div.cl background-repeat: no-repeat; margin-left: 3px; } + + .heart-icon { + height: 9px; + width: 13px; + filter: contrast(0); + background-repeat: no-repeat; + margin-left: 3px; + } } .lossless-icon { @@ -3281,6 +3289,7 @@ body[platform='darwin'] { .app-chrome .app-chrome-item > .app-mainmenu { opacity: 0; + width: 52px; pointer-events: none; -webkit-app-region: drag; } diff --git a/src/renderer/todo.js b/src/renderer/todo.js index 3e8deaf9..4c95d8ed 100644 --- a/src/renderer/todo.js +++ b/src/renderer/todo.js @@ -133,11 +133,11 @@ app.mkapi("artists", false, "412778295", { var library = [] var downloaded = null; function downloadChunk () { - if(downloaded == null) { + if (downloaded == null) { app.mk.api.library.songs("", {limit: 100}, {includeResponseMeta: !0}).then((response)=>{ processChunk(response) }) - }else{ + } else { downloaded.next("", {limit: 100}, {includeResponseMeta: !0}).then((response)=>{ processChunk(response) }) diff --git a/src/renderer/views/app/chrome-bottom.ejs b/src/renderer/views/app/chrome-bottom.ejs index 4e88e841..e27a6e13 100644 --- a/src/renderer/views/app/chrome-bottom.ejs +++ b/src/renderer/views/app/chrome-bottom.ejs @@ -101,7 +101,7 @@ :class="{'active': this.cfg.audio.volume == 0}"> + v-b-tooltip.hover :title="formatVolumeTooltip()">
+ v-b-tooltip.hover :title="formatVolumeTooltip()">
- -
diff --git a/src/renderer/views/components/airplay-modal.ejs b/src/renderer/views/components/airplay-modal.ejs new file mode 100644 index 00000000..16ab91a4 --- /dev/null +++ b/src/renderer/views/components/airplay-modal.ejs @@ -0,0 +1,42 @@ + + \ No newline at end of file diff --git a/src/renderer/views/components/artist-chip.ejs b/src/renderer/views/components/artist-chip.ejs index 12453996..9c2d884b 100644 --- a/src/renderer/views/components/artist-chip.ejs +++ b/src/renderer/views/components/artist-chip.ejs @@ -29,7 +29,7 @@ template: '#artist-chip', async mounted() { let artistId = this.item.id - if(typeof this.item.relationships == "object") { + if (typeof this.item.relationships == "object") { artistId = this.item.relationships.catalog.data[0].id } app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/artists/${artistId}`).then(response => { diff --git a/src/renderer/views/components/audio-controls.ejs b/src/renderer/views/components/audio-controls.ejs index 57a174e6..657a01d6 100644 --- a/src/renderer/views/components/audio-controls.ejs +++ b/src/renderer/views/components/audio-controls.ejs @@ -14,7 +14,7 @@
+ step="2" v-model="volume"/>
diff --git a/src/renderer/views/components/audio-settings.ejs b/src/renderer/views/components/audio-settings.ejs index c8e0c5d4..249d49ca 100644 --- a/src/renderer/views/components/audio-settings.ejs +++ b/src/renderer/views/components/audio-settings.ejs @@ -54,7 +54,7 @@ app.modals.audioSettings = false }, openSpatialAudio() { - if(app.cfg.audio.spatial === true && app.cfg.audio.maikiwiAudio.spatial === false) { + if (app.cfg.audio.spatial === true && app.cfg.audio.maikiwiAudio.spatial === false) { app.modals.spatialProperties = true app.modals.audioSettings = false } else { diff --git a/src/renderer/views/components/castmenu.ejs b/src/renderer/views/components/castmenu.ejs index 7ab727f3..afe4ff39 100644 --- a/src/renderer/views/components/castmenu.ejs +++ b/src/renderer/views/components/castmenu.ejs @@ -37,7 +37,7 @@
- {{true ? 'Homepods only for now! (NO PASSWORD PLEASE!)' : 'Please add FFmpeg location in Settings -> Advanced'}} + {{'EXPERIMENTAL!!! Supports Homepods / Apple TVs / Shairport for now, AirPlay on Samsung/LG/Sony devices will be added later'}}