diff --git a/.circleci/config.yml b/.circleci/config.yml index e02b98b2..48037d62 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -80,6 +80,12 @@ jobs: sudo dpkg --add-architecture i386 sudo apt-get update -y sudo apt-get install -y wine32 + - run: + name: Reinstall proper rust node module + command: | + cd ./node_modules/cider_utils + yarn run prebuild-downloads --platform=win32 --verbose + cd ../.. - run: name: Generate Builds (Windows) command: yarn electron-builder -w --x64 -p never @@ -105,6 +111,12 @@ jobs: sudo dpkg --add-architecture i386 sudo apt-get update -y sudo apt-get install -y wine32 + - run: + name: Reinstall proper rust node module + command: | + cd ./node_modules/cider_utils + yarn run prebuild-downloads --platform=win32 --verbose + cd ../.. - run: name: Generate Builds (Winget) command: yarn electron-builder --win -c winget.json -p never @@ -147,6 +159,7 @@ jobs: - run: name: Publish Release command: | + echo "Creating release for Cider v${APP_VERSION} on the ${CIRCLE_BRANCH} branch." gh release create "v${APP_VERSION}" --title "Cider Version ${APP_VERSION} (${CIRCLE_BRANCH})" --generate-notes -R ciderapp/cider-releases ~/Cider/dist/artifacts/*.deb ~/Cider/dist/artifacts/*.AppImage ~/Cider/dist/artifacts/*.snap ~/Cider/dist/artifacts/*.exe ~/Cider/dist/artifacts/*.yml ~/Cider/dist/artifacts/*.blockmap # Orchestrate our job run sequence diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 21c3e7b2..f47f1d37 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -96,6 +96,7 @@ jobs: cp resources/macPackager.js node_modules/app-builder-lib/out/macPackager.js rm -r node_modules/pouchdb-node/node_modules/leveldown rm -r node_modules/pouchdb-adapter-leveldb/node_modules/leveldown + rm -r /node_modules/leveldown/node_modules/node-gyp-build || true yarn dist:universalNotWorking -p never # - name: Perform CodeQL Analysis # uses: github/codeql-action/analyze@v1 diff --git a/.gitignore b/.gitignore index 99ecd4d6..fc8bf102 100644 --- a/.gitignore +++ b/.gitignore @@ -330,3 +330,4 @@ savedconfig/config.json savedconfig/session.json savedconfig/window-state.json src/main/base/sample.json + diff --git a/package.json b/package.json index 24db95e8..69ed419c 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "leveldown": "^6.1.1", "mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git", "mpris-service": "^2.1.2", - "music-metadata": "^7.12.3", + "music-metadata": "^7.12.4", "node-gyp": "^9.0.0", "node-ssdp": "^4.0.1", "pouchdb-adapter-leveldb": "^7.3.0", @@ -80,7 +80,8 @@ "wallpaper": "5.0.1", "ws": "^8.5.0", "xml2js": "^0.4.23", - "youtube-search-without-api-key": "^1.0.7" + "youtube-search-without-api-key": "^1.0.7", + "cider_utils": "git+https://github.com/ciderapp/cider_utils" }, "devDependencies": { "@types/adm-zip": "^0.5.0", @@ -121,9 +122,9 @@ } ], "build": { - "electronVersion": "18.3.3", + "electronVersion": "18.3.5", "electronDownload": { - "version": "18.3.3+wvcus", + "version": "18.3.5+wvcus", "mirror": "https://github.com/castlabs/electron-releases/releases/download/v" }, "appId": "cider", diff --git a/resources/afterPack.js b/resources/afterPack.js index dd9eda0d..8e3ff50f 100644 --- a/resources/afterPack.js +++ b/resources/afterPack.js @@ -24,9 +24,10 @@ exports.default = function(context) { // execSync('python3 -m castlabs_evs.vmp -n sign-pkg dist/mac',{stdio: 'inherit'}) // if (fs.existsSync('dist/mac-arm64')) // execSync('python3 -m castlabs_evs.vmp -n sign-pkg dist/mac-arm64 -z',{stdio: 'inherit'}) - // if (fs.existsSync('dist/mac-x64')) - // execSync('python3 -m castlabs_evs.vmp -n sign-pkg dist/mac-x64',{stdio: 'inherit'}) + + if (fs.existsSync('dist/mac-x64') || fs.existsSync('dist/mac-universal--x64') ) + execSync('cd ./node_modules/cider_utils; yarn run prebuild-downloads --platform=darwin --arch=arm64 --verbose; cd ../..',{stdio: 'inherit'}) // console.log('VMP signing complete') -} \ No newline at end of file +} diff --git a/src/i18n/README.md b/src/i18n/README.md index 182e730b..cf5f1de4 100644 --- a/src/i18n/README.md +++ b/src/i18n/README.md @@ -515,3 +515,9 @@ Update 21/06/2022 20:39 UTC Update 23/06/2022 04:00 UTC * `settings.option.connectivity.lastfmScrobble.filterTypes`: Added to `en_US` + + +Update 03/07/2022 20:00 UTC + +* `term.plugins`: Added to `en_US` +* `settings.header.visual.styles`: Added to `en_US` \ No newline at end of file diff --git a/src/i18n/de_DE.json b/src/i18n/de_DE.json index dd3b4970..8f86cdf6 100644 --- a/src/i18n/de_DE.json +++ b/src/i18n/de_DE.json @@ -394,5 +394,10 @@ "action.cut": "Ausschneiden", "action.paste": "Einfügen", "action.selectAll": "Alles auswählen", - "action.delete": "Löschen" + "action.delete": "Löschen", + "home.syncFavorites": "Sync Favoriten", + "term.quit" : "Beenden", + "settings.option.connectivity.lastfmScrobble.filterLoop.description": "Verhindert, dass geloopte Titel gescrobbelt oder in der (Hört Gerade)-Liste auf Last.fm angezeigt werden", + "settings.option.connectivity.lastfmScrobble.filterTypes": "Medientypen filtern (Last.fm)", + "settings.option.connectivity.lastfmScrobble.manualToken": "Last.fm-Token manuell eingeben" } diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json index c16b717a..d8b8d5a8 100644 --- a/src/i18n/en_US.json +++ b/src/i18n/en_US.json @@ -31,6 +31,10 @@ "term.miniplayer": "MiniPlayer", "term.history": "History", "term.search": "Search", + "term.scroll": "Scroll Mode", + "term.scroll.infinite": "Infinite", + "term.scroll.paged": "${songsPerPage} per page", + "term.live": "LIVE", "term.showSearch": "Show search bar", "term.hideSearch": "Hide search bar", "term.library": "Library", @@ -90,9 +94,6 @@ "term.size": "Size", "term.size.normal": "Normal", "term.size.compact": "Compact", - "term.scroll": "Scroll Mode", - "term.scroll.infinite": "Infinite", - "term.scroll.paged": "${songsPerPage} per page", "term.enable": "Enable", "term.disable": "Disable", "term.enabled": "Enabled", @@ -139,7 +140,6 @@ "term.recentStations": "Recent Stations", "term.personalStations": "Personal Stations", "term.amLive": "Apple Music Live", - "term.live": "LIVE", "term.language": "Language", "term.funLanguages": "Fun", "term.noLyrics": "Instrumental Track / No Lyrics.", @@ -184,8 +184,9 @@ "term.top": "Top", "term.version": "Version", "term.noVideos": "No videos found.", - "term.plugin": "Plug-in", - "term.pluginMenu": "Plug-in Menu", + "term.plugins": "Plugins", + "term.plugin": "Plugin", + "term.pluginMenu": "Plugins Menu", "term.pluginMenu.none": "No interactive plugins", "term.replay": "Replay", "term.uniqueAlbums": "Unique Albums", @@ -200,7 +201,7 @@ "term.confirmLogout": "Are you sure you want to logout?", "term.creditDesignedBy": "Designed by ${authorUsername}", "term.discNumber": "Disc ${discNumber}", - "term.reload" : "Reload Cider ?", + "term.reload" : "Reload Cider?", "term.toggleprivate" : "Toggle Private Session", "term.webremote" : "Web Remote", "term.cast" : "Cast", @@ -213,6 +214,9 @@ "term.nowPlaying": "Now Playing", "home.syncFavorites": "Sync Favorites", "home.syncFavorites.gettingArtists": "Getting Favorited Artists...", + "action.favorite": "Favorite", + "action.removeFavorite": "Remove Favorite", + "action.refresh": "Refresh", "home.title": "Home", "home.recentlyPlayed": "Recently Played", "home.recentlyAdded": "Recently Added", @@ -233,8 +237,6 @@ "podcast.episodes": "Episodes", "podcast.playEpisode": "Play Episode", "podcast.website": "Podcast Website", - "action.favorite": "Favorite", - "action.removeFavorite": "Remove Favorite", "action.hideLibrary": "Hide Library", "action.showLibrary": "Show Library", "action.cut": "Cut", @@ -308,7 +310,6 @@ "action.createNew": "Create New...", "action.openArtworkInBrowser": "Open artwork in browser", "action.scrollToTop": "Scroll to top", - "action.refresh": "Refresh", "menubar.options.view": "View", "menubar.options.reload": "Reload", "menubar.options.forcereload": "Force Reload", @@ -421,9 +422,9 @@ "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "Jasmine Macchiato", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "Hokkaido Milk Tea", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500A": "Moonlight Softcake", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "Brown Sugar Creme Brûlée Milk", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500B": "Clafoutis aux Cerises", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500C": "Uji Matcha Mochi", - "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "Brown Sugar Creme Brûlée Milk", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE": "Cuddle Warmth", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™️", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Enhances the perceived audio quality of AAC encoded audio by using a real-time algorithm that takes advantage of both psychoacoustic models of human hearing and AAC encoding characteristics.", @@ -457,15 +458,10 @@ "settings.header.visual": "Visual", "settings.header.visual.description": "Adjust the visual settings for Cider.", "settings.option.visual.windowStyle": "Window Style", - "settings.option.visual.customAccentColor": "Custom Accent Color", - "settings.option.visual.accentColor": "Accent Color", - "settings.option.visual.purplePodcastPlaybackBar": "Purple Playback Bar for Podcasts", - "settings.option.visual.windowColor": "Window Tint Color", "settings.option.visual.windowBackgroundStyle": "Window Background Style", "settings.header.visual.windowBackgroundStyle.none": "None", "settings.header.visual.windowBackgroundStyle.artwork": "Artwork", "settings.header.visual.windowBackgroundStyle.image": "Image", - "settings.header.visual.windowBackgroundStyle.color": "Color Tint", "settings.option.visual.animatedArtwork": "Animated Artwork", "settings.header.visual.animatedArtwork.always": "Always", "settings.header.visual.animatedArtwork.limited": "Limited to pages and special entries", @@ -491,6 +487,7 @@ "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.header.visual.styles": "Styles", "settings.option.visual.theme.manageStyles": "Manage Styles", "settings.option.visual.theme.uninstall": "Uninstall", "settings.option.visual.theme.viewInfo": "View Info", @@ -559,11 +556,17 @@ "settings.option.experimental.unknownPlugin.description": "Allow installation of plugins from repos other than the Cider Plugin Repository", "settings.option.experimental.compactUI": "Compact UI", "settings.option.window.close_button_hide": "Close Button Should Hide the Application", + "settings.option.window.maxElementScale": "Maximum Element Scale", "settings.option.experimental.inline_playlists": "Inline Playlists and Albums", "settings.option.advanced.playlistTrackMapping": "Playlist Track Mapping", "settings.option.advanced.playlistTrackMapping.description": "Enables deep scanning of playlists to determine which tracks are in which playlists. Playlist cache build times can increase significantly.", "settings.option.visual.transparent": "Transparent frame", "settings.option.visual.transparent.description": "needs Theme Support, requires relaunch", + "settings.option.visual.customAccentColor": "Custom Accent Color", + "settings.option.visual.accentColor": "Accent Color", + "settings.option.visual.purplePodcastPlaybackBar": "Purple Playback Bar for Podcasts", + "settings.option.visual.windowColor": "Window Tint Color", + "settings.header.visual.windowBackgroundStyle.color": "Color Tint", "settings.header.advanced": "Advanced", "settings.header.connect": "Sync", "settings.option.connect.link_account": "Enable Sync with Cider Connect", @@ -640,4 +643,4 @@ "oobe.visual.suggestingThemes.community3": "Dracula", "oobe.visual.suggestingThemes.community3.text": "The iconic Dracula color scheme.", "oobe.amsignin.title": "" -} +} \ No newline at end of file diff --git a/src/i18n/source/en_US.json b/src/i18n/source/en_US.json index 256b937d..b01419a5 100644 --- a/src/i18n/source/en_US.json +++ b/src/i18n/source/en_US.json @@ -31,6 +31,10 @@ "term.miniplayer": "MiniPlayer", "term.history": "History", "term.search": "Search", + "term.scroll": "Scroll Mode", + "term.scroll.infinite": "Infinite", + "term.scroll.paged": "${songsPerPage} per page", + "term.live": "LIVE", "term.showSearch": "Show search bar", "term.hideSearch": "Hide search bar", "term.library": "Library", @@ -180,8 +184,9 @@ "term.top": "Top", "term.version": "Version", "term.noVideos": "No videos found.", - "term.plugin": "Plug-in", - "term.pluginMenu": "Plug-in Menu", + "term.plugins": "Plugins", + "term.plugin": "Plugin", + "term.pluginMenu": "Plugins Menu", "term.pluginMenu.none": "No interactive plugins", "term.replay": "Replay", "term.uniqueAlbums": "Unique Albums", @@ -196,7 +201,7 @@ "term.confirmLogout": "Are you sure you want to logout?", "term.creditDesignedBy": "Designed by ${authorUsername}", "term.discNumber": "Disc ${discNumber}", - "term.reload" : "Reload Cider ?", + "term.reload" : "Reload Cider?", "term.toggleprivate" : "Toggle Private Session", "term.webremote" : "Web Remote", "term.cast" : "Cast", @@ -206,6 +211,12 @@ "term.zoomout" : "Zoom Out", "term.zoomreset" : "Reset Zoom", "term.fullscreen" : "Fullscreen", + "term.nowPlaying": "Now Playing", + "home.syncFavorites": "Sync Favorites", + "home.syncFavorites.gettingArtists": "Getting Favorited Artists...", + "action.favorite": "Favorite", + "action.removeFavorite": "Remove Favorite", + "action.refresh": "Refresh", "home.title": "Home", "home.recentlyPlayed": "Recently Played", "home.recentlyAdded": "Recently Added", @@ -410,6 +421,8 @@ "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "Hokkaido Milk Tea", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500A": "Moonlight Softcake", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "Brown Sugar Creme Brûlée Milk", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500B": "Clafoutis aux Cerises", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500C": "Uji Matcha Mochi", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE": "Cuddle Warmth", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider Adrenaline Processor™️", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "Enhances the perceived audio quality of AAC encoded audio by using a real-time algorithm that takes advantage of both psychoacoustic models of human hearing and AAC encoding characteristics.", @@ -472,6 +485,7 @@ "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.header.visual.styles": "Styles", "settings.option.visual.theme.manageStyles": "Manage Styles", "settings.option.visual.theme.uninstall": "Uninstall", "settings.option.visual.theme.viewInfo": "View Info", @@ -540,11 +554,17 @@ "settings.option.experimental.unknownPlugin.description": "Allow installation of plugins from repos other than the Cider Plugin Repository", "settings.option.experimental.compactUI": "Compact UI", "settings.option.window.close_button_hide": "Close Button Should Hide the Application", + "settings.option.window.maxElementScale": "Maximum Element Scale", "settings.option.experimental.inline_playlists": "Inline Playlists and Albums", "settings.option.advanced.playlistTrackMapping": "Playlist Track Mapping", "settings.option.advanced.playlistTrackMapping.description": "Enables deep scanning of playlists to determine which tracks are in which playlists. Playlist cache build times can increase significantly.", "settings.option.visual.transparent": "Transparent frame", "settings.option.visual.transparent.description": "needs Theme Support, requires relaunch", + "settings.option.visual.customAccentColor": "Custom Accent Color", + "settings.option.visual.accentColor": "Accent Color", + "settings.option.visual.purplePodcastPlaybackBar": "Purple Playback Bar for Podcasts", + "settings.option.visual.windowColor": "Window Tint Color", + "settings.header.visual.windowBackgroundStyle.color": "Color Tint", "settings.header.advanced": "Advanced", "settings.header.connect": "Sync", "settings.option.connect.link_account": "Enable Sync with Cider Connect", @@ -621,4 +641,4 @@ "oobe.visual.suggestingThemes.community3": "Dracula", "oobe.visual.suggestingThemes.community3.text": "The iconic Dracula color scheme.", "oobe.amsignin.title": "" -} +} \ No newline at end of file diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json index 9e726cb9..100546db 100644 --- a/src/i18n/zh_CN.json +++ b/src/i18n/zh_CN.json @@ -2,7 +2,7 @@ "i18n.languageName": "简体中文(中国)", "i18n.languageNameEnglish": "Simp. Chinese (China)", "i18n.category": "main", - "i18n.authors": "@notmaikiwi @BillKerman @jay900604", + "i18n.authors": "@notmaikiwi @BillKerman @jay900604 @sakura0224", "app.name": "Cider", "date.format": "${y}年${m}月${d}日", "dialog.cancel": "取消", @@ -21,17 +21,22 @@ "term.accountSettings": "账户设置", "term.logout": "退出登录", "term.login": "登录", - "term.quickNav":"快速导航", - "term.cast":"投射", + "term.quickNav": "快速导航", "term.about": "关于", "term.privateSession": "隐身聆听", - "term.disablePrivateSession":"停止隐身聆听", - "term.autoplay":"自动播放", - "term.lyrics": "歌词", + "term.disablePrivateSession": "停止隐身聆听", "term.queue": "待播清单", - "term.history": "历史记录", + "term.autoplay": "自动播放", + "term.lyrics": "歌词", "term.miniplayer": "迷你播放器", + "term.history": "历史记录", "term.search": "搜索", + "term.scroll": "滚动模式", + "term.scroll.infinite": "无限制", + "term.scroll.paged": "每页${songsPerPage}首", + "term.live": "LIVE", + "term.showSearch": "显示搜索栏", + "term.hideSearch": "隐藏搜索栏", "term.library": "资料库", "term.listenNow": "现在就听", "term.browse": "浏览", @@ -42,23 +47,24 @@ "term.artists": "艺人", "term.podcasts": "播客", "term.playlists": "播放列表", - "term.charts":"排行榜", + "term.charts": "排行榜", "term.playlist": "播放列表", "term.newPlaylist": "新播放列表", "term.newPlaylistFolder": "新播放列表文件夹", "term.createNewPlaylist": "新建播放列表", "term.createNewPlaylistFolder": "新建播放列表文件夹", "term.deletePlaylist": "您确定要删除该播放列表吗?", - "term.navigateBack":"上一页", - "term.navigateForward":"下一页", + "term.navigateBack": "上一页", + "term.navigateForward": "下一页", "term.play": "播放", + "term.playpause": "播放/暂停", "term.pause": "暂停", "term.stop": "停止", "term.previous": "上一首", "term.next": "下一首", "term.shuffle": "随机播放", - "term.enableShuffle":"开启随机播放", - "term.disableShuffle":"关闭随机播放", + "term.enableShuffle": "开启随机播放", + "term.disableShuffle": "关闭随机播放", "term.repeat": "循环播放", "term.enableRepeatOne": "开启单曲循环", "term.disableRepeatOne": "关闭单曲循环", @@ -67,7 +73,7 @@ "term.mute": "静音", "term.unmute": "解除静音", "term.share": "分享", - "term.share.success": "已拷貝到剪贴板", + "term.share.success": "已拷贝到剪贴板", "term.settings": "设置", "term.seeAll": "查看全部", "term.sortBy": "排序", @@ -77,14 +83,14 @@ "term.sortBy.genre": "类型", "term.sortBy.releaseDate": "发行日期", "term.sortBy.duration": "时长", - "term.sortBy.dateAdded":"加入日期", + "term.sortBy.dateAdded": "加入日期", "term.sortOrder": "字母排序", "term.sortOrder.ascending": "升序", "term.sortOrder.descending": "倒序", "term.viewAs": "显示模式", "term.viewAs.coverArt": "专辑封面", "term.viewAs.list": "列表", - "term.dynamic":"动态", + "term.dynamic": "动态", "term.size": "大小", "term.size.normal": "正常", "term.size.compact": "紧凑", @@ -106,6 +112,8 @@ "term.time.added": "添加于", "term.time.released": "发行于", "term.time.updated": "更新于", + "term.time.days": "天", + "term.time.day": "天", "term.time.hours": "小时", "term.time.hour": "小时", "term.time.minutes": "分钟", @@ -118,8 +126,8 @@ "term.audioSettings": "音频设置", "term.clearAll": "清空", "term.recentStations": "最近播放的广播", - "term.personalStations":"最近播放的个人广播", - "term.amLive":"amLive", + "term.personalStations": "最近播放的个人广播", + "term.amLive": "Apple Music Live", "term.language": "语言", "term.funLanguages": "恶搞", "term.noLyrics": "加载中... / 无搜索结果 / 纯音乐", @@ -133,13 +141,11 @@ "term.contributors": "贡献者", "term.equalizer": "均衡器", "term.reset": "重置", - "term.track": { - "one": "首歌曲", - "other": "首歌曲" - }, "term.tracks": "歌曲", + "term.track": "首歌曲", "term.videos": "音乐视频", "term.menu": "菜单", + "term.themeManaged": "由主题所管理", "term.check": "检查", "term.aboutArtist": "关于{{artistName}}", "term.topResult": "热门搜索结果", @@ -155,30 +161,47 @@ "term.song.link.generate": "获取 song.link 共享链接...", "term.musicVideos": "音乐视频", "term.stations": "电台", + "term.curators": "策展人", + "term.appleCurators": "Apple 策展人", "term.radioShows": "广播单集", "term.recordLabels": "唱片公司", "term.videoExtras": "视频特辑", - "term.top":"顶部", + "term.top": "顶部", "term.version": "版本", - "term.noVideos":"无视频", + "term.noVideos": "无视频", + "term.plugins": "插件", "term.plugin": "插件", "term.pluginMenu": "插件菜单", "term.pluginMenu.none": "沒有交互式插件", - "term.replay":"音乐回忆", - "term.uniqueAlbums":"Unique Albums", - "term.uniqueArtists":"Unique Artists", - "term.uniqueSongs":"Unique Songs", - "term.topArtists":"热门艺人", - "term.listenedTo":"听过", - "term.times":"次", - "term.topAlbums":"热门专辑", - "term.plays":"次", - "term.topGenres":"热门类型", - "term.confirmLogout":"你确定要退出登录吗?", - "term.creditDesignedBy":"由 ${authorUsername} 设计", - "term.discNumber":"碟 ${discNumber}", + "term.replay": "音乐回忆", + "term.uniqueAlbums": "独特专辑", + "term.uniqueArtists": "超绝艺人", + "term.uniqueSongs": "别致单曲", + "term.topArtists": "热门艺人", + "term.listenedTo": "听过", + "term.times": "次", + "term.topAlbums": "热门专辑", + "term.plays": "次", + "term.topGenres": "热门类型", + "term.confirmLogout": "你确定要退出登录吗?", + "term.creditDesignedBy": "由 ${authorUsername} 设计", + "term.discNumber": "碟 ${discNumber}", + "term.reload" : "重新载入 Cider?", + "term.toggleprivate": "切换隐身聆听", + "term.webremote": "远程控制", + "term.cast": "投射", + "term.cast2" : "投射到设备", + "term.quit" : "退出应用", + "term.zoomin" : "放大", + "term.zoomout" : "缩小", + "term.zoomreset" : "重置缩放", + "term.fullscreen" : "全屏模式", + "term.nowPlaying": "正在播放", "home.syncFavorites": "同步喜爱艺人", "home.syncFavorites.gettingArtists": "获取喜爱艺人...", + "action.favorite": "喜爱", + "action.removeFavorite": "取消喜爱", + "action.refresh": "刷新", "home.title": "主页", "home.recentlyPlayed": "最近播放", "home.recentlyAdded": "最近添加", @@ -199,16 +222,15 @@ "podcast.episodes": "单集", "podcast.playEpisode": "播放单集", "podcast.website": "Podcast 网站", - "action.favorite":"喜爱", - "action.removeFavorite":"取消喜爱", - "action.hideLibrary":"隐藏资料库", - "action.showLibrary":"显示资料可查", - "action.cut":"剪切", - "action.paste":"粘贴", - "action.selectAll":"全选", - "action.delete":"删除", + "action.hideLibrary": "隐藏资料库", + "action.showLibrary": "显示资料库", + "action.cut": "剪切", + "action.paste": "粘贴", + "action.selectAll": "全选", + "action.delete": "删除", "action.edit": "编辑", "action.done": "完成", + "action.submit": "提交", "action.editTracklist": "编辑歌曲清单", "action.addToLibrary": "加入资料库", "action.addToLibrary.success": "成功加入资料库", @@ -224,7 +246,7 @@ "action.createPlaylist": "新建播放列表", "action.addToPlaylist": "添加到播放列表", "action.removeFromPlaylist": "从播放列表移除", - "action.addToFavorites": "加至收藏", + "action.addToFavorites": "添加至收藏", "action.follow": "关注", "action.follow.success": "已关注", "action.follow.error": "尝试关注的过程发生了错误", @@ -236,7 +258,7 @@ "action.startRadio": "开始电台", "action.goToArtist": "前往艺人", "action.goToAlbum": "前往专辑", - "action.showInPlaylist":"在播放列表中显示", + "action.showInPlaylist": "在播放列表中显示", "action.showInAppleMusic": "在 Apple Music 中显示", "action.moveToTop": "移到顶部", "action.share": "分享歌曲", @@ -253,11 +275,7 @@ "action.export": "导出", "action.showAlbum": "显示专辑", "action.tray.minimize": "最小化", - "action.tray.quit": "退出", "action.tray.show": "显示 Cider", - "action.tray.playpause": "播放/暂停", - "action.tray.next": "下一首", - "action.tray.previous": "上一首", "action.tray.listento": "Listen To:", "action.update": "更新", "action.install": "安装", @@ -266,7 +284,7 @@ "action.deletepreset": "删除默认", "action.open": "打开", "action.close": "关闭", - "action.relaunch.confirm":"你想重新启动 Cider 吗?", + "action.relaunch.confirm": "你想重新启动 Cider 吗?", "action.cast.chromecast": "Chromecast", "action.cast.todevices": "投射到设备", "action.cast.stop": "停止投射到所有设备", @@ -277,49 +295,29 @@ "action.createNew": "添加...", "action.openArtworkInBrowser": "在浏览器中打开专辑封面", "action.scrollToTop": "回到顶部", - "menubar.options.about": "关于", - "menubar.options.settings": "设置", - "menubar.options.quit": "退出 Cider", "menubar.options.view": "查看 ", "menubar.options.reload": "重新载入", "menubar.options.forcereload": "强制重新载入", "menubar.options.toggledevtools": "切换开发人员工具", "menubar.options.window": "窗口", "menubar.options.minimize": "最小化", - "menubar.options.toggleprivate": "切换隐身聆听", - "menubar.options.webremote": "远程控制", - "menubar.options.audio": "音频设定", "menubar.options.plugins": "插件目录", "menubar.options.controls": "控制", - "menubar.options.next": "下一首", - "menubar.options.playpause": "播放/暂停", - "menubar.options.previous": "上一首", "menubar.options.volumeup": "增大音量", "menubar.options.volumedown": "减小音量", - "menubar.options.browse": "浏览", - "menubar.options.artists": "艺人", - "menubar.options.search": "搜索", - "menubar.options.albums": "专辑", - "menubar.options.cast": "投射至设备", "menubar.options.account": "账户", - "menubar.options.accountsettings": "账户设置", "menubar.options.signout": "注销", "menubar.options.support": "支持", - "menubar.options.discord": "Discord", - "menubar.options.github": "GitHub Wiki", "menubar.options.report": "报告...", "menubar.options.bug": "Bug", "menubar.options.feature": "功能请求", "menubar.options.trans": "翻译报告/请求", "menubar.options.license": "查看授权", "menubar.options.conf": "在编辑器打开配置文件", - "menubar.options.listennow": "现在就听", - "menubar.options.recentlyAdded": "最近添加", - "menubar.options.songs": "歌曲", + "menubar.options.zoom": "缩放", "settings.header.general": "通用", "settings.header.general.description": "调整 Cider 的通用设置", - "settings.option.audio.volumeStep": "音量改变量", - "settings.option.audio.maxVolume": "最大音量", + "settings.option.general.language": "语言", "settings.option.general.resumebehavior": "恢复行为", "settings.option.general.resumebehavior.description": "会影响你回到 Cider 应用程序时,恢复歌曲的方式。", "settings.option.general.resumebehavior.locally": "本地", @@ -330,61 +328,66 @@ "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": "恶搞语言", "settings.option.general.language.unsorted": "未分类", "settings.option.general.customizeSidebar": "自定义侧边栏的功能", "settings.option.general.customizeSidebar.customize": "自定义", "settings.option.general.keybindings": "快捷操作键", - "settings.option.general.keybindings.pressCombination":"按下两个键组合来更新操作设定。", + "settings.option.general.keybindings.library": "资料库", + "settings.option.general.keybindings.session": "聆听", + "settings.option.general.keybindings.control": "控制", + "settings.option.general.keybindings.interface": "界面", + "settings.option.general.keybindings.advanced": "高级", + "settings.option.general.keybindings.pressCombination": "按下两个键组合来更新操作设定。", "settings.option.general.keybindings.pressEscape": "按下 Esc 键返回。", - "settings.notyf.general.keybindings.update.success":"快捷键更新成功。", - "settings.prompt.general.keybindings.update.success":"快捷键更新成功,按下 OK 重新启动 Cider.", - "settings.option.general.keybindings.open": "打开", - "settings.option.general.themeUpdateNotification":"自动检查主题更新", - "settings.option.general.showLovedTracksInline":"行内显示喜爱曲目", - "settings.description.search":"搜索", - "settings.description.albums":"资料库专辑", - "settings.description.artists":"资料库艺人", - "settings.description.browse":"浏览", - "settings.description.private":"隐身聆听", - "settings.description.remote":"远程控制", - "settings.description.audio":"音频设定", - "settings.description.plugins":"插件目录", - "settings.description.cast":"投射到装置", - "settings.description.settings":"设置", - "settings.description.developer":"开发者", - "settings.description.listnow":"现在就听", - "settings.description.recentAdd":"最近加入", - "settings.description.songs":"歌曲", + "settings.notyf.general.keybindings.update.success": "快捷键更新成功。", + "settings.prompt.general.keybindings.update.success": "快捷键更新成功,按下 OK 重新启动 Cider。", + "settings.option.general.themeUpdateNotification": "自动检查主题更新", + "settings.option.general.showLovedTracksInline": "行内显示喜爱曲目", + "settings.description.search": "搜索", + "settings.description.albums": "资料库专辑", + "settings.description.artists": "资料库艺人", + "settings.description.browse": "浏览", + "settings.description.private": "隐身聆听", + "settings.description.remote": "远程控制", + "settings.description.audio": "音频设定", + "settings.description.plugins": "插件目录", + "settings.description.cast": "投射到装置", + "settings.description.settings": "设置", + "settings.description.developer": "开发者", + "settings.description.listnow": "现在就听", + "settings.description.recentAdd": "最近加入", + "settings.description.songs": "歌曲", "settings.notyf.updateCider.update-not-available": "没有可用的更新", "settings.notyf.updateCider.update-downloaded": "更新已成功下载,重启后进行更新", "settings.notyf.updateCider.update-timeout": "更新超时", "settings.header.audio": "音频", "settings.header.audio.description": "调整 Cider 的音频设置", - "settings.option.audio.advanced":"高级功能", - "settings.option.audio.changePlaybackRate":"修改播放速度", - "settings.option.audio.playbackRate":"播放速度", - "settings.option.audio.playbackRate.change":"修改", + "settings.option.audio.volumeStep": "音量改变量", + "settings.option.audio.advanced": "高级功能", + "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": "高解析度无损", "settings.header.audio.quality.hireslossless.description": "(最高 24 位/192 kHz)", "settings.header.audio.quality.lossless": "无损", "settings.header.audio.quality.lossless.description": "(最高 24 位/48 kHz)", "settings.header.audio.quality.high": "高音质", - "settings.header.audio.quality.high.description":"256 kbps", + "settings.header.audio.quality.high.description": "256 kbps", "settings.header.audio.quality.standard": "高效率", - "settings.header.audio.quality.standard.description":"64 kbps", + "settings.header.audio.quality.standard.description": "64 kbps", "settings.option.audio.seamlessTransition": "无缝播放", "settings.option.audio.enableAdvancedFunctionality": "高级音频功能", "settings.option.audio.enableAdvancedFunctionality.description": "打开 AudioContext 将启用类似音量平衡和等化器的高级设置。但这并不一定适合每部电脑,可能会发生音乐卡顿。", - "settings.warn.audio.enableAdvancedFunctionality.lowcores":"您的电脑可能无法处理这些功能, 您确定要继续?", + "settings.warn.audio.enableAdvancedFunctionality.lowcores": "您的电脑可能无法处理这些功能, 您确定要继续?", "settings.option.audio.audioLab": "Cider 音频实验室", "settings.option.audio.audioLab.description": "包含由 Cider 开发团队进行的各种音频优化功能。", - "settings.option.audio.audioLab.subheader":"Designed by Cider Acoustic Technologies in California", + "settings.option.audio.audioLab.subheader": "Designed by Cider Acoustic Technologies in California", "settings.warn.audioLab.withoutAF": "使用 Cider 音频实验室需要打开进阶音频功能才可使用。", - "settings.warn.enableAdvancedFunctionality":"此功能需要开启高级音频功能才可使用。", + "settings.warn.enableAdvancedFunctionality": "此功能需要开启高级音频功能才可使用。", "settings.option.audio.enableAdvancedFunctionality.analogWarmth": "模拟温暖", "settings.option.audio.enableAdvancedFunctionality.analogWarmth.description": "以 Korg Nutube 6P1 为蓝本的模拟温暖。", "settings.option.audio.enableAdvancedFunctionality.analogWarmthIntensity": "模拟温暖强度", @@ -401,18 +404,20 @@ "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E68_2": "宇治抹茶奶茶", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "春毫茉莉玛琪雅朵", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "北海道奶茶", - "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500": "月光软饼干", - "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM":"布蕾黑糖鲜奶", - "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE":"温暖抱抱", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500A": "月光软饼干", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.BSCBM": "布蕾黑糖鲜奶", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500B": "樱桃克拉芙缇", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z8500C": "宇治抹茶麻糬", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.CUDDLE": "温暖抱抱", "settings.option.audio.enableAdvancedFunctionality.ciderPPE": "Cider 数码增强音频处理™️", "settings.option.audio.enableAdvancedFunctionality.ciderPPE.description": "通过人类的听力心理学模型和 AAC 编码特色的即时算法,强化 AAC 音频的感知音频质量。", + "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "数码增强音频处理与空间音频不兼容,请先停用空间音频。", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength": "数码增强音频处理设置", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.description": "将更改音频处理的激进/振奋程度(增强选项有可能会引起杂讯)。", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.standard": "标准", - "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.adaptive":"自适应", - "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.legacy":"传统", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.adaptive": "自适应", + "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.legacy": "传统", "settings.option.audio.enableAdvancedFunctionality.ciderPPEStrength.aggressive": "增强", - "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility":"数码增强音频处理与空间音频不兼容,请先停用空间音频。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization": "音量平衡", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.description": "自动将歌曲播放音量调整到相同水平,享受更舒适的聆听体验。", "settings.option.audio.enableAdvancedFunctionality.audioNormalization.disabled": "此功能由音频实验室管理", @@ -421,28 +426,25 @@ "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider 音频空间配置档", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "变更音频空间的配置档,需重新启动应用程序。", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "标准", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.soundstage":"声场", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation":"分离感", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal":"微调", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.soundstage": "声场", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation": "分离感", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "微调", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "发烧友", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "扩散", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "安可", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "延长版安可", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "音频空间无法与 CAP 相容,请关闭 CAP 在进行操作。", - "settings.option.audio.dbspl.display":"显示 dB SPL(声压)", - "settings.option.audio.dbspl.description":"(专业用户选项) 音量滑动条显示 dB SPL 而非 dBFS.", - "settings.option.audio.dbfs.calibration":"0 dBFS 校正", - "settings.option.audio.dbfs.description":"Enter the peak Z-weighted dB SPL when Cider is at 0 dBFS.", - "settings.option.visual.uiscale": "UI界面大小", + "settings.option.audio.dbspl.display": "显示 dB SPL(声压)", + "settings.option.audio.dbspl.description": "(专业用户选项) 音量滑动条显示 dB SPL 而非 dBFS。", + "settings.option.audio.dbfs.calibration": "0 dBFS 校正", + "settings.option.audio.dbfs.description": "输入当 Cider 为 0 dBFS 时的峰值 Z 加权 dB SPL。", "settings.header.visual": "外观", "settings.header.visual.description": "调整 Cider 的外观", - "settings.option.visual.windowStyle":"窗口风格", - "settings.option.visual.customAccentColor": "自定义强调色", - "settings.option.visual.accentColor": "强调色", - "settings.option.visual.purplePodcastPlaybackBar": "播放播客时使用紫色进度条", - "settings.option.visual.windowColor": "窗口色调", + "settings.option.visual.windowStyle": "窗口风格", "settings.option.visual.windowBackgroundStyle": "窗口背景样式", "settings.header.visual.windowBackgroundStyle.none": "无", "settings.header.visual.windowBackgroundStyle.artwork": "专辑插图", "settings.header.visual.windowBackgroundStyle.image": "图像", - "settings.header.visual.windowBackgroundStyle.color":"色调", "settings.option.visual.animatedArtwork": "动态专辑插图", "settings.header.visual.animatedArtwork.always": "总是显示", "settings.header.visual.animatedArtwork.limited": "只在艺人页面和专辑插图显示", @@ -458,20 +460,22 @@ "settings.option.visual.hardwareAcceleration.description": "需要重启 Cider 才会生效", "settings.header.visual.hardwareAcceleration.default": "默认", "settings.header.visual.hardwareAcceleration.webGPU": "WebGPU", + "settings.option.visual.uiscale": "UI界面大小", "settings.header.visual.theme": "主题", "settings.option.visual.theme.github.download": "从 GitHub 链接安装", - "settings.option.visual.theme.github.openfolder":"开启主题文件夹", + "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.header.visual.theme.github.page": "GitHub 上的主题", + "settings.option.visual.theme.github.install.confirm": "你确定要安装 {{ repo }}", "settings.prompt.visual.theme.github.URL": "输入您要安装的窗口主题链接", - "settings.prompt.visual.theme.uninstallTheme":"你确定要删除 {{ theme }}", - "settings.option.visual.theme.checkForUpdates":"检查更新", - "settings.option.visual.theme.manageStyles":"管理风格", - "settings.option.visual.theme.uninstall":"卸载", - "settings.option.visual.theme.viewInfo":"查看信息", - "settings.option.visual.theme.github.available":"可使用的主题", - "settings.option.visual.theme.github.applied":"已应用", + "settings.prompt.visual.theme.uninstallTheme": "你确定要删除 {{ theme }}", + "settings.option.visual.theme.checkForUpdates": "检查更新", + "settings.header.visual.styles": "主题", + "settings.option.visual.theme.manageStyles": "管理主题", + "settings.option.visual.theme.uninstall": "卸载", + "settings.option.visual.theme.viewInfo": "查看信息", + "settings.option.visual.theme.github.available": "可使用的主题", + "settings.option.visual.theme.github.applied": "已应用", "settings.notyf.visual.theme.install.success": "主题成功安装", "settings.notyf.visual.theme.install.error": "主题安装失败", "settings.header.visual.plugin": "插件", @@ -503,21 +507,27 @@ "settings.option.lyrics.enableQQLyrics": "启用 QQ 音乐的歌词", "settings.header.connectivity": "外部连接", "settings.header.connectivity.description": "调整 Cider 与外部应用的交互设置", - "settings.option.connectivity.discordRPC": "Discord 动态", "settings.option.connectivity.playbackNotifications": "歌曲播放通知", + "settings.option.connectivity.discordRPC": "Discord 动态", "settings.option.connectivity.discordRPC.clientName": "应用程序名称", "settings.option.connectivity.discordRPC.clearOnPause": "暂停时清除 Discord 动态", "settings.option.connectivity.discordRPC.hideButtons": "隐藏 Discord 动态上的按钮", - "settings.option.connectivity.discordRPC.hideTimestamp":"隐藏 Discord 动态上的时间戳", + "settings.option.connectivity.discordRPC.hideTimestamp": "隐藏 Discord 动态上的时间戳", "settings.option.connectivity.discordRPC.detailsFormat": "详细信息格式", "settings.option.connectivity.discordRPC.stateFormat": "动态格式", - "settings.option.connectivity.discordRPC.reload":"重新加载 DiscordRPC", - "settings.option.connectivity.discordRPC.reconnectedToUser":"DiscordRPC 重新连接至用户: {{user}} ({{userid}})", + "settings.option.connectivity.discordRPC.reload": "重新加载 DiscordRPC", + "settings.option.connectivity.discordRPC.reconnectedToUser": "DiscordRPC 重新连接至用户: {{user}} ({{userid}})", "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.option.connectivity.lastfmScrobble.filterLoop.description": "防止循环单曲被记录或展示在Last.FM 的正在播放列表中。", + "settings.option.connectivity.lastfmScrobble.filterTypes": "过滤媒体类型 (Last.fm)", + "settings.option.connectivity.lastfmScrobble.manualToken": "手动输入 Last.fm 验证码", + "settings.notyf.connectivity.lastfmScrobble.connectError": "Last.fm 连接超时", + "settings.notyf.connectivity.lastfmScrobble.connectSuccess": "Last.fm 连接成功", + "settings.notyf.connectivity.lastfmScrobble.connecting": "正在连接至 Last.fm...", "settings.header.debug": "Debug", "settings.option.debug.copy_log": "拷贝日志至剪贴板", "settings.option.debug.openAppData": "打开 Cider 程序文件夹", @@ -529,15 +539,21 @@ "settings.option.experimental.unknownPlugin.description": "允许从 Cider 来源以外的 repo 安装插件", "settings.option.experimental.compactUI": "紧凑型 UI", "settings.option.window.close_button_hide": "关闭按钮将 Cider 隐藏至系统栏", + "settings.option.window.maxElementScale": "最大元素比例", "settings.option.experimental.inline_playlists": "将播放列表做为行内元素显示", "settings.option.advanced.playlistTrackMapping": "播放列表追踪映射", "settings.option.advanced.playlistTrackMapping.description": "打开对播放列表的深度扫描,以确认歌曲在哪些播放列表中。但播放列表加载时间会显著增加。", "settings.option.visual.transparent": "透明窗口框架", "settings.option.visual.transparent.description": "需主题有支持透明框架,且须重新启动才会生效。", + "settings.option.visual.customAccentColor": "自定义强调色", + "settings.option.visual.accentColor": "强调色", + "settings.option.visual.purplePodcastPlaybackBar": "播放播客时使用紫色进度条", + "settings.option.visual.windowColor": "窗口色调", + "settings.header.visual.windowBackgroundStyle.color": "色调", "settings.header.advanced": "高级", - "settings.header.connect":"同步", - "settings.option.connect.link_account":"开启 Cider Connect 同步", - "settings.option.connect.link_account.description":"将您的 Discord 帐户与 Cider Connect 关联后,您可以储存用户资料,包括设定、均衡器,并在后续版本中加入更多可同步选项。(正在更新中)", + "settings.header.connect": "同步", + "settings.option.connect.link_account": "开启 Cider Connect 同步", + "settings.option.connect.link_account.description": "将您的 Discord 帐户与 Cider Connect 关联后,您可以储存用户资料,包括设定、均衡器,并在后续版本中加入更多可同步选项。(正在更新中)", "spatial.notTurnedOn": "请在设置中开启空间音频。", "spatial.spatialProperties": "空间属性", "spatial.width": "宽度", @@ -560,54 +576,54 @@ "settings.header.unfinished": "未完成", "remote.web.title": "Cider 远程控制", "remote.web.description": "扫描以下的二维码以控制 Cider", + "share.platform.twitter.tweet": "在 Apple Music 上聆听 {{song}}。 \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": "电子邮件", + "share.platform.songLink": "复制 song.link 链接", + "share.platform.clipboard": "复制到剪贴板", "about.thanks": "郑重感谢 Cider Collective 以及为这个项目提供支持的贡献者。", - "share.platform.twitter.tweet":"在 Apple Music 上聆听 {{song}}。 \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":"电子邮件", - "share.platform.songLink":"复制 song.link 链接", - "share.platform.clipboard":"复制到剪贴板", - "oobe.yes":"好的", - "oobe.no":"不", - "oobe.next":"下一步", - "oobe.previous":"上一步", - "oobe.done":"完成", - "oobe.amupsell.title":"在我们开始之前", - "oobe.amupsell.text":"使用 Cider 需要付费的 Apple Music 订阅。\nCider 不能在 Apple Music Voice 计划或某些促销试用订阅状态下使用。 如果您已经订阅 Apple Music,请点击下一步继续。", - "oobe.amupsell.subscribeBtn":"订阅 Apple Music", - "oobe.amupsell.explainBtn":"这是什么?", - "oobe.amupsell.subscribeUrl":"https://apple.co/3MdqJVQ", - "oobe.amupsell.amWebUrl":"https://beta.music.apple.com/", - "oobe.amupsell.promoExplained":"Cider 无法获取部分促销活动与非美区 Apple Muisc 试用状态下的网络播放器API. 要验证您的试用订阅是否能够在Cider内使用, 点击{{ amWebUrl }}, 登陆后尝试播放音乐。如果能够播放,您就可以使用 Cider 了!否则请考虑订阅 Apple Music 服务: {{ subscribeUrl }}。", - "oobe.intro.title":"欢迎使用 Cider", - "oobe.intro.subtitle":"", - "oobe.intro.text":"为了按您喜欢的方式使用 Cider ,请先完成一些设置。您之后可以随时改变这些设置。", - "oobe.general.title":"通用设置", - "oobe.general.subtitle":"", - "oobe.general.text":"", - "oobe.audio.title":"音频设置", - "oobe.audio.subtitle":"", - "oobe.audio.text":"Cider 能够自定义调整和设置的音频属性,提供丰富的高品质音频体验,包括Cider Adrenaline Processor,气氛实现器和空间音频。要启用这些功能,必须启用 \"高级音频功能\"。", - "oobe.audio.advancedFunctionality":"", - "oobe.visual.title":"外观设置", - "oobe.visual.subtitle":"", - "oobe.visual.text":"", - "oobe.visual.layout.text":"Cider 拥有两种不同的窗口布局。Maverick 是一个类似 iTunes 的布局,播放器在窗口的顶部。Mojave 是由 Cider 团队设计的一种新的布局。您可以在设置中随时改变布局。", - "oobe.visual.suggestingThemes":"主题能够个性化您的播放器。以下是推荐的几个主题:", - "oobe.visual.suggestingThemes.subtext":"(主题会从 GitHub 上下载)", - "oobe.visual.suggestingThemes.default":"Cider", - "oobe.visual.suggestingThemes.default.text":"传统的 Cider 主题。", - "oobe.visual.suggestingThemes.dark":"Dark", - "oobe.visual.suggestingThemes.dark.text":"暗黑模式。", - "oobe.visual.suggestingThemes.community1":"Groovy", - "oobe.visual.suggestingThemes.community1.text":"类 WinUI 主题。", - "oobe.visual.suggestingThemes.community2":"iTheme", - "oobe.visual.suggestingThemes.community2.text":"基于 MacOS Monterey 的 Apple Music bata 主题。", - "oobe.visual.suggestingThemes.community3":"Dracula", - "oobe.visual.suggestingThemes.community3.text":"著名的德古拉吸血鬼主题。", + "oobe.yes": "好的", + "oobe.no": "不", + "oobe.next": "下一步", + "oobe.previous": "上一步", + "oobe.done": "完成", + "oobe.amupsell.title": "在我们开始之前", + "oobe.amupsell.text": "使用 Cider 需要付费的 Apple Music 订阅。\nCider 不能在 Apple Music Voice 计划或某些促销试用订阅状态下使用。 如果您已经订阅 Apple Music,请点击下一步继续。", + "oobe.amupsell.subscribeBtn": "订阅 Apple Music", + "oobe.amupsell.explainBtn": "这是什么?", + "oobe.amupsell.subscribeUrl": "https://apple.co/3MdqJVQ", + "oobe.amupsell.amWebUrl": "https://beta.music.apple.com/", + "oobe.amupsell.promoExplained": "Cider 无法获取部分促销活动与非美区 Apple Muisc 试用状态下的网络播放器API. 要验证您的试用订阅是否能够在Cider内使用, 点击{{ amWebUrl }}, 登陆后尝试播放音乐。如果能够播放,您就可以使用 Cider 了!否则请考虑订阅 Apple Music 服务: {{ subscribeUrl }}。", + "oobe.intro.title": "欢迎使用 Cider", + "oobe.intro.subtitle": "", + "oobe.intro.text": "为了按您喜欢的方式使用 Cider ,请先完成一些设置。您之后可以随时改变这些设置。", + "oobe.general.title": "通用设置", + "oobe.general.subtitle": "", + "oobe.general.text": "", + "oobe.audio.title": "音频设置", + "oobe.audio.subtitle": "", + "oobe.audio.text": "Cider 能够自定义调整和设置的音频属性,提供丰富的高品质音频体验,包括Cider Adrenaline Processor,气氛实现器和空间音频。要启用这些功能,必须启用 \"高级音频功能\"。", + "oobe.audio.advancedFunctionality": "", + "oobe.visual.title": "外观设置", + "oobe.visual.subtitle": "", + "oobe.visual.text": "", + "oobe.visual.layout.text": "Cider 拥有两种不同的窗口布局。Maverick 是一个类似 iTunes 的布局,播放器在窗口的顶部。Mojave 是由 Cider 团队设计的一种新的布局。您可以在设置中随时改变布局。", + "oobe.visual.suggestingThemes": "主题能够个性化您的播放器。以下是推荐的几个主题:", + "oobe.visual.suggestingThemes.subtext": "(主题会从 GitHub 上下载)", + "oobe.visual.suggestingThemes.default": "Cider", + "oobe.visual.suggestingThemes.default.text": "经典的 Cider 主题。", + "oobe.visual.suggestingThemes.dark": "Dark", + "oobe.visual.suggestingThemes.dark.text": "暗黑模式。", + "oobe.visual.suggestingThemes.community1": "Groovy", + "oobe.visual.suggestingThemes.community1.text": "类 WinUI 主题。", + "oobe.visual.suggestingThemes.community2": "iTheme", + "oobe.visual.suggestingThemes.community2.text": "经典的苹果风主题。", + "oobe.visual.suggestingThemes.community3": "Dracula", + "oobe.visual.suggestingThemes.community3.text": "著名的德古拉吸血鬼主题。", "oobe.amsignin.title": "" } diff --git a/src/i18n/zh_TW.json b/src/i18n/zh_TW.json index 4ce84e9c..6d30e581 100644 --- a/src/i18n/zh_TW.json +++ b/src/i18n/zh_TW.json @@ -135,6 +135,7 @@ "term.videos": "音樂錄影帶", "term.menu": "選單", "term.check": "檢查", + "term.themeManaged": "此功能現在由主題管理。", "term.aboutArtist": "關於{{artistName}}", "term.requestError": "請求發生錯誤。", "term.song.link.generate": "正在取得 song.link 的分享網址...", @@ -142,8 +143,10 @@ "term.version": "版本", "term.creditDesignedBy": "由 ${authorUsername} 設計", "term.plugin": "模組", + "term.plugins": "模組", "term.pluginMenu": "模組選單", "term.pluginMenu.none": "沒有交互式模組", + "term.fullscreen" : "全螢幕模式", "home.title": "首頁", "home.recentlyPlayed": "最近播放", "home.recentlyAdded": "最近加入", @@ -232,6 +235,7 @@ "action.cast.scanning": "尋找中...", "action.createNew": "新增...", "action.refresh": "重新整理", + "menubar.options.reload": "重新載入", "settings.header.general": "一般", "settings.header.general.description": "調整 Cider 的一般設定", "settings.option.general.resumebehavior": "還原行為", @@ -345,6 +349,7 @@ "settings.option.visual.theme.github.explore": "探索 GitHub 上的主題", "settings.prompt.visual.theme.github.URL": "輸入你要安裝的主題網址", "settings.option.visual.theme.checkForUpdates": "檢查更新", + "settings.header.visual.styles": "主題", "settings.option.visual.theme.manageStyles": "管理主題", "settings.option.visual.theme.uninstall": "移除", "settings.option.visual.theme.viewInfo": "查看資訊", @@ -366,6 +371,7 @@ "settings.option.visual.showPersonalInfo": "顯示個人檔案", "settings.header.window": "視窗", "settings.header.window.description": "調整 Cider 的視窗設定", + "settings.option.window.maxElementScale": "最大元素比例", "settings.option.window.openOnStartup": "開機時,啟動 Cider ", "settings.option.window.openOnStartup.hidden": "啟動時,自動隱藏至系統列", "settings.option.window.useNativeTitleBar": "使用原生視窗標題列", diff --git a/src/main/base/browserwindow.ts b/src/main/base/browserwindow.ts index 60715685..b946cb8f 100644 --- a/src/main/base/browserwindow.ts +++ b/src/main/base/browserwindow.ts @@ -64,7 +64,7 @@ export class BrowserWindow { "pages/groupings", "pages/charts", "pages/settings", - "pages/installed-themes", + //"pages/installed-themes", "pages/listen_now", "pages/radio", "pages/home", @@ -80,13 +80,12 @@ export class BrowserWindow { "pages/about", "pages/library-videos", "pages/remote-pair", - "pages/themes-github", - "pages/plugins-github", + //"pages/themes-github", + //"pages/plugins-github", "pages/replay", "pages/audiolabs", "pages/zoo", "pages/plugin-renderer", - "pages/keybinds", "pages/oobe", "components/app-content", "components/sidebar", @@ -126,7 +125,11 @@ export class BrowserWindow { "components/hello-world", "components/inline-collection-list", "components/settings-window", - "components/pagination" + "components/pagination", + "components/settings-keybinds", + "components/settings-themes", + "components/settings-themes-github", + "components/settings-plugins-github", ], appRoutes: [ { @@ -276,6 +279,10 @@ export class BrowserWindow { page: "replay", component: ``, condition: `$root.page == 'replay'` + }, { + page: "keydinds", + component: ``, + condition: `$root.page == 'keybinds-settings'` } ] }, @@ -1563,4 +1570,4 @@ export class BrowserWindow { server2.start(); console.log('remote broadcasted') } -} +} \ No newline at end of file diff --git a/src/main/base/store.ts b/src/main/base/store.ts index c111c73b..af3e60f7 100644 --- a/src/main/base/store.ts +++ b/src/main/base/store.ts @@ -244,12 +244,13 @@ export class Store { "windowColor": "#000000", "customAccentColor": false, "accentColor": "#fc3c44", - "purplePodcastPlaybackBar": false + "purplePodcastPlaybackBar": false, + "maxElementScale": -1 // -1 default, anything else is a custom scale }, "lyrics": { - "enable_mxm": false, + "enable_mxm": true, "mxm_karaoke": false, - "mxm_language": "en", + "mxm_language": "disabled", "enable_qq": false, "enable_yt": false, }, @@ -258,7 +259,7 @@ export class Store { "experiments": [], "playlistTrackMapping": true, "ffmpegLocation": "", - "disableLogging": false + "disableLogging": true }, "connectUser": { "auth": null, diff --git a/src/main/plugins/discordrpc.ts b/src/main/plugins/discordrpc.ts index cdd78144..9c606d17 100644 --- a/src/main/plugins/discordrpc.ts +++ b/src/main/plugins/discordrpc.ts @@ -52,22 +52,50 @@ export default class DiscordRPC { const self = this this.connect(); console.debug(`[Plugin][${this.name}] Ready.`); - ipcMain.on('updateRPCImage', (_event, imageurl) => { + ipcMain.on('updateRPCImage', async (_event, imageurl) => { if (!this._utils.getStoreValue("general.privateEnabled")) { - fetch('https://api.cider.sh/v1/images', { + let b64data = "" + let postbody = "" + if (imageurl.startsWith("/ciderlocalart")){ + let port = await _win.webContents.executeJavaScript( + `app.clientPort` + ); + console.log("http://localhost:"+port+imageurl) + const response = await fetch("http://localhost:"+port+imageurl) + b64data = (await response.buffer()).toString('base64'); + postbody = JSON.stringify({data: b64data}) + 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) + method: 'POST', + body: postbody, + 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) + }) + } else { + postbody = JSON.stringify({url: imageurl}) + fetch('https://api.cider.sh/v1/images', { + + method: 'POST', + body: postbody, + 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) + }) + } + } }) ipcMain.on("reloadRPC", () => { @@ -88,6 +116,7 @@ export default class DiscordRPC { }) } + /** * Runs on app stop */ diff --git a/src/main/plugins/lastfm.ts b/src/main/plugins/lastfm.ts index 33796c84..027f997c 100644 --- a/src/main/plugins/lastfm.ts +++ b/src/main/plugins/lastfm.ts @@ -7,10 +7,7 @@ export default class lastfm { public version: string = '2.0.0'; public author: string = 'Core (Cider Collective)'; - /** - * Private variables for interaction in plugins - */ - private _attributes: any; + private _apiCredentials = { key: "f9986d12aab5a0fe66193c559435ede3", secret: "acba3c29bd5973efa38cc2f0b63cc625" @@ -31,31 +28,36 @@ export default class lastfm { constructor(utils: any) { this._utils = utils; - this.initializeLastFM("", this._apiCredentials) } onReady(_win: Electron.BrowserWindow): void { + this.initializeLastFM("", this._apiCredentials) // Register the ipcMain handlers this._utils.getIPCMain().handle('lastfm:url', (event: any) => { - console.debug(`${lastfm.name}:url`) + console.debug(`[${lastfm.name}:url] Called.`) return this._lfm.getAuthenticationUrl({"cb": "cider://auth/lastfm"}) }) this._utils.getIPCMain().on('lastfm:auth', (event: any, token: string) => { - console.debug(`${lastfm.name}:auth`, token) + console.debug(`[${lastfm.name}:auth] Token: `, token) this.authenticateLastFM(token) }) this._utils.getIPCMain().on('lastfm:disconnect', (_event: any) => { this._lfm.setSessionCredentials(null, null); this._authenticated = false; - console.debug(`${lastfm.name}:disconnect`) + console.debug(`[${lastfm.name}:disconnect] Disconnected`) }) this._utils.getIPCMain().on('lastfm:nowPlayingChange', (event: any, attributes: any) => { - if (this._utils.getStoreValue("connectivity.lastfm.filter_loop")) return; - this.onNowPlayingItemDidChange(attributes) + if (this._utils.getStoreValue("connectivity.lastfm.filter_loop") || this._utils.getStoreValue("general.privateEnabled")) return; + this.updateNowPlayingTrack(attributes) + }) + + this._utils.getIPCMain().on('lastfm:scrobbleTrack', (event: any, attributes: any) => { + if (this._utils.getStoreValue("general.privateEnabled")) return; + this.scrobbleTrack(attributes) }) } @@ -64,22 +66,15 @@ export default class lastfm { * @param attributes Music Attributes (attributes.status = current state) */ onPlaybackStateDidChange(attributes: object): void { - this._attributes = attributes - // this.scrobbleTrack(attributes) } /** * Runs on song change * @param attributes Music Attributes + * @param scrobble */ - onNowPlayingItemDidChange(attributes: any): void { + onNowPlayingItemDidChange(attributes: any, scrobble = false): void { if (this._utils.getStoreValue("general.privateEnabled")) return; - this._attributes = attributes - if (!attributes?.lfmTrack || !attributes?.lfmAlbum) { - this.verifyTrack(attributes) - return - } - this.scrobbleTrack(attributes) this.updateNowPlayingTrack(attributes) } @@ -90,6 +85,7 @@ export default class lastfm { * @private */ private initializeLastFM(token: string, api: { key: string, secret: string }): void { + console.debug(`[${lastfm.name}:initialize] Initializing LastFM`) const LastfmAPI = require("lastfmapi") this._lfm = new LastfmAPI({ 'api_key': api.key, @@ -113,7 +109,7 @@ export default class lastfm { if (!token) return; this._lfm.authenticate(token, (err: any, session: any) => { if (err) { - console.error(err); + console.error(`[${lastfm.name}:authenticate] Error: ${typeof err === "string" ? err : err.message}`); this._utils.getWindow().webContents.executeJavaScript(`app.notyf.error("${err.message}");`) return; @@ -127,37 +123,36 @@ export default class lastfm { /** * Verifies the track information with lastfm * @param attributes + * @param callback * @private */ - private verifyTrack(attributes: any): object { + private verifyTrack(attributes: any, callback: Function): void { if (!attributes) return attributes; if (!attributes.lfmAlbum) { - return this._lfm.album.getInfo({ + this._lfm.album.getInfo({ "artist": attributes.artistName, "album": attributes.albumName }, (err: any, data: any) => { if (err) { console.error(`[${lastfm.name}] [album.getInfo] Error: ${typeof err === "string" ? err : err.message}`) - console.error(err) return {}; } if (data) { attributes.lfmAlbum = data + callback(attributes) } - this.onNowPlayingItemDidChange(attributes) }) } else { - return this._lfm.track.getCorrection(attributes.artistName, attributes.name, (err: any, data: any) => { + this._lfm.track.getCorrection(attributes.artistName, attributes.name, (err: any, data: any) => { if (err) { console.error(`[${lastfm.name}] [track.getCorrection] Error: ${typeof err === "string" ? err : err.message}`) - console.error(err) return {}; } if (data) { attributes.lfmTrack = data.correction.track + callback(attributes) } - this.onNowPlayingItemDidChange(attributes) }) } @@ -170,44 +165,53 @@ export default class lastfm { * @private */ private scrobbleTrack(attributes: any): void { - if (!this._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._scrobbleCache.track === attributes.lfmTrack.name)) return; - - if (this._scrobbleDelay) { - clearTimeout(this._scrobbleDelay); + if (!attributes?.lfmTrack || !attributes?.lfmAlbum) { + this.verifyTrack(attributes, (a: any) => { + this.scrobbleTrack(a) + }) + return } - // Scrobble delay - this._scrobbleDelay = setTimeout(() => { + if (!this._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._scrobbleCache.track === attributes.lfmTrack.name)) return; - // Scrobble - const scrobble = { - 'artist': attributes.lfmTrack.artist.name, - 'track': attributes.lfmTrack.name, - 'album': attributes.lfmAlbum.name, - 'albumArtist': attributes.lfmAlbum.artist, - 'timestamp': new Date().getTime() / 1000, - 'trackNumber': attributes.trackNumber, - 'duration': attributes.durationInMillis / 1000, + // Scrobble + const scrobble = { + 'artist': attributes.lfmTrack.artist.name, + 'track': attributes.lfmTrack.name, + 'album': attributes.lfmAlbum.name, + 'albumArtist': attributes.lfmAlbum.artist, + 'timestamp': new Date().getTime() / 1000, + 'trackNumber': attributes.trackNumber, + 'duration': attributes.durationInMillis / 1000, + } + + // Easy Debugging + console.debug(`[${lastfm.name}:scrobble] Scrobbling ${scrobble.artist} - ${scrobble.track}`) + + // Scrobble the track + this._lfm.track.scrobble(scrobble, (err: any, _res: any) => { + if (err) { + console.error(`[${lastfm.name}:scrobble] Scrobble failed: ${err.message}`); + } else { + console.debug(`[${lastfm.name}:scrobble] Track scrobbled: ${scrobble.artist} - ${scrobble.track}`); + this._scrobbleCache = scrobble } - - // Easy Debugging - if (!this._utils.getApp().isPackaged) { - console.debug(scrobble) - } - - // Scrobble the track - this._lfm.track.scrobble(scrobble, (err: any, _res: any) => { - if (err) { - console.error(`[${lastfm.name}:scrobble] Scrobble failed: ${err.message}`); - } else { - console.debug(`[${lastfm.name}:scrobble] Track scrobbled: ${scrobble.artist} - ${scrobble.track}`); - this._scrobbleCache = scrobble - } - }); - }, Math.round(attributes.durationInMillis * Math.min((this._utils.getStoreValue("connectivity.lastfm.scrobble_after") / 100), 0.8))) + }); } + /** + * Updates the now playing track + * @param attributes + * @private + */ private updateNowPlayingTrack(attributes: any): void { + if (!attributes?.lfmTrack || !attributes?.lfmAlbum) { + this.verifyTrack(attributes, (a: any) => { + this.updateNowPlayingTrack(a) + }) + return + } + if (!this._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._nowPlayingCache.track === attributes.lfmTrack.name)) return; const nowPlaying = { @@ -223,7 +227,6 @@ export default class lastfm { if (err) { console.error(`[${lastfm.name}:updateNowPlaying] Now Playing Update failed: ${err.message}`); } else { - console.log(res) console.debug(`[${lastfm.name}:updateNowPlaying] Now Playing Updated: ${nowPlaying.artist} - ${nowPlaying.track}`); this._nowPlayingCache = nowPlaying } diff --git a/src/main/providers/local/index.ts b/src/main/providers/local/index.ts index ffd0ed71..157ac85e 100644 --- a/src/main/providers/local/index.ts +++ b/src/main/providers/local/index.ts @@ -6,8 +6,7 @@ import * as mm from 'music-metadata'; import {Md5} from 'ts-md5/dist/md5'; import e from "express"; import { EventEmitter } from 'events'; - - +import { parseFile, recursiveFolderSearch } from 'cider_utils'; export class LocalFiles { static localSongs: any = []; @@ -38,21 +37,20 @@ export class LocalFiles { let folders = utils.getStoreValue("libraryPrefs.localPaths") if (folders == null || folders.length == null || folders.length == 0) folders = [] console.log('folders', folders) - let files: any[] = [] + let parseFileQueue: any[] = []; let mmQueue: any[] = [] for (var folder of folders) { - // get files from the Music folder - files = files.concat(await LocalFiles.getFiles(folder)) + // Recursively search and add + let result = await recursiveFolderSearch(folder) + parseFileQueue = parseFileQueue.concat(result.parseFile) + mmQueue = mmQueue.concat(result.musicMetadata) } - - //console.log("cider.files", files2); - let supporttedformats = ["mp3", "aac", "webm", "flac", "m4a", "ogg", "wav", "opus"] - let audiofiles = files.filter(f => supporttedformats.includes(f.substring(f.lastIndexOf('.') + 1))); - // console.log("cider.files2", audiofiles, audiofiles.length); + if (parseFileQueue.length !== 0 || mmQueue.length !== 0) {console.log('Recursive Folder Search in Cider Utils worki')} let metadatalist = [] let metadatalistart = [] let numid = 0; - for (var audio of audiofiles) { + // Music Metadata fallback + for (var audio of mmQueue) { try { const metadata = await mm.parseFile(audio); let lochash = Md5.hashStr(audio) ?? numid; @@ -104,7 +102,7 @@ export class LocalFiles { lossless: metadata.format?.lossless, container: metadata.format?.container, bitDepth: metadata.format?.bitsPerSample ?? 0, - sampleRate: metadata.format?.sampleRate ?? 0, + sampleRate: metadata.format?.sampleRate ?? 0, }, }; let art = { @@ -121,7 +119,80 @@ export class LocalFiles { if (this.localSongs.length === 0 && numid % 10 === 0) { // send updated chunks only if there is no previous database this.eventEmitter.emit('newtracks', metadatalist)} } - } catch (e) { } + } catch (e) {console.error("error:", e)} + } + + // Cider-Utils supported formats. + for (var audio of parseFileQueue) { + try { + const metadata = await parseFile(audio); + let lochash = Md5.hashStr(audio) ?? numid; + if (metadata != null) { + let form = { + "id": "ciderlocal" + lochash, + "_id": "ciderlocal" + lochash, + "type": "podcast-episodes", + "href": audio, + "attributes": { + "artwork": { + "width": 3000, + "height": 3000, + "url": "/ciderlocalart/" + "ciderlocal" + lochash, + }, + "topics": [], + "url": "", + "subscribable": true, + "mediaKind": "audio", + "genreNames": [ + "" + ], + // "playParams": { + // "id": "ciderlocal" + numid, + // "kind": "podcast", + // "isLibrary": true, + // "reporting": false }, + "trackNumber": metadata.track_number ?? 0, + "discNumber": metadata.disc_number ?? 0, + "name": metadata.title ?? audio.substring(audio.lastIndexOf('\\') + 1), + "albumName": metadata.album, + "artistName": metadata.artist, + "copyright": metadata.copyright ?? "", + "assetUrl": "file:///" + audio, + "contentAdvisory": "", + "releaseDateTime": `${metadata.year ?? '2022'}-05-13T00:23:00Z`, + "durationInMillis": metadata.duration_in_ms ?? 0, + "bitrate": metadata.bitrate ?? 0, + "offers": [ + { + "kind": "get", + "type": "STDQ" + } + ], + "contentRating": "clean" + }, + flavor: metadata.bitrate, + localFilesMetadata: { + lossless: metadata.lossless, + container: metadata.container, + bitDepth: metadata.bit_depth, + sampleRate: metadata.sample_rate ?? 0, + }, + }; + let art = { + id: "ciderlocal" + lochash, + _id: "ciderlocalart" + lochash, + url: metadata.artwork != undefined ? metadata.artwork : "", + } + metadatalistart.push(art) + numid += 1; + ProviderDB.db.putIfNotExists(form) + ProviderDB.db.putIfNotExists(art) + metadatalist.push(form) + + if (this.localSongs.length === 0 && numid % 10 === 0) { // send updated chunks only if there is no previous database + this.eventEmitter.emit('newtracks', metadatalist)} + } + } catch (e) {console.error("error:", e)} } this.localSongs = metadatalist; this.localSongsArts = metadatalistart; @@ -173,7 +244,7 @@ export class LocalFiles { // metadata.common.picture[0].data.toString('base64') res.setHeader('Cache-Control', 'public, max-age=31536000'); - res.setHeader('Expires', new Date(Date.now() + 31536000).toUTCString()); + res.setHeader('Expires', new Date(Date.now() + 31536000000).toUTCString()); res.setHeader('Content-Type', 'image/jpeg'); let data = diff --git a/src/renderer/assets/feather/plugins.svg b/src/renderer/assets/feather/plugins.svg new file mode 100644 index 00000000..1ad5759a --- /dev/null +++ b/src/renderer/assets/feather/plugins.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/assets/feather/style.svg b/src/renderer/assets/feather/style.svg new file mode 100644 index 00000000..e6eae9e2 --- /dev/null +++ b/src/renderer/assets/feather/style.svg @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/renderer/assets/ko_fi.svg b/src/renderer/assets/ko_fi.svg index e44aadbe..dcd86131 100644 --- a/src/renderer/assets/ko_fi.svg +++ b/src/renderer/assets/ko_fi.svg @@ -1,57 +1,4 @@ - - - - - - - - + + + diff --git a/src/renderer/index.js b/src/renderer/index.js index 13caca7b..a9aaf86b 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -1,5 +1,9 @@ var notyf = new Notyf(); +function clamp(num, min, max) { + return Math.min(Math.max(num, min), max); +} + const MusicKitObjects = { LibraryPlaylist: function () { this.id = ""; @@ -84,8 +88,14 @@ function fallbackinitMusicKit() { } function initMusicKit() { - + if(!this.responseText) { + console.log("Using stored token") + this.responseText = JSON.stringify({ + token: localStorage.getItem("lastToken") + }) + } let parsedJson = JSON.parse(this.responseText); + localStorage.setItem("lastToken", parsedJson.token); MusicKit.configure({ developerToken: parsedJson.token, app: { @@ -116,7 +126,13 @@ function capiInit() { request.addEventListener("load", initMusicKit); request.onreadystatechange = function (aEvt) { if (request.readyState == 4) { - if (request.status != 200) fallbackinitMusicKit(); + if (request.status != 200) { + if(localStorage.getItem("lastToken") != null) { + initMusicKit() + }else{ + fallbackinitMusicKit() + } + }; } }; request.open("GET", "https://api.cider.sh/v1/"); diff --git a/src/renderer/less/ameframework.less b/src/renderer/less/ameframework.less index de057c2f..fe69d570 100644 --- a/src/renderer/less/ameframework.less +++ b/src/renderer/less/ameframework.less @@ -239,6 +239,12 @@ input[type=range].md-slider::-webkit-slider-runnable-track { width: auto; } +@media only screen and (min-width: 1133px) and (max-width: 1233px) { + .row .col-auto { + display: none !important; + } +} + .col-1 { flex: 0 0 auto; width: 8.33333333%; diff --git a/src/renderer/less/bootstrap.less b/src/renderer/less/bootstrap.less index f420e5aa..c0c8bf72 100644 --- a/src/renderer/less/bootstrap.less +++ b/src/renderer/less/bootstrap.less @@ -2642,6 +2642,12 @@ fieldset:disabled .btn { width: auto; } +@media only screen and (min-width: 1133px) and (max-width: 1241px) { + .row .col-auto { + display: none !important; + } +} + .col-1 { flex : 0 0 auto; width: 8.33333333%; diff --git a/src/renderer/less/elements.less b/src/renderer/less/elements.less index 1b8026f7..975ac5b6 100644 --- a/src/renderer/less/elements.less +++ b/src/renderer/less/elements.less @@ -984,11 +984,11 @@ /* mediaitem-square */ .cd-mediaitem-square { - --transitionDuration: .25s; + --transitionDuration: .5s; --scaleRate: 1.25; --scaleRateArtwork: 1; - width: 200px; - height: 200px; + width: calc(160px * var(--windowRelativeScale)); + height: calc(200px * var(--windowRelativeScale)); display: inline-flex; flex: 0 0 auto; flex-direction: column; @@ -996,14 +996,13 @@ justify-content: center; align-items: center; border-radius: 6px; - transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; .artwork-container { position: relative; .artwork { - height: 150px; - width: 150px; + height: calc(140px * var(--windowRelativeScale)); + width: calc(140px * var(--windowRelativeScale)); background: blue; border-radius: var(--mediaItemRadius); background: var(--artwork); @@ -1011,7 +1010,6 @@ flex: 0 0 auto; margin: 6px; cursor: pointer; - transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; .mediaitem-artwork { box-shadow: unset; @@ -1085,31 +1083,31 @@ } } - &:not(.mediaitem-card):not(.mediaitem-brick):not(.mediaitem-video):not(.noscale) { - @media (min-width: 1460px) { - --scaleRate: 1.1; - --scaleRateArtwork: 0.9; - width: calc(200px * var(--scaleRate)); - height: calc(200px * var(--scaleRate)); + // &:not(.mediaitem-card):not(.mediaitem-brick):not(.mediaitem-video):not(.noscale) { + // @media (min-width: 1460px) { + // --scaleRate: 1.1; + // --scaleRateArtwork: 0.9; + // width: calc(200px * var(--scaleRate)); + // height: calc(200px * var(--scaleRate)); - .artwork-container > .artwork { - width: calc(190px * var(--scaleRateArtwork)); - height: calc(190px * var(--scaleRateArtwork)); - } - } + // .artwork-container > .artwork { + // width: calc(190px * var(--scaleRateArtwork)); + // height: calc(190px * var(--scaleRateArtwork)); + // } + // } - @media (min-width: 1550px) { - --scaleRate: 1.25; - --scaleRateArtwork: 1; - width: calc(200px * var(--scaleRate)); - height: calc(200px * var(--scaleRate)); + // @media (min-width: 1550px) { + // --scaleRate: 1.25; + // --scaleRateArtwork: 1; + // width: calc(200px * var(--scaleRate)); + // height: calc(200px * var(--scaleRate)); - .artwork-container > .artwork { - width: calc(190px * var(--scaleRateArtwork)); - height: calc(190px * var(--scaleRateArtwork)); - } - } - } + // .artwork-container > .artwork { + // width: calc(190px * var(--scaleRateArtwork)); + // height: calc(190px * var(--scaleRateArtwork)); + // } + // } + // } .info-rect { @@ -1161,10 +1159,12 @@ &.mediaitem-video { height: 200px; width: 240px; + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; .artwork { height: 120px; width: 212px; + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; } &:not(.noscale) { @@ -1197,10 +1197,12 @@ &.mediaitem-brick { height: 200px; width: 240px; + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; .artwork { height: 123px; width: 220px; + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; } &:not(.noscale) { @@ -1231,12 +1233,14 @@ } &.mediaitem-small { - width: 140px; - height: 180px; + width: calc(140px, var(--windowRelativeScale)); + height: calc(180px, var(--windowRelativeScale)); + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; .artwork { - height: 128px; - width: 128px; + height: calc(128px, var(--windowRelativeScale)); + width: calc(128px, var(--windowRelativeScale)); + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; } } @@ -1249,6 +1253,7 @@ position: relative; border-radius: calc(var(--mediaItemRadius) * 2); box-shadow: var(--mediaItemShadow-ShadowSubtle); + transition: width var(--transitionDuration) linear, height var(--transitionDuration) linear; .artwork { width: 230px; @@ -1341,7 +1346,7 @@ } &:not(.noscale) { - @media (min-width: 1460px) { + @media (min-width: 1200px) { width: calc(230px * 1.1); height: calc(298px * 1.1); .artwork-container > .artwork { @@ -1350,7 +1355,7 @@ } } - @media (min-width: 1550px) { + @media (min-width: 1400px) { width: calc(230px * 1.25); height: calc(298px * 1.25); .artwork-container > .artwork { diff --git a/src/renderer/less/macos.less b/src/renderer/less/macos.less index b7cd49e4..4819a263 100644 --- a/src/renderer/less/macos.less +++ b/src/renderer/less/macos.less @@ -1,6 +1,6 @@ body[platform="darwin"] { html { - background: transparent!important; + background: transparent !important; } &.notransparency::before { @@ -11,6 +11,7 @@ body[platform="darwin"] { &.simplebg { background: transparent; } + &::before { display: none; } @@ -26,6 +27,7 @@ body[platform="darwin"] { .app-chrome .app-chrome-item.search { margin-right: 12px; } + .app-chrome .app-mainmenu { width: 46px; } @@ -35,15 +37,20 @@ body[platform="darwin"] { } } - // &::after { - // position: fixed; - // top:0;left:0;right:0;bottom:0; - // box-shadow: inset 0px 0px .5px 1px rgb(200 200 200 / 40%); - // border-radius: 10px; - // content: " "; - // z-index: 999999; - // pointer-events: none; - // } + &[window-state="normal"] { + &::after { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + box-shadow: inset 0px 0px .5px 1px rgb(200 200 200 / 40%); + border-radius: 10px; + content: " "; + z-index: 999999; + pointer-events: none; + } + } } #app-main { @@ -58,6 +65,13 @@ body[platform="darwin"] { } } + .settings-window.maxed { + .tabs>.col-auto { + transition: padding-top .3s linear; + padding-top: var(--chromeHeight1); + } + } + #apple-music-video-player-controls #player-exit { margin-top: 18px; left: 70px; diff --git a/src/renderer/less/pages.less b/src/renderer/less/pages.less index c1c057d3..c5b015bc 100644 --- a/src/renderer/less/pages.less +++ b/src/renderer/less/pages.less @@ -23,8 +23,7 @@ .github-themes-page { display: flex; flex-direction: column; - padding: 0px; - height: calc(100% - var(--navigationBarHeight)); + height: 100%; .github-avatar { height: 42px; @@ -52,9 +51,11 @@ .repos-list { height: 100%; - overflow-y: overlay; width: 320px; font-size: 14px; + overflow: overlay; + padding-bottom: 16px; + flex: 0 0 auto; > .list-group { margin: 0px; @@ -76,9 +77,9 @@ .github-preview { height: 100%; flex: 1; - background: var(--color2); padding: 16px 32px; - overflow-y: overlay; + overflow: auto; + padding-bottom: 16px; } .gh-content { @@ -90,6 +91,182 @@ .gh-header { padding: 16px; + height: 64px; + display: grid; + align-content: center; + flex: 0 0 auto; + + .header-text { + position: initial !important; + justify-content: left !important; + } + } + + .installed-themes-page { + .style-editor-container { + height: 100%; + flex: 1; + background: var(--color2); + padding: 0px; + overflow-y: overlay; + + .list-group-item { + border-radius: 0px; + } + } + } +} + +//Styles Page +.installed-themes-page { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; + .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%); + } + + .gh-header { + z-index: 5; + padding: 16px; + flex: 0 0 auto; + height: 64px; + display: grid; + align-content: center; + .header-text { + position: initial !important; + justify-content: left !important; + } + } + + .gh-content { + display: flex; + flex-direction: row; + padding: 0px; + height: 100%; + flex: 0 0 auto; + + .repos-list { + width: 320px; + overflow: overlay; + height: 90%; + font-size: 14px; + white-space: nowrap; + + > .list-group { + margin: 0px; + padding-bottom: 16px; + } + + .list-group-item { + padding: 12px 6px; + + &:hover { + filter: brightness(1.2); + } + + &:active { + filter: brightness(0.8); + } + } + } + + .style-editor-container { + height: 100%; + flex: 1; + padding: 0px; + width: 100%; + overflow: hidden; + + .stylestack-editor { + padding-bottom: 16px; + } + + .list-group-item { + border-radius: 0px; + } + } + } + + .stylestack-editor { + width: 100%; + + .btn, + .btn-group { + width: 100%; + } + + .themeLabel { + 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; + font-weight: bold; + color: var(--textColor); + cursor: pointer; + } + + .stylesDropdown { + > .dropdown-menu { + height: 300px; + overflow-y: overlay; + } + } } } @@ -1053,7 +1230,7 @@ } &.animated .artist-header { - min-height: 500px; + min-height: 80vh; } .artist-header { @@ -1113,6 +1290,24 @@ right: 28px; } + .social-btn { + border-radius: 100%; + background: transparent; + height: 17px; + border: 0px; + cursor: pointer; + z-index: 69; + display: flex; + justify-content: center; + align-items: center; + float: right; + } + @media only screen and (min-width: 1133px) and (max-width: 1277px) { + .social-btn { + display: none !important; + } + } + .animated { width: 100%; height: 100%; @@ -1131,6 +1326,7 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); + object-fit: cover; } } @@ -1273,107 +1469,6 @@ /* Artist Page End */ - -.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%; - - .btn, - .btn-group { - width: 100%; - } - - .themeLabel { - 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; - font-weight: bold; - color: var(--textColor); - cursor: pointer; - } - - .stylesDropdown { - > .dropdown-menu { - height: 300px; - overflow-y: overlay; - } - } - } -} - // Settings page .settings-page { padding: 0px; @@ -1987,7 +2082,7 @@ .settings-window { background: var(--baseColorMix); - max-width: 80%; + max-width: 90%; max-height: 90%; width: 100%; height: 100%; @@ -2024,6 +2119,11 @@ display: flex; gap: 10px; align-items: center; + height: 35px; + + :nth-child(2) { + white-space: nowrap; + } } @@ -2082,6 +2182,11 @@ background-color: rgb(196, 43, 28) } + &.back-btn { + left: 10px; + right: unset; + } + &.minmax-btn { right: 52px; @@ -2109,6 +2214,9 @@ > .col-auto { width: 230px; + overflow-x: hidden; + overflow-y: overlay; + transition: width 0.25s ease-in-out; } .tab-content { @@ -2120,9 +2228,50 @@ overflow-y: overlay; height: 100%; background-color: var(--panelColor2); + padding:0px; padding-top: 48px; border-left: 1px solid var(--borderColor); } + + .github-themes-page, .installed-themes-page { + .header-text { + font-size: 1.25em; + } + } + + .tab-pane { + height:100%; + } + + .settings-tab-content { + height:100%; + } + + &.no-sidebar { + .gh-header { + >.row { + &:last-child { + padding-right: 90px; + } + } + } + .tab-content { + padding-top:0px; + } + + .tabs { + .nav-pills .nav-link { + width: 50px; + :nth-child(2) { + // font-size: 0px; + opacity:0; + } + } + >.col-auto { + width: 80px; + } + } + } } } diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js index c1b4026c..91174618 100644 --- a/src/renderer/main/vueapp.js +++ b/src/renderer/main/vueapp.js @@ -12,6 +12,7 @@ const app = new Vue({ ipcRenderer: ipcRenderer, cfg: ipcRenderer.sendSync("getStore"), isDev: ipcRenderer.sendSync("is-dev"), + clientPort: ipcRenderer.sendSync("get-port"), drawertest: false, platform: "", mk: {}, @@ -705,6 +706,9 @@ const app = new Vue({ } catch (err) { } + // Used to get a scale factor for the window for CSS scaling + window.addEventListener("resize", e => this.setWindowScaleFactor()) + this.setWindowScaleFactor() this.mk._bag.features['seamless-audio-transitions'] = this.cfg.audio.seamless_audio this.mk._bag.features["broadcast-radio"] = true this.mk._services.apiManager.store.storekit._restrictedEnabled = false @@ -915,13 +919,19 @@ const app = new Vue({ } }); + this.mk.addEventListener(MusicKit.Events.playbackProgressDidChange, () => { + if (self.mk.currentPlaybackProgress === (app.cfg.connectivity.lastfm.scrobble_after / 100)) { + ipcRenderer.send('lastfm:scrobbleTrack', MusicKitInterop.getAttributes()); + } + }) + this.mk.addEventListener(MusicKit.Events.playbackStateDidChange, (event) => { ipcRenderer.send('wsapi-updatePlaybackState', wsapi.getAttributes()); document.body.setAttribute("playback-state", event.state == 2 ? "playing" : "paused") }) this.mk.addEventListener(MusicKit.Events.playbackTimeDidChange, (a) => { - self.lyriccurrenttime = self.mk.currentPlaybackTime + app.lyricOffset + // self.lyriccurrenttime = self.mk.currentPlaybackTime - app.lyricOffset this.currentSongInfo = a self.playerLCD.playbackDuration = (self.mk.currentPlaybackTime) // wsapi @@ -1091,6 +1101,18 @@ const app = new Vue({ ipcRenderer.invoke("scanLibrary") }, + setWindowScaleFactor() { + let scale = window.devicePixelRatio * window.innerWidth / 1280 * window.innerHeight / 720 + let desiredScale = clamp(parseFloat(app.cfg.visual.maxElementScale == -1 ? 1.5 : app.cfg.visual.maxElementScale), 1, 1.5) + app.$store.state.windowRelativeScale = scale + if(scale <= 1) { + scale = 1 + }else if(scale >= desiredScale) { + scale = desiredScale + } + document.documentElement.style + .setProperty('--windowRelativeScale', scale); + }, showFoo(querySelector, time) { clearTimeout(this.idleTimer); if (this.idleState == true) { @@ -1120,7 +1142,7 @@ const app = new Vue({ message: `[Themes] ${theme.name} has an update available.` }) notify.on("click", () => { - app.appRoute("themes-github") + app.openSettingsPage("github-themes") notyf.dismiss(notify) }) } @@ -1944,12 +1966,31 @@ const app = new Vue({ }) return; + } else if(item.attributes.link.url.includes("viewFeature")) { + const params = new Proxy(new URLSearchParams(new URL(item.attributes.link.url).search), { + get: (searchParams, prop) => searchParams.get(prop), + }); + id = params.id + app.mk.api.v3.music(`/v1/editorial/vn/multiplex/${id}?art%5Burl%5D=f&format%5Bresources%5D=map&platform=web`).then( + (data) => { + let item = data.data.results?.target ?? [] + app.routeView(item) + } + ) + } else { window.open(item.attributes.link.url) } } - } else if (kind == "multirooms") { + } else if (kind == "multiplex") { + app.mk.api.v3.music(`/v1/editorial/vn/multiplex/${id}?art%5Burl%5D=f&format%5Bresources%5D=map&platform=web`).then( + (data) => { + let item = data.data.results?.target ?? [] + app.routeView(item) + } + ) + }if (kind == "multirooms") { app.getTypeFromID("multiroom", id, false, { platform: "web", extend: "editorialArtwork,uber,lockupStyle" @@ -1990,7 +2031,7 @@ const app = new Vue({ params["fields[artists]"] = "name,url" params["omit[resource]"] = "autos" params["meta[albums:tracks]"] = 'popularity' - params["fields[albums]"] = "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialNotes,editorialVideo,name,playParams,releaseDate,url,copyright" + params["fields[albums]"] = "artistName,artistUrl,artwork,contentRating,editorialArtwork,editorialNotes,editorialVideo,name,playParams,releaseDate,url,copyright,genreNames" } if (kind.includes("playlist") || kind.includes("album")) { app.page = (kind) + "_" + (id); @@ -3019,7 +3060,10 @@ const app = new Vue({ const track = encodeURIComponent((this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.title ?? '' : ''); const artist = encodeURIComponent((this.mk.nowPlayingItem != null) ? this.mk.nowPlayingItem.artistName ?? '' : ''); const time = encodeURIComponent((this.mk.nowPlayingItem != null) ? (Math.round((this.mk.nowPlayingItem.attributes["durationInMillis"] ?? -1000) / 1000) ?? -1) : -1); - const id = encodeURIComponent((this.mk.nowPlayingItem != null) ? app.mk.nowPlayingItem._songId ?? (app.mk.nowPlayingItem["songId"] ?? '') : ''); + let id = null; let vanity_id = null; + if (this.mk.nowPlayingItem != null && app.mk.nowPlayingItem.localFilesMetadata != null) {const id = encodeURIComponent('')} + else {id = encodeURIComponent((this.mk.nowPlayingItem != null) ? (app.mk.nowPlayingItem._songId) ?? (app.mk.nowPlayingItem["songId"] ?? '') : '');} + let lrcfile = ""; let richsync = []; const lang = app.cfg.lyrics.mxm_language // translation language @@ -3027,67 +3071,13 @@ const app = new Vue({ return Math.random().toString(36).replace(/[^a-z]+/g, '').slice(2, 10); } - /* get token */ - function getToken(mode, track, artist, songid, lang, time, id) { - if (attempt > 2) { - app.loadNeteaseLyrics(); - // app.loadAMLyrics(); - } else { - attempt = attempt + 1; - let url = "https://apic-desktop.musixmatch.com/ws/1.1/token.get?app_id=web-desktop-app-v1.0&t=" + revisedRandId(); - let req = new XMLHttpRequest(); - req.overrideMimeType("application/json"); - req.open('GET', url, true); - req.setRequestHeader("authority", "apic-desktop.musixmatch.com"); - req.onload = function () { - try { - let jsonResponse = JSON.parse(this.responseText); - let status2 = jsonResponse["message"]["header"]["status_code"]; - if (status2 == 200) { - let token = jsonResponse["message"]["body"]["user_token"] ?? ''; - if (token != "" && token != "UpgradeOnlyUpgradeOnlyUpgradeOnlyUpgradeOnly") { - console.debug('200 token', mode); - // token good - app.mxmtoken = token; - if (mode == 1) { - getMXMSubs(track, artist, app.mxmtoken, lang, time, id); - } else { - getMXMTrans(songid, lang, app.mxmtoken); - } - } else { - console.debug('fake 200 token'); - getToken(mode, track, artist, songid, lang, time) - } - } else { - // console.log('token 4xx'); - getToken(mode, track, artist, songid, lang, time) - } - } catch (e) { - console.log('error'); - app.loadQQLyrics(); - //app.loadAMLyrics(); - } - }; - req.onerror = function () { - console.log('error'); - app.loadQQLyrics(); - // app.loadAMLyrics(); - }; - req.send(); - } - } - - function getMXMSubs(track, artist, token, lang, time, id) { - let usertoken = encodeURIComponent(token); - let richsyncQuery = (app.cfg.lyrics.mxm_karaoke) ? "&optional_calls=track.richsync" : "" - let timecustom = (!time || (time && time < 0)) ? '' : `&f_subtitle_length=${time}&q_duration=${time}&f_subtitle_length_max_deviation=40`; - let itunesid = (id && id != "") ? `&track_itunes_id=${id}` : ''; - let url = "https://apic-desktop.musixmatch.com/ws/1.1/macro.subtitles.get?format=json&namespace=lyrics_richsynched" + richsyncQuery + "&subtitle_format=lrc&q_artist=" + artist + "&q_track=" + track + itunesid + "&usertoken=" + usertoken + timecustom + "&app_id=web-desktop-app-v1.0&t=" + revisedRandId(); + function getMXMSubs(track, artist, lang, time, id) { + let richsyncQuery = app.cfg.lyrics.mxm_karaoke + let itunesid = (id && id != "") ? id : ''; // Mode 1 -> Subs + let url = "https://api.cider.sh/v1/lyrics?" + "mode=1" + "&richsyncQuery=" + richsyncQuery + "&track=" + track + "&artist=" + artist + "&songID=" + itunesid + "&source=mxm" + "&lang=" + lang + "&time=" + time; let req = new XMLHttpRequest(); req.overrideMimeType("application/json"); - req.open('GET', url, true); - req.setRequestHeader("authority", "apic-desktop.musixmatch.com"); req.onload = function () { try { let jsonResponse = JSON.parse(this.responseText); @@ -3095,11 +3085,13 @@ const app = new Vue({ let status1 = jsonResponse["message"]["header"]["status_code"]; if (status1 == 200) { - let id = ''; + let id, songLang = ''; try { if (jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["header"]["status_code"] == 200 && jsonResponse["message"]["body"]["macro_calls"]["track.subtitles.get"]["message"]["header"]["status_code"] == 200) { id = jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["body"]["track"]["track_id"] ?? ''; lrcfile = jsonResponse["message"]["body"]["macro_calls"]["track.subtitles.get"]["message"]["body"]["subtitle_list"][0]["subtitle"]["subtitle_body"]; + vanity_id = jsonResponse["message"]["body"]["macro_calls"]["matcher.track.get"]["message"]["body"]["track"]["commontrack_vanity_id"]; + songLang = jsonResponse["message"]["body"]["macro_calls"]["track.lyrics.get"]["message"]["body"]["lyrics"]["lyrics_language_description"]; try { let lrcrich = jsonResponse["message"]["body"]["macro_calls"]["track.richsync.get"]["message"]["body"]["richsync"]["richsync_body"]; @@ -3114,7 +3106,7 @@ const app = new Vue({ // app.loadAMLyrics() } else { if (richsync == [] || richsync.length == 0) { - console.log("ok"); + console.log("musixmatch worki"); // process lrcfile to json here app.lyricsMediaItem = lrcfile let u = app.lyricsMediaItem.split(/[\r\n]/); @@ -3155,24 +3147,21 @@ const app = new Vue({ }); app.lyrics = preLrc; } - if (lrcfile != null && lrcfile != '') { - // load translation - getMXMTrans(id, lang, token); - } else { - // app.loadAMLyrics() - app.loadQQLyrics(); + + // Load translation + if (songLang.toLowerCase() !== lang){ + getMXMTrans(lang, vanity_id); } + } } catch (e) { console.log(e); app.loadQQLyrics(); // app.loadAMLyrics() } - } else { //4xx rejected - getToken(1, track, artist, '', lang, time); - } + } } catch (e) { - console.log(e); + console.error(e); app.loadQQLyrics(); //app.loadAMLyrics() } @@ -3182,59 +3171,59 @@ const app = new Vue({ console.log('error'); // app.loadAMLyrics(); }; + req.open('POST', url, true); req.send(); } - function getMXMTrans(id, lang, token) { - if (lang != "disabled" && id != '') { - let usertoken = encodeURIComponent(token); - let url2 = "https://apic-desktop.musixmatch.com/ws/1.1/crowd.track.translations.get?translation_fields_set=minimal&selected_language=" + lang + "&track_id=" + id + "&comment_format=text&part=user&format=json&usertoken=" + usertoken + "&app_id=web-desktop-app-v1.0&t=" + revisedRandId(); - let req2 = new XMLHttpRequest(); - req2.overrideMimeType("application/json"); - req2.open('GET', url2, true); - req2.setRequestHeader("authority", "apic-desktop.musixmatch.com"); - req2.onload = function () { - try { - let jsonResponse2 = JSON.parse(this.responseText); - console.log(jsonResponse2); - let status2 = jsonResponse2["message"]["header"]["status_code"]; - if (status2 == 200) { - try { - let preTrans = [] - let u = app.lyrics; - let translation_list = jsonResponse2["message"]["body"]["translations_list"]; - if (translation_list.length > 0) { - for (var i = 0; i < u.length - 1; i++) { - preTrans[i] = "" - for (var trans_line of translation_list) { - if (u[i].line == " " + trans_line["translation"]["matched_line"] || u[i].line == trans_line["translation"]["matched_line"]) { - u[i].translation = trans_line["translation"]["description"]; - break; - } - } - } - app.lyrics = u; + function getMXMTrans(lang, vanity_id) { + try { + if (lang != "disabled" && vanity_id != '') { // Mode 2 -> Trans + fetch('https://www.musixmatch.com/lyrics/' + vanity_id +'/translation/' + lang, { + method: 'GET', + headers: { + 'Host': 'musixmatch.com', + 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", + 'authority': "www.musixmatch.com" + }, + }) + .then(async (res) => { + if (res.status != 200) {return} + let html = document.createElement('html'); html.innerHTML = await res.text() + let lyric_isolated = html.querySelector("#site > div > div > div > main > div > div > div.mxm-track-lyrics-container > div.container > div > div > div > div.col-sm-12.col-md-10.col-ml-9.col-lg-9 > div.mxm-lyrics.translated > div.row > div.col-xs-12.col-sm-12.col-md-12.col-ml-12.col-lg-12") + let raw_lines = lyric_isolated.getElementsByClassName("col-xs-6 col-sm-6 col-md-6 col-ml-6 col-lg-6") + let applied = 0; + for (let i = 1; applied < app.lyrics.length; i+=2) { // Start on odd elements because even ones are original. + if (raw_lines[i].childNodes[0].childNodes[0].textContent.trim() == "") {i+=2;} + if (app.lyrics[applied].line.trim() == "") {applied+=1;} + if (app.lyrics[applied].line.trim() === raw_lines[i].childNodes[0].childNodes[0].textContent.trim().replace('′', "'")) { + // Do Nothing + applied +=1; } - } catch (e) { - /// not found trans -> ignore + else { + if (app.lyrics[applied].line === "lrcInstrumental") { + if (app.lyrics[applied+1].line.trim() === raw_lines[i].childNodes[0].childNodes[0].textContent.trim()) { + // Do Nothing + applied +=2; + } + else { + app.lyrics[applied+1].translation = raw_lines[i].childNodes[0].childNodes[0].textContent.trim(); + applied +=2; + } + } + else { + app.lyrics[applied].translation = raw_lines[i].childNodes[0].childNodes[0].textContent.trim(); + applied +=1; + } + } } - } else { //4xx rejected - getToken(2, '', '', id, lang, ''); - } - } catch (e) { - } + }) } - req2.send(); - } + } catch (e) {console.debug("Error while parsing MXM Trans: " + e)} } if (track != "" & track != "No Title Found") { - if (app.mxmtoken != null && app.mxmtoken != '') { - getMXMSubs(track, artist, app.mxmtoken, lang, time, id) - } else { - getToken(1, track, artist, '', lang, time); - } + getMXMSubs(track, artist, lang, time, id); } }, loadNeteaseLyrics() { @@ -3976,9 +3965,8 @@ const app = new Vue({ this.currentArtUrl = this.mk.nowPlayingItem._assets[0].artworkURL } try { - document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); - } catch (e) { - } + // document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); + } catch (e) {} } else { let data = await this.mk.api.v3.music(`/v1/me/library/songs/${this.mk.nowPlayingItem.id}`); data = data.data.data[0]; @@ -3990,14 +3978,14 @@ const app = new Vue({ } ipcRenderer.send('updateRPCImage', this.currentArtUrl ?? ''); try { - document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); + // document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); } catch (e) { } } else { this.currentArtUrlRaw = '' this.currentArtUrl = ''; try { - document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); + // document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); } catch (e) { } } @@ -4441,21 +4429,30 @@ const app = new Vue({ case "audiolabs": this.$store.state.pageState.settings.currentTabIndex = 2 break; - case "visual": + case "styles": this.$store.state.pageState.settings.currentTabIndex = 3 break; - case "lyrics": + case "visual": this.$store.state.pageState.settings.currentTabIndex = 4 break; - case "connectivity": + case "github-plugins": this.$store.state.pageState.settings.currentTabIndex = 5 break; - case "advanced": + case "lyrics": this.$store.state.pageState.settings.currentTabIndex = 6 break; - case "keybindings": + case "connectivity": this.$store.state.pageState.settings.currentTabIndex = 7 break; + case "advanced": + this.$store.state.pageState.settings.currentTabIndex = 8 + break; + case "keybindings": + this.$store.state.pageState.settings.currentTabIndex = 9 + break; + case "github-themes": + this.$store.state.pageState.settings.currentTabIndex = 10 + break; } app.modals.settings = true }, diff --git a/src/renderer/main/vuex-store.js b/src/renderer/main/vuex-store.js index e8934d26..8263f065 100644 --- a/src/renderer/main/vuex-store.js +++ b/src/renderer/main/vuex-store.js @@ -1,5 +1,6 @@ const store = new Vuex.Store({ state: { + windowRelativeScale: 1, library: { // songs: ipcRenderer.sendSync("get-library-songs"), // albums: ipcRenderer.sendSync("get-library-albums"), diff --git a/src/renderer/style.less b/src/renderer/style.less index fb08bc70..162261a5 100644 --- a/src/renderer/style.less +++ b/src/renderer/style.less @@ -14,6 +14,7 @@ @import url("less/pages.less"); :root { + --windowRelativeScale: 1; --appleEase: cubic-bezier(0.42, 0, 0.58, 1); --borderColor: rgb(200 200 200 / 16%); --mediaItemShadow: inset 0px 0px 0px 1px rgb(200 200 200 / 16%); @@ -1885,6 +1886,25 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { } } +.social-btn { + border-radius: 100%; + background: transparent; + height: 17px; + border: 0px; + cursor: pointer; + z-index: 69; + display: flex; + justify-content: center; + align-items: center; + float: right; +} + +@media only screen and (min-width: 1133px) and (max-width: 1277px) { + .social-btn { + display: none !important; + } +} + .about-page { .teamBtn { display: flex; @@ -2037,7 +2057,7 @@ input[type="range"].web-slider.display--small::-webkit-slider-thumb { // screen size > 1200px @media screen and (min-width: 1500px) { - grid-template-columns: repeat(6, minmax(200px, 1fr)); + grid-template-columns: repeat(5, minmax(200px, 1fr)); } // less than 1100px @media screen and (max-width: 1150px) { diff --git a/src/renderer/views/app/app-navigation.ejs b/src/renderer/views/app/app-navigation.ejs index 83280293..2f072641 100644 --- a/src/renderer/views/app/app-navigation.ejs +++ b/src/renderer/views/app/app-navigation.ejs @@ -146,7 +146,7 @@ -