diff --git a/.circleci/config.yml b/.circleci/config.yml index 739b7308..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 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 daa903da..69ed419c 100644 --- a/package.json +++ b/package.json @@ -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/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 e7873bef..5924e72f 100644 --- a/src/i18n/en_US.json +++ b/src/i18n/en_US.json @@ -201,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", diff --git a/src/i18n/source/en_US.json b/src/i18n/source/en_US.json index e7873bef..5924e72f 100644 --- a/src/i18n/source/en_US.json +++ b/src/i18n/source/en_US.json @@ -201,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", diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json index 070547b1..837ec4ab 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": "取消", @@ -205,7 +205,7 @@ "action.favorite":"喜爱", "action.removeFavorite":"取消喜爱", "action.hideLibrary":"隐藏资料库", - "action.showLibrary":"显示资料可查", + "action.showLibrary":"显示资料库", "action.cut":"剪切", "action.paste":"粘贴", "action.selectAll":"全选", @@ -404,8 +404,10 @@ "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.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 音频的感知音频质量。", @@ -522,7 +524,7 @@ "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.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 连接超时", @@ -616,7 +618,7 @@ "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.community2.text":"基于 MacOS Monterey 的 Apple Music beta 主题。", "oobe.visual.suggestingThemes.community3":"Dracula", "oobe.visual.suggestingThemes.community3.text":"著名的德古拉吸血鬼主题。", "oobe.amsignin.title": "" 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/providers/local/index.ts b/src/main/providers/local/index.ts index 3993b8d9..da898235 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 } from 'cider_utils'; export class LocalFiles { static localSongs: any = []; @@ -43,18 +42,15 @@ export class LocalFiles { // get files from the Music folder files = files.concat(await LocalFiles.getFiles(folder)) } - - //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); let metadatalist = [] let metadatalistart = [] let numid = 0; for (var audio of audiofiles) { try { - const metadata = await mm.parseFile(audio); + const metadata = await parseFile(audio); let lochash = Md5.hashStr(audio) ?? numid; if (metadata != null) { let form = { @@ -80,17 +76,17 @@ export class LocalFiles { // "kind": "podcast", // "isLibrary": true, // "reporting": false }, - "trackNumber": metadata.common.track?.no ?? 0, - "discNumber": metadata.common.disk?.no ?? 0, - "name": metadata.common.title ?? audio.substring(audio.lastIndexOf('\\') + 1), - "albumName": metadata.common.album, - "artistName": metadata.common.artist, - "copyright": metadata.common.copyright ?? "", + "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?.common?.year ?? '2022'}-05-13T00:23:00Z`, - "durationInMillis": Math.floor((metadata.format.duration ?? 0) * 1000), - "bitrate": Math.floor((metadata.format?.bitrate ?? 0) / 1000), + "releaseDateTime": `${metadata.year ?? '2022'}-05-13T00:23:00Z`, + "durationInMillis": metadata.duration_in_ms ?? 0, + "bitrate": metadata.bitrate ?? 0, "offers": [ { "kind": "get", @@ -99,18 +95,18 @@ export class LocalFiles { ], "contentRating": "clean" }, - flavor: Math.floor((metadata.format?.bitrate ?? 0) / 1000), + flavor: metadata.bitrate, localFilesMetadata: { - lossless: metadata.format?.lossless, - container: metadata.format?.container, - bitDepth: metadata.format?.bitsPerSample ?? 0, - sampleRate: metadata.format?.sampleRate ?? 0, + 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.common.picture != undefined ? metadata.common.picture[0].data.toString('base64') : "", + url: metadata.artwork != undefined ? metadata.artwork : "", } metadatalistart.push(art) numid += 1; @@ -121,7 +117,7 @@ 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)} } this.localSongs = metadatalist; this.localSongsArts = metadatalistart; diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js index 413ed6f3..a975a90d 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: {}, @@ -3038,7 +3039,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; + 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 @@ -3046,67 +3050,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); @@ -3133,7 +3083,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]/); @@ -3176,7 +3126,7 @@ const app = new Vue({ } if (lrcfile != null && lrcfile != '') { // load translation - getMXMTrans(id, lang, token); + getMXMTrans(id, lang); } else { // app.loadAMLyrics() app.loadQQLyrics(); @@ -3187,11 +3137,9 @@ const app = new Vue({ 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() } @@ -3201,23 +3149,22 @@ 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(); + function getMXMTrans(id, lang) { + if (lang != "disabled" && id != '') { // Mode 2 -> Trans + let url2 = "https://api.cider.sh/v1/lyrics?" + "mode=2" + "&richsyncQuery=" + richsyncQuery + "&track=" + track + "&artist=" + artist + "&songID=" + itunesid + "&source=mxm" + "&lang=" + lang + "&time=" + time; let req2 = new XMLHttpRequest(); req2.overrideMimeType("application/json"); - req2.open('GET', url2, true); - req2.setRequestHeader("authority", "apic-desktop.musixmatch.com"); + req2.open('POST', url2, true); req2.onload = function () { try { let jsonResponse2 = JSON.parse(this.responseText); console.log(jsonResponse2); let status2 = jsonResponse2["message"]["header"]["status_code"]; - if (status2 == 200) { + if (status2 === 200) { try { let preTrans = [] let u = app.lyrics; @@ -3249,11 +3196,7 @@ const app = new Vue({ } 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() { diff --git a/src/renderer/views/components/settings-window.ejs b/src/renderer/views/components/settings-window.ejs index 60400b3f..ac9ece72 100644 --- a/src/renderer/views/components/settings-window.ejs +++ b/src/renderer/views/components/settings-window.ejs @@ -686,17 +686,15 @@ {{$root.getLz('settings.header.lyrics')}}