diff --git a/.circleci/config.yml b/.circleci/config.yml index 4da1dde0..27466672 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,7 +8,7 @@ executors: orbs: # Add orbs to your configuration jira: circleci/jira@1.0.5 # invokes the Jira orb, making its commands accessible - + gh: circleci/github-cli@2.1 # The jobs for this project jobs: prepare-build: @@ -111,14 +111,7 @@ jobs: steps: - attach_workspace: at: ~/Cider/ - - run: - name: Installing GitHub Command Line Interface - command: | - sudo apt-get update -y - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null - sudo apt-get update -y - sudo apt install -y gh + - gh/install - run: name: Move Build Files command: | @@ -142,12 +135,13 @@ jobs: echo "Attempting to create release for Cider v${APP_VERSION} on the ${CIRCLE_BRANCH} branch."; if [[ "${APP_VERSION}" = *"beta"* ]]; then echo $'**Beta Release**\nA full changelog is unavailable, but you can view the branch comparison [here](https://github.com/ciderapp/cider/compare/stable...main).\nThese builds are considered bleeding edge, expect bugs and please do not use this as a representation of the fu ll app.\nOur full support disclaimer can be found [here](https://docs.cider.sh/support/disclaimer#support-nightly-beta-releases).' > release-notes.md + gh release create "v${APP_VERSION}" --prerelease --title "Cider Version ${APP_VERSION} (${CIRCLE_BRANCH})" --notes-file release-notes.md -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 + curl http://129.146.42.180/api/v1/github/sync/main else echo $'**Stable Release**\nA full changelog is unavailable, but you can view the branch comparison [here](https://github.com/ciderapp/cider/compare/stable...main).\nThese are the most stable builds we can provide. If you experience any issues, please report them [here](https://github.com/ciderapp/cider/issues/new).\nOur full support disclaimer can be found [here](https://docs.cider.sh/support/disclaimer#support-releases).' > release-notes.md + gh release create "v${APP_VERSION}" --title "Cider Version ${APP_VERSION} (${CIRCLE_BRANCH})" --notes-file release-notes.md -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 + curl http://129.146.42.180/api/v1/github/sync/stable fi; - gh release create "v${APP_VERSION}" --title "Cider Version ${APP_VERSION} (${CIRCLE_BRANCH})" --notes-file release-notes.md -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 workflows: diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 58a3c496..85d64312 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -25,7 +25,7 @@ body: placeholder: Tell us how you did it value: "1. Clicked on..." validations: - required: true + required: true - type: textarea id: environment attributes: @@ -33,12 +33,30 @@ body: description: | Examples: - **OS**: Ubuntu 20.04 - - **App Version** and/or **Commit ID**: 1.0.0 c9d43be + - **Desktop Environment (if applicable)**: i3wm + - **App Version**: 1.5.5-beta.22 + - **Commit ID (if available)**: c9d43be - To find app version, Cider Menu -> About + *To find the app version, Cider Menu -> About* value: | - OS: - - App Version and/or Commit ID: + - Desktop Environment: + - App Version: + - Commit ID: + validations: + required: true + - type: dropdown + id: download + attributes: + label: How did you download the software? + options: + - Microsoft Store + - GitHub + - Winget + - Winget (Nightly) + - Chocolatey + - Flathub + - AUR validations: required: true - type: textarea diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 84d25b91..25a514e8 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + node-version: [ 18 ] steps: - uses: maxim-lobanov/setup-xcode@v1 @@ -34,6 +34,16 @@ jobs: with: fetch-depth: 0 + - uses: pnpm/action-setup@v2.2.2 + with: + version: 7 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + - name: Run Version Script env: GH_REQUEST_TOKEN: ${{ secrets.RELEASE_TOKEN }} @@ -41,6 +51,9 @@ jobs: sudo chmod +x resources/version.sh && ./resources/version.sh || true echo "APP_VERSION=$(node -p -e 'require("./package.json").version')" >>$GITHUB_ENV + - name: Clear node_airtunes2 cache + run: rm -rf ~/Library/pnpm/store/v3/tmp/cf5bc2de2629636ca224995234b8eaa1 || true + - name: Sign in to EVS run: | python3 -m pip install --upgrade castlabs-evs @@ -49,12 +62,9 @@ jobs: - name: Setup Environment run: brew install automake #libtool autoconf - - name: Clear node_airtunes2 cache - run: sudo rm -rf /Users/runner/Library/Caches/Yarn/v6/.tmp/cf5bc2de2629636ca224995234b8eaa1 || true - - name: Install and Configure Node Modules run: | - yarn install + pnpm install cp resources/verror-types node_modules/@types/verror/index.d.ts cp resources/macPackager.js node_modules/app-builder-lib/out/macPackager.js rm -r node_modules/pouchdb-node/node_modules/leveldown || true @@ -70,7 +80,7 @@ jobs: APPLE_ID_PASSWORD: ${{ secrets.APPLEIDPASS }} PSC_NAME: ${{ secrets.PSC_NAME }} DEVELOPER_DIR: /Applications/Xcode_12.4.app/Contents/Developer - run: yarn dist:universalNotWorking -p never + run: pnpm dist:universalNotWorking -p never - name: Add license to DMG run: npx dmg-license resources/license.json dist/*.dmg @@ -125,3 +135,16 @@ jobs: repo_token: ${{ secrets.RELEASE_TOKEN }} file: dist/Cider-${{ env.APP_VERSION }}-universal.pkg tag: v${{ env.APP_VERSION }} + - name: Trigger Bot Sync (main) + uses: indiesdev/curl@v1.1 + with: + url: http://129.146.42.180/api/v1/github/sync/main + log-response: true + timeout: 60000 + - name: Trigger Bot Sync (stable) + uses: indiesdev/curl@v1.1 + with: + url: http://129.146.42.180/api/v1/github/sync/stable + log-response: true + timeout: 60000 + diff --git a/.github/workflows/cider-chore.yml b/.github/workflows/cider-chore.yml deleted file mode 100644 index 2b26ea4d..00000000 --- a/.github/workflows/cider-chore.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Cider Chores - -on: - push: - branches: - - main - -jobs: - update-lockfile: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [ 18 ] - - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - # Make sure the actual branch is checked out when running on pull requests - ref: ${{ github.head_ref }} - - - uses: pnpm/action-setup@v2.2.2 - with: - version: 7 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' - - - name: Update lockfile - run: pnpm i --lockfile-only - - - name: Commit Updated Lockfile - uses: stefanzweifel/git-auto-commit-action@v4.14.1 - with: - commit_message: "chore: Updated Lockfile" - commit_user_name: "cider-chore[bot]" - commit_user_email: "cider-chore[bot]@users.noreply.github.com" - - prettier: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [ 18 ] - - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - # Make sure the actual branch is checked out when running on pull requests - ref: ${{ github.head_ref }} - - - name: Prettify code - run: npm run prettier - - - name: Commit Prettier Code - uses: stefanzweifel/git-auto-commit-action@v4.14.1 - with: - commit_message: "chore: Prettified Code\n [ci skip]" - commit_user_name: "cider-chore[bot]" - commit_user_email: "cider-chore[bot]@users.noreply.github.com" diff --git a/.github/workflows/cider-chores.yml b/.github/workflows/cider-chores.yml new file mode 100644 index 00000000..48136a22 --- /dev/null +++ b/.github/workflows/cider-chores.yml @@ -0,0 +1,118 @@ +name: Cider Chores + +on: + push: + branches: + - main + +jobs: + update-lockfile: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18] + + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + # Make sure the actual branch is checked out when running on pull requests + ref: ${{ github.head_ref }} + + - uses: pnpm/action-setup@v2.2.2 + with: + version: 7 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + cache: "pnpm" + + - name: Update lockfile + run: pnpm i --lockfile-only + + - name: Commit Updated Lockfile + uses: stefanzweifel/git-auto-commit-action@v4.14.1 + with: + commit_message: "chore: Updated Lockfile" + commit_user_name: "cider-chore[bot]" + commit_user_email: "cider-chore[bot]@users.noreply.github.com" + + prettier: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18] + + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + # Make sure the actual branch is checked out when running on pull requests + ref: ${{ github.head_ref }} + + - name: Prettify code + run: npm run format:write + + - name: Commit Prettier Code + uses: stefanzweifel/git-auto-commit-action@v4.14.1 + with: + commit_message: "chore: Prettified Code\n [ci skip]" + commit_user_name: "cider-chore[bot]" + commit_user_email: "cider-chore[bot]@users.noreply.github.com" + + update-i18n-source: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18] + + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Update Source Files + run: cp src/i18n/en_US.json src/i18n/source/en_US.json + + - name: Commit Updated Source File + uses: stefanzweifel/git-auto-commit-action@v4.14.1 + with: + commit_message: "chore: Updated i18n Source\n [ci skip]" + commit_user_name: "cider-chore[bot]" + commit_user_email: "cider-chore[bot]@users.noreply.github.com" + + + synchronize-with-crowdin: + runs-on: ubuntu-latest + if: ${{ false }} # disable for now + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: crowdin action + uses: crowdin/github-action@1.4.13 + with: + upload_translations: true + download_translations: false + project_id: ${{ secrets.CROWDIN_PROJECT_ID }} + token: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + source: '/src/i18n/source/**.*' + translation: '/src/i18n/%locale_with_underscore%.json' + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} diff --git a/.github/workflows/pr-chores.yml b/.github/workflows/pr-chores.yml new file mode 100644 index 00000000..bd622916 --- /dev/null +++ b/.github/workflows/pr-chores.yml @@ -0,0 +1,37 @@ +name: PR Chores + +on: [pull_request] + +jobs: + linter-check: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest] + node: [14] + + steps: + - name: Checkout 🛎 + uses: actions/checkout@v2 + + - name: Setup pnpm 🚧F + uses: pnpm/action-setup@v2.2.2 + with: + version: 7 + + - name: Setup node env 🏗 + uses: actions/setup-node@v3.4.1 + with: + node-version: ${{ matrix.node }} + check-latest: true + cache: 'pnpm' + + - name: Install dependencies 👨🏻💻 + run: pnpm install + + - name: Run linter 👀 + uses: wearerequired/lint-action@v2 + with: + prettier: true + prettier_args: "'**/*.{js,json,ts,css,vue,less}'" diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index 3307a8b2..7c3309c3 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -19,4 +19,4 @@ jobs: stale-issue-label: "stale" stale-pr-label: "stale" exempt-all-assignees: true - exempt-issue-labels: 'more-info,work-in-progress,accessibility-feature,help-wanted' + exempt-issue-labels: 'more-info,work-in-progress,accessibility-feature,help-wanted,persist' diff --git a/.npmrc b/.npmrc index 2162f3c3..231886ac 100644 --- a/.npmrc +++ b/.npmrc @@ -3,4 +3,3 @@ public-hoist-pattern=* shamefully-hoist=true auto-install-peers=true strict-peer-dependencies=false -store-dir=.pnpm-store diff --git a/.prettierignore b/.prettierignore index 7645927e..ee6ef80d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ -src/renderer/apple-hls* +src/renderer/*hls*.js build/* src/renderer/lib/* *.min.* +.pnpm-store diff --git a/.prettierrc b/.prettierrc index 03328464..b93c35c2 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,2 +1,13 @@ -bracketSameLine: true -printWidth: 240 +{ + "printWidth": 600, + "singleAttributePerLine": true, + "bracketSameLine": true, + "overrides": [ + { + "files": "src/renderer/main/**/*.js", + "options": { + "singleAttributePerLine": false + } + } + ] +} diff --git a/README.md b/README.md index a5c70150..c4cb84c6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ - + diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..3ce3bcbd --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,3 @@ +files: + - source: /src/i18n/source/en_US.json + translation: /src/i18n/%locale_with_underscore%.json diff --git a/package.json b/package.json index 445cecfe..208b2d5c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "cider", "applicationId": "Cider", "productName": "Cider", - "version": "1.5.6", + "version": "1.5.7", "description": "A new cross-platform Apple Music experience based on Electron and Vue.js written from scratch with performance in mind.", "license": "AGPL-3.0", "main": "./build/index.js", @@ -27,6 +27,7 @@ "pack": "electron-builder --dir", "dist": "npm run build && electron-builder", "dist:macarm": "npm run build && electron-builder --mac --arm64", + "dist:linuxdir": "npm run build && electron-builder --linux dir", "slim-build:macarm": "ditto --arch arm64 ./dist/mac-universal/Cider.app ./dist/mac-universal/arm64/Cider.app", "slim-build:macintel": "ditto --arch x86_64 ./dist/mac-universal/Cider.app ./dist/mac-universal/x86_x64/Cider.app", "dist:universalNotWorking": "npm run build && electron-builder --mac --universal", @@ -36,11 +37,12 @@ "msft": "npm run build && electron-builder -c msft-package.json", "mstest": "npm run build && electron-builder -c msft-test.json", "postinstall": "electron-builder install-app-deps", - "prettier": "npx prettier --write '**/*.{js,json,ts,css,less}'" + "format:check": "npx prettier --check \"src/**/*.{js,json,ts,less}\"", + "format:write": "npx prettier --write \"src/**/*.{js,json,ts,less}\"" }, "dependencies": { - "@sentry/electron": "^4.0.0", - "@sentry/integrations": "^7.8.1", + "@sentry/electron": "^4.0.2", + "@sentry/integrations": "^7.13.0", "adm-zip": "0.4.10", "airtunes2": "git+https://github.com/ciderapp/node_airtunes2.git", "castv2-client": "^1.2.0", @@ -56,7 +58,7 @@ "electron-window-state": "^5.0.3", "express": "^4.18.1", "get-port": "5.1.1", - "jimp": "^0.16.1", + "jimp": "^0.16.2", "lastfmapi": "^0.1.1", "mdns-js": "git+https://github.com/ciderapp/node-mdns-js.git", "mpris-service": "^2.1.2", @@ -68,6 +70,7 @@ "run-script-os": "^1.1.6", "source-map-support": "^0.5.21", "ts-md5": "1.2.11", + "upnp-mediarenderer-client": "git+https://github.com/vapormusic/node-upnp-mediarenderer-client.git", "v8-compile-cache": "^2.3.0", "wallpaper": "5.0.1", "ws": "^8.8.1", @@ -77,17 +80,18 @@ "devDependencies": { "@types/adm-zip": "^0.5.0", "@types/discord-rpc": "4.0.3", - "@types/express": "^4.17.13", + "@types/express": "^4.17.14", + "@types/node": "^18.7.18", "@types/qrcode-terminal": "^0.12.0", "@types/ws": "^8.5.3", - "@types/node": "^18.7.13", "electron": "git+https://github.com/castlabs/electron-releases.git", - "electron-builder": "^23.0.2", + "electron-builder": "^23.3.3", "electron-builder-notarize-pkg": "^1.2.0", "electron-webpack": "^2.8.2", "less": "^4.1.3", "musickit-typescript": "^1.2.4", - "typescript": "^4.7.4", + "prettier": "2.7.1", + "typescript": "^4.8.3", "vue-devtools": "^5.1.4", "webpack": "~5.74.0" }, @@ -114,9 +118,9 @@ } ], "build": { - "electronVersion": "20.1.0", + "electronVersion": "20.1.3", "electronDownload": { - "version": "20.1.0+wvcus", + "version": "20.1.3+wvcus", "mirror": "https://github.com/castlabs/electron-releases/releases/download/v" }, "appId": "cider", @@ -224,5 +228,8 @@ "NSUserNotificationAlertStyle": "alert" } } + }, + "electronWebpack": { + "devtool": "source-map" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7967d21..ee9ba462 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,12 +1,12 @@ lockfileVersion: 5.4 specifiers: - '@sentry/electron': ^4.0.0 - '@sentry/integrations': ^7.8.1 + '@sentry/electron': ^4.0.2 + '@sentry/integrations': ^7.13.0 '@types/adm-zip': ^0.5.0 '@types/discord-rpc': 4.0.3 - '@types/express': ^4.17.13 - '@types/node': ^18.7.13 + '@types/express': ^4.17.14 + '@types/node': ^18.7.18 '@types/qrcode-terminal': ^0.12.0 '@types/ws': ^8.5.3 adm-zip: 0.4.10 @@ -17,7 +17,7 @@ specifiers: dns-js: git+https://github.com/ciderapp/node-dns-js.git ejs: ^3.1.8 electron: git+https://github.com/castlabs/electron-releases.git - electron-builder: ^23.0.2 + electron-builder: ^23.3.3 electron-builder-notarize-pkg: ^1.2.0 electron-fetch: ^1.7.4 electron-log: ^4.4.8 @@ -28,7 +28,7 @@ specifiers: electron-window-state: ^5.0.3 express: ^4.18.1 get-port: 5.1.1 - jimp: ^0.16.1 + jimp: ^0.16.2 lastfmapi: ^0.1.1 less: ^4.1.3 mdns-js: git+https://github.com/ciderapp/node-mdns-js.git @@ -37,12 +37,14 @@ specifiers: musickit-typescript: ^1.2.4 node-gyp: ^9.1.0 node-ssdp: ^4.0.1 + prettier: 2.7.1 qrcode: ^1.5.1 request: ^2.88.2 run-script-os: ^1.1.6 source-map-support: ^0.5.21 ts-md5: 1.2.11 - typescript: ^4.7.4 + typescript: ^4.8.3 + upnp-mediarenderer-client: git+https://github.com/vapormusic/node-upnp-mediarenderer-client.git v8-compile-cache: ^2.3.0 vue-devtools: ^5.1.4 wallpaper: 5.0.1 @@ -52,10 +54,10 @@ specifiers: youtube-search-without-api-key: ^1.0.7 dependencies: - '@sentry/electron': 4.0.0 - '@sentry/integrations': 7.11.1 + '@sentry/electron': 4.0.2 + '@sentry/integrations': 7.13.0 adm-zip: 0.4.10 - airtunes2: github.com/ciderapp/node_airtunes2/63e3a34a551aa700f89b622d65d19f52f48548ce + airtunes2: github.com/ciderapp/node_airtunes2/92dc56e0b86e02aa40b569880d58bc2fc0eee2c7 castv2-client: 1.2.0 chokidar: 3.5.3 discord-auto-rpc: 1.0.17 @@ -69,7 +71,7 @@ dependencies: electron-window-state: 5.0.3 express: 4.18.1 get-port: 5.1.1 - jimp: 0.16.1 + jimp: 0.16.2 lastfmapi: 0.1.1 mdns-js: github.com/ciderapp/node-mdns-js/d8f658b623233c3cf58be229332f3342f553cde2 mpris-service: 2.1.2 @@ -81,6 +83,7 @@ dependencies: run-script-os: 1.1.6 source-map-support: 0.5.21 ts-md5: 1.2.11 + upnp-mediarenderer-client: github.com/vapormusic/node-upnp-mediarenderer-client/d4856931500026eb6c4c67b3eb12c1c34755627e v8-compile-cache: 2.3.0 wallpaper: 5.0.1 ws: 8.8.1 @@ -90,17 +93,18 @@ dependencies: devDependencies: '@types/adm-zip': 0.5.0 '@types/discord-rpc': 4.0.3 - '@types/express': 4.17.13 - '@types/node': 18.7.13 + '@types/express': 4.17.14 + '@types/node': 18.7.18 '@types/qrcode-terminal': 0.12.0 '@types/ws': 8.5.3 - electron: github.com/castlabs/electron-releases/a1749958b51da6ce1596992260cbacb602c23762 + electron: github.com/castlabs/electron-releases/5878335d0e4d27df7ac19771ab73725ede3849b6 electron-builder: 23.3.3 electron-builder-notarize-pkg: 1.2.0 electron-webpack: 2.8.2_webpack@5.74.0 less: 4.1.3 musickit-typescript: 1.2.4 - typescript: 4.7.4 + prettier: 2.7.1 + typescript: 4.8.3 vue-devtools: 5.1.4 webpack: 5.74.0 @@ -1321,22 +1325,22 @@ packages: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} dev: false - /@jimp/bmp/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-iwyNYQeBawrdg/f24x3pQ5rEx+/GwjZcCXd3Kgc+ZUd+Ivia7sIqBsOnDaMZdKCBPlfW364ekexnlOqyVa0NWg==} + /@jimp/bmp/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-4g9vW45QfMoGhLVvaFj26h4e7cC+McHUQwyFQmNTLW4FfC1OonN9oUr2m/FEDGkTYKR7aqdXR5XUqqIkHWLaFw==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 bmp-js: 0.1.0 dev: false - /@jimp/core/0.16.1: - resolution: {integrity: sha512-la7kQia31V6kQ4q1kI/uLimu8FXx7imWVajDGtwUG8fzePLWDFJyZl0fdIXVCL1JW2nBcRHidUot6jvlRDi2+g==} + /@jimp/core/0.16.2: + resolution: {integrity: sha512-dp7HcyUMzjXphXYodI6PaXue+I9PXAavbb+AN+1XqFbotN22Z12DosNPEyy+UhLY/hZiQQqUkEaJHkvV31rs+w==} dependencies: '@babel/runtime': 7.18.9 - '@jimp/utils': 0.16.1 + '@jimp/utils': 0.16.2 any-base: 1.1.0 buffer: 5.7.1 exif-parser: 0.1.12 @@ -1348,79 +1352,79 @@ packages: tinycolor2: 1.4.2 dev: false - /@jimp/custom/0.16.1: - resolution: {integrity: sha512-DNUAHNSiUI/j9hmbatD6WN/EBIyeq4AO0frl5ETtt51VN1SvE4t4v83ZA/V6ikxEf3hxLju4tQ5Pc3zmZkN/3A==} + /@jimp/custom/0.16.2: + resolution: {integrity: sha512-GtNwOs4hcVS2GIbqRUf42rUuX07oLB92cj7cqxZb0ZGWwcwhnmSW0TFLAkNafXmqn9ug4VTpNvcJSUdiuECVKg==} dependencies: '@babel/runtime': 7.18.9 - '@jimp/core': 0.16.1 + '@jimp/core': 0.16.2 dev: false - /@jimp/gif/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-r/1+GzIW1D5zrP4tNrfW+3y4vqD935WBXSc8X/wm23QTY9aJO9Lw6PEdzpYCEY+SOklIFKaJYUAq/Nvgm/9ryw==} + /@jimp/gif/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-TMdyT9Q0paIKNtT7c5KzQD29CNCsI/t8ka28jMrBjEK7j5RRTvBfuoOnHv7pDJRCjCIqeUoaUSJ7QcciKic6CA==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 gifwrap: 0.9.4 omggif: 1.0.10 dev: false - /@jimp/jpeg/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-8352zrdlCCLFdZ/J+JjBslDvml+fS3Z8gttdml0We759PnnZGqrnPRhkOEOJbNUlE+dD4ckLeIe6NPxlS/7U+w==} + /@jimp/jpeg/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-BW5gZydgq6wdIwHd+3iUNgrTklvoQc/FUKSj9meM6A0FU21lUaansRX5BDdJqHkyXJLnnlDGwDt27J+hQuBAVw==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 jpeg-js: 0.4.2 dev: false - /@jimp/plugin-blit/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-fKFNARm32RoLSokJ8WZXHHH2CGzz6ire2n1Jh6u+XQLhk9TweT1DcLHIXwQMh8oR12KgjbgsMGvrMVlVknmOAg==} + /@jimp/plugin-blit/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-Z31rRfV80gC/r+B/bOPSVVpJEWXUV248j7MdnMOFLu4vr8DMqXVo9jYqvwU/s4LSTMAMXqm4Jg6E/jQfadPKAg==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-blur/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-1WhuLGGj9MypFKRcPvmW45ht7nXkOKu+lg3n2VBzIB7r4kKNVchuI59bXaCYQumOLEqVK7JdB4glaDAbCQCLyw==} + /@jimp/plugin-blur/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-ShkJCAzRI+1fAKPuLLgEkixpSpVmKTYaKEFROUcgmrv9AansDXGNCupchqVMTdxf8zPyW8rR1ilvG3OJobufLQ==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-circle/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-JK7yi1CIU7/XL8hdahjcbGA3V7c+F+Iw+mhMQhLEi7Q0tCnZ69YJBTamMiNg3fWPVfMuvWJJKOBRVpwNTuaZRg==} + /@jimp/plugin-circle/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-6T4z/48F4Z5+YwAVCLOvXQcyGmo0E3WztxCz6XGQf66r4JJK78+zcCDYZFLMx0BGM0091FogNK4QniP8JaOkrA==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-color/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-9yQttBAO5SEFj7S6nJK54f+1BnuBG4c28q+iyzm1JjtnehjqMg6Ljw4gCSDCvoCQ3jBSYHN66pmwTV74SU1B7A==} + /@jimp/plugin-color/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-6oBV0g0J17/7E+aTquvUsgSc85nUbUi+64tIK5eFIDzvjhlqhjGNJYlc46KJMCWIs61qRJayQoZdL/iT/iQuGQ==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 tinycolor2: 1.4.2 dev: false - /@jimp/plugin-contain/0.16.1_hdurtz6n4amigqqh6fpopmgoni: - resolution: {integrity: sha512-44F3dUIjBDHN+Ym/vEfg+jtjMjAqd2uw9nssN67/n4FdpuZUVs7E7wadKY1RRNuJO+WgcD5aDQcsvurXMETQTg==} + /@jimp/plugin-contain/0.16.2_imjb3y324usacerau4nmcprwgm: + resolution: {integrity: sha512-pLcxO3hVN3LCEhMNvpZ9B7xILHVlS433Vv16zFFJxLRqZdYvPLsc+ZzJhjAiHHuEjVblQrktHE3LGeQwGJPo0w==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-blit': '>=0.3.5' @@ -1428,15 +1432,15 @@ packages: '@jimp/plugin-scale': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-blit': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-scale': 0.16.1_yobdog5u6yhkf7jbdico3cvr4i - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-blit': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-scale': 0.16.2_ml7r7ku4xtw2ibnxqj5uaaiwdu + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-cover/0.16.1_huj753jw6x6ymgmn4r5xrmstcq: - resolution: {integrity: sha512-YztWCIldBAVo0zxcQXR+a/uk3/TtYnpKU2CanOPJ7baIuDlWPsG+YE4xTsswZZc12H9Kl7CiziEbDtvF9kwA/Q==} + /@jimp/plugin-cover/0.16.2_gpdsppfdtbiku5k3fcmgpdqet4: + resolution: {integrity: sha512-gzWM7VvYeI8msyiwbUZxH+sGQEgO6Vd6adGxZ0CeKX00uQOe5lDzxb1Wjx7sHcJGz8a/5fmAuwz7rdDtpDUbkw==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-crop': '>=0.3.5' @@ -1444,130 +1448,130 @@ packages: '@jimp/plugin-scale': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-crop': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-scale': 0.16.1_yobdog5u6yhkf7jbdico3cvr4i - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-crop': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-scale': 0.16.2_ml7r7ku4xtw2ibnxqj5uaaiwdu + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-crop/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-UQdva9oQzCVadkyo3T5Tv2CUZbf0klm2cD4cWMlASuTOYgaGaFHhT9st+kmfvXjKL8q3STkBu/zUPV6PbuV3ew==} + /@jimp/plugin-crop/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-qCd3hfMEE+Z2EuuyXewgXRTtKJGIerWzc1zLEJztsUkPz5i73IGgkOL+mrNutZwGaXZbm+8SwUaGb46sxAO6Tw==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-displace/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-iVAWuz2+G6Heu8gVZksUz+4hQYpR4R0R/RtBzpWEl8ItBe7O6QjORAkhxzg+WdYLL2A/Yd4ekTpvK0/qW8hTVw==} + /@jimp/plugin-displace/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-6nXdvNNjCdD95v2o3/jPeur903dz08lG4Y8gmr5oL2yVv9LSSbMonoXYrR/ASesdyXqGdXJLU4NL+yZs4zUqbQ==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-dither/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-tADKVd+HDC9EhJRUDwMvzBXPz4GLoU6s5P7xkVq46tskExYSptgj5713J5Thj3NMgH9Rsqu22jNg1H/7tr3V9Q==} + /@jimp/plugin-dither/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-DERpIzy21ZanMkVsD0Tdy8HQLbD1E41OuvIzaMRoW4183PA6AgGNlrQoFTyXmzjy6FTy1SxaQgTEdouInAWZ9Q==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-fisheye/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-BWHnc5hVobviTyIRHhIy9VxI1ACf4CeSuCfURB6JZm87YuyvgQh5aX5UDKtOz/3haMHXBLP61ZBxlNpMD8CG4A==} + /@jimp/plugin-fisheye/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-Df7PsGIwiIpQu3EygYCnaJyTfOwvwtYV3cmYJS7yFLtdiFUuod+hlSo5GkwEPLAy+QBxhUbDuUqnsWo4NQtbiQ==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-flip/0.16.1_ebpr3hwfcslp7e7vflcwddbwma: - resolution: {integrity: sha512-KdxTf0zErfZ8DyHkImDTnQBuHby+a5YFdoKI/G3GpBl3qxLBvC+PWkS2F/iN3H7wszP7/TKxTEvWL927pypT0w==} + /@jimp/plugin-flip/0.16.2_ktwwnsoypijoghlnmlrfdl3qqm: + resolution: {integrity: sha512-+2uC8ioVQUr06mnjSWraskz2L33nJHze35LkQ8ZNsIpoZLkgvfiWatqAs5bj+1jGI/9kxoCFAaT1Is0f+a4/rw==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-rotate': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-rotate': 0.16.1_g72lzg55x5a4ao4td3uebcztme - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-rotate': 0.16.2_rqxyxjeek4vqmsj2o2ueqoj3ai + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-gaussian/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-u9n4wjskh3N1mSqketbL6tVcLU2S5TEaFPR40K6TDv4phPLZALi1Of7reUmYpVm8mBDHt1I6kGhuCJiWvzfGyg==} + /@jimp/plugin-gaussian/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-2mnuDSg4ZEH8zcJig7DZZf4st/cYmQ5UYJKP76iGhZ+6JDACk6uejwAgT5xHecNhkVAaXMdCybA2eknH/9OE1w==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-invert/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-2DKuyVXANH8WDpW9NG+PYFbehzJfweZszFYyxcaewaPLN0GxvxVLOGOPP1NuUTcHkOdMFbE0nHDuB7f+sYF/2w==} + /@jimp/plugin-invert/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-xFvHbVepTY/nus+6yXiYN1iq+UBRkT0MdnObbiQPstUrAsz0Imn6MWISsnAyMvcNxHGrxaxjuU777JT/esM0gg==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-mask/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-snfiqHlVuj4bSFS0v96vo2PpqCDMe4JB+O++sMo5jF5mvGcGL6AIeLo8cYqPNpdO6BZpBJ8MY5El0Veckhr39Q==} + /@jimp/plugin-mask/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-AbdO85xxhfgEDdxYKpUotEI9ixiCMaIpfYHD5a5O/VWeimz2kuwhcrzlHGiyq1kKAgRcl0WEneTCZAHVSyvPKA==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-normalize/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-dOQfIOvGLKDKXPU8xXWzaUeB0nvkosHw6Xg1WhS1Z5Q0PazByhaxOQkSKgUryNN/H+X7UdbDvlyh/yHf3ITRaw==} + /@jimp/plugin-normalize/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-+ItBWFwmB0Od7OfOtTYT1gm543PpHUgU8/DN55z83l1JqS0OomDJAe7BmCppo2405TN6YtVm/csXo7p4iWd/SQ==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-print/0.16.1_kgi3mefd5odb2o5kccttl2e6zq: - resolution: {integrity: sha512-ceWgYN40jbN4cWRxixym+csyVymvrryuKBQ+zoIvN5iE6OyS+2d7Mn4zlNgumSczb9GGyZZESIgVcBDA1ezq0Q==} + /@jimp/plugin-print/0.16.2_jjb7dzm7g3gdkyninttbcskgge: + resolution: {integrity: sha512-ifTGEeJ5UZTCiqC70HMeU3iXk/vsOmhWiwVGOXSFXhFeE8ZpDWvlmBsrMYnRrJGuaaogHOIrrQPI+kCdDBSBIQ==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-blit': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-blit': 0.16.1_@jimp+custom@0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-blit': 0.16.2_@jimp+custom@0.16.2 + '@jimp/utils': 0.16.2 load-bmfont: 1.4.1 dev: false - /@jimp/plugin-resize/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-u4JBLdRI7dargC04p2Ha24kofQBk3vhaf0q8FwSYgnCRwxfvh2RxvhJZk9H7Q91JZp6wgjz/SjvEAYjGCEgAwQ==} + /@jimp/plugin-resize/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-gE4N9l6xuwzacFZ2EPCGZCJ/xR+aX2V7GdMndIl/6kYIw5/eib1SFuF9AZLvIPSFuE1FnGo8+vT0pr++SSbhYg==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-rotate/0.16.1_g72lzg55x5a4ao4td3uebcztme: - resolution: {integrity: sha512-ZUU415gDQ0VjYutmVgAYYxC9Og9ixu2jAGMCU54mSMfuIlmohYfwARQmI7h4QB84M76c9hVLdONWjuo+rip/zg==} + /@jimp/plugin-rotate/0.16.2_rqxyxjeek4vqmsj2o2ueqoj3ai: + resolution: {integrity: sha512-/CTEYkR1HrgmnE0VqPhhbBARbDAfFX590LWGIpxcYIYsUUGQCadl+8Qo4UX13FH0Nt8UHEtPA+O2x08uPYg9UA==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-blit': '>=0.3.5' @@ -1575,122 +1579,122 @@ packages: '@jimp/plugin-resize': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-blit': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-crop': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-blit': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-crop': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-scale/0.16.1_yobdog5u6yhkf7jbdico3cvr4i: - resolution: {integrity: sha512-jM2QlgThIDIc4rcyughD5O7sOYezxdafg/2Xtd1csfK3z6fba3asxDwthqPZAgitrLgiKBDp6XfzC07Y/CefUw==} + /@jimp/plugin-scale/0.16.2_ml7r7ku4xtw2ibnxqj5uaaiwdu: + resolution: {integrity: sha512-3inuxfrlquyLaqFdiiiQNJUurR0WbvN5wAf1qcYX2LubG1AG8grayYD6H7XVoxfUGTZXh1kpmeirEYlqA2zxcw==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-resize': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-shadow/0.16.1_qjkavnenl6bwqbeu6ngq3leqp4: - resolution: {integrity: sha512-MeD2Is17oKzXLnsphAa1sDstTu6nxscugxAEk3ji0GV1FohCvpHBcec0nAq6/czg4WzqfDts+fcPfC79qWmqrA==} + /@jimp/plugin-shadow/0.16.2_5zizl4daumbyziiswanqnnjtui: + resolution: {integrity: sha512-Q0aIs2/L6fWMcEh9Ms73u34bT1hyUMw/oxaVoIzOLo6/E8YzCs2Bi63H0/qaPS0MQpEppI++kvosPbblABY79w==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-blur': '>=0.3.5' '@jimp/plugin-resize': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-blur': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-blur': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugin-threshold/0.16.1_5qgih43agfe2q6apq4cf7aqzei: - resolution: {integrity: sha512-iGW8U/wiCSR0+6syrPioVGoSzQFt4Z91SsCRbgNKTAk7D+XQv6OI78jvvYg4o0c2FOlwGhqz147HZV5utoSLxA==} + /@jimp/plugin-threshold/0.16.2_qzopulswoxcbleqavzrwgrm3ue: + resolution: {integrity: sha512-gyOwmBgjtMPvcuyOhkP6dOGWbQdaTfhcBRN22mYeI/k/Wh/Zh1OI21F6eKLApsVRmg15MoFnkrCz64RROC34sw==} peerDependencies: '@jimp/custom': '>=0.3.5' '@jimp/plugin-color': '>=0.8.0' '@jimp/plugin-resize': '>=0.8.0' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-color': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugin-color': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/utils': 0.16.2 dev: false - /@jimp/plugins/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-c+lCqa25b+4q6mJZSetlxhMoYuiltyS+ValLzdwK/47+aYsq+kcJNl+TuxIEKf59yr9+5rkbpsPkZHLF/V7FFA==} + /@jimp/plugins/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-zCvYtCgctmC0tkYEu+y+kSwSIZBsNznqJ3/3vkpzxdyjd6wCfNY5Qc/68MPrLc1lmdeGo4cOOTYHG7Vc6myzRw==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugin-blit': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-blur': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-circle': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-color': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-contain': 0.16.1_hdurtz6n4amigqqh6fpopmgoni - '@jimp/plugin-cover': 0.16.1_huj753jw6x6ymgmn4r5xrmstcq - '@jimp/plugin-crop': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-displace': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-dither': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-fisheye': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-flip': 0.16.1_ebpr3hwfcslp7e7vflcwddbwma - '@jimp/plugin-gaussian': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-invert': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-mask': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-normalize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-print': 0.16.1_kgi3mefd5odb2o5kccttl2e6zq - '@jimp/plugin-resize': 0.16.1_@jimp+custom@0.16.1 - '@jimp/plugin-rotate': 0.16.1_g72lzg55x5a4ao4td3uebcztme - '@jimp/plugin-scale': 0.16.1_yobdog5u6yhkf7jbdico3cvr4i - '@jimp/plugin-shadow': 0.16.1_qjkavnenl6bwqbeu6ngq3leqp4 - '@jimp/plugin-threshold': 0.16.1_5qgih43agfe2q6apq4cf7aqzei + '@jimp/custom': 0.16.2 + '@jimp/plugin-blit': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-blur': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-circle': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-color': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-contain': 0.16.2_imjb3y324usacerau4nmcprwgm + '@jimp/plugin-cover': 0.16.2_gpdsppfdtbiku5k3fcmgpdqet4 + '@jimp/plugin-crop': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-displace': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-dither': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-fisheye': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-flip': 0.16.2_ktwwnsoypijoghlnmlrfdl3qqm + '@jimp/plugin-gaussian': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-invert': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-mask': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-normalize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-print': 0.16.2_jjb7dzm7g3gdkyninttbcskgge + '@jimp/plugin-resize': 0.16.2_@jimp+custom@0.16.2 + '@jimp/plugin-rotate': 0.16.2_rqxyxjeek4vqmsj2o2ueqoj3ai + '@jimp/plugin-scale': 0.16.2_ml7r7ku4xtw2ibnxqj5uaaiwdu + '@jimp/plugin-shadow': 0.16.2_5zizl4daumbyziiswanqnnjtui + '@jimp/plugin-threshold': 0.16.2_qzopulswoxcbleqavzrwgrm3ue timm: 1.7.1 dev: false - /@jimp/png/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-iyWoCxEBTW0OUWWn6SveD4LePW89kO7ZOy5sCfYeDM/oTPLpR8iMIGvZpZUz1b8kvzFr27vPst4E5rJhGjwsdw==} + /@jimp/png/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-sFOtOSz/tzDwXEChFQ/Nxe+0+vG3Tj0eUxnZVDUG/StXE9dI8Bqmwj3MIa0EgK5s+QG3YlnDOmlPUa4JqmeYeQ==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/utils': 0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/utils': 0.16.2 pngjs: 3.4.0 dev: false - /@jimp/tiff/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-3K3+xpJS79RmSkAvFMgqY5dhSB+/sxhwTFA9f4AVHUK0oKW+u6r52Z1L0tMXHnpbAdR9EJ+xaAl2D4x19XShkQ==} + /@jimp/tiff/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-ADcdqmtZF+U2YoaaHTzFX8D6NFpmN4WZUT0BPMerEuY7Cq8QoLYU22z2h034FrVW+Rbi1b3y04sB9iDiQAlf2w==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 + '@jimp/custom': 0.16.2 utif: 2.0.1 dev: false - /@jimp/types/0.16.1_@jimp+custom@0.16.1: - resolution: {integrity: sha512-g1w/+NfWqiVW4CaXSJyD28JQqZtm2eyKMWPhBBDCJN9nLCN12/Az0WFF3JUAktzdsEC2KRN2AqB1a2oMZBNgSQ==} + /@jimp/types/0.16.2_@jimp+custom@0.16.2: + resolution: {integrity: sha512-0Ue5Sq0XnDF6TirisWv5E+8uOnRcd8vRLuwocJOhF76NIlcQrz+5r2k2XWKcr3d+11n28dHLXW5TKSqrUopxhA==} peerDependencies: '@jimp/custom': '>=0.3.5' dependencies: '@babel/runtime': 7.18.9 - '@jimp/bmp': 0.16.1_@jimp+custom@0.16.1 - '@jimp/custom': 0.16.1 - '@jimp/gif': 0.16.1_@jimp+custom@0.16.1 - '@jimp/jpeg': 0.16.1_@jimp+custom@0.16.1 - '@jimp/png': 0.16.1_@jimp+custom@0.16.1 - '@jimp/tiff': 0.16.1_@jimp+custom@0.16.1 + '@jimp/bmp': 0.16.2_@jimp+custom@0.16.2 + '@jimp/custom': 0.16.2 + '@jimp/gif': 0.16.2_@jimp+custom@0.16.2 + '@jimp/jpeg': 0.16.2_@jimp+custom@0.16.2 + '@jimp/png': 0.16.2_@jimp+custom@0.16.2 + '@jimp/tiff': 0.16.2_@jimp+custom@0.16.2 timm: 1.7.1 dev: false - /@jimp/utils/0.16.1: - resolution: {integrity: sha512-8fULQjB0x4LzUSiSYG6ZtQl355sZjxbv8r9PPAuYHzS9sGiSHJQavNqK/nKnpDsVkU88/vRGcE7t3nMU0dEnVw==} + /@jimp/utils/0.16.2: + resolution: {integrity: sha512-XENrPvmigiXZQ8E2nxJqO6UVvWBLzbNwyYi3Y8Q1IECoYhYI3kgOQ0fmy4G269Vz1V0omh1bNmC42r4OfXg1Jg==} dependencies: '@babel/runtime': 7.18.9 regenerator-runtime: 0.13.9 @@ -1828,68 +1832,68 @@ packages: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} dev: false - /@sentry/browser/7.8.1: - resolution: {integrity: sha512-9JuagYqHyaZu/4RqyxrAgEHo71oV592XBuUKC33gajCVKWbyG3mNqudSMoHtdM1DrV9REZ4Elha7zFaE2cJX6g==} + /@sentry/browser/7.12.1: + resolution: {integrity: sha512-pgyL65CrGFLe8sKcEG8KXAuVTE8zkAsyTlv/AuME06cSdxzO/memPK/r3BI6EM7WupIdga+V5tQUldeT1kgHNA==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.8.1 - '@sentry/types': 7.8.1 - '@sentry/utils': 7.8.1 + '@sentry/core': 7.12.1 + '@sentry/types': 7.12.1 + '@sentry/utils': 7.12.1 tslib: 1.14.1 dev: false - /@sentry/core/7.8.1: - resolution: {integrity: sha512-PRivbdIzApi/gSixAxozhOBTylSVdw/9VxaStYHd7JJGhs36KXkV8ylpbCmYO4ap7/Ue9/slzwpvPOJJzmzAgA==} + /@sentry/core/7.12.1: + resolution: {integrity: sha512-DFHbzHFjukhlkRZ5xzfebx0IBzblW43kmfnalBBq7xEMscUvnhsYnlvL9Y20tuPZ/PrTcq4JAHbFluAvw6M0QQ==} engines: {node: '>=8'} dependencies: - '@sentry/hub': 7.8.1 - '@sentry/types': 7.8.1 - '@sentry/utils': 7.8.1 + '@sentry/hub': 7.12.1 + '@sentry/types': 7.12.1 + '@sentry/utils': 7.12.1 tslib: 1.14.1 dev: false - /@sentry/electron/4.0.0: - resolution: {integrity: sha512-VBThihqk3Xm7sWxUJfzIm9pbKG70U2fM+Gjn0QaeC4bwt3B0z285lygSkM6VstEAZBWXI2sIt4iM216d4DeIVw==} + /@sentry/electron/4.0.2: + resolution: {integrity: sha512-1xeOSyPzf+hZs8E8g4fIHpnaf944f9L0t7TANr22zQ2rJ87kAhPlnq11EzEJxanfAKMNiAajtu30cmx3J7YFJQ==} dependencies: - '@sentry/browser': 7.8.1 - '@sentry/core': 7.8.1 - '@sentry/hub': 7.8.1 - '@sentry/node': 7.8.1 - '@sentry/types': 7.8.1 - '@sentry/utils': 7.8.1 + '@sentry/browser': 7.12.1 + '@sentry/core': 7.12.1 + '@sentry/hub': 7.12.1 + '@sentry/node': 7.12.1 + '@sentry/types': 7.12.1 + '@sentry/utils': 7.12.1 deepmerge: 4.2.2 tslib: 2.4.0 transitivePeerDependencies: - supports-color dev: false - /@sentry/hub/7.8.1: - resolution: {integrity: sha512-AxwyGyS9Lp4XsURu4t8opa5vZ+NAB6I/n+B/Uix3YZea9z8jdWYAu9vsXSizOrtxekc/i7ZN4bnlNgXVHix0iA==} + /@sentry/hub/7.12.1: + resolution: {integrity: sha512-KLVnVqXf+CRmXNy9/T8K2/js7QvOQ94xtgP5KnWJbu2rl+JhxnIGiBRF51lPXFIatt7zWwB9qNdMS8lVsvLMGQ==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.8.1 - '@sentry/utils': 7.8.1 + '@sentry/types': 7.12.1 + '@sentry/utils': 7.12.1 tslib: 1.14.1 dev: false - /@sentry/integrations/7.11.1: - resolution: {integrity: sha512-G4aw9X2WdRGwLk/2pAj+5LuZnLM4u1GG3o8bOWNASR9E7IiQQ9ERYlnfW7jas+08B1Q61WLwJPXZhJxvQfxLQw==} + /@sentry/integrations/7.13.0: + resolution: {integrity: sha512-el5tonn/96fC+GMco70TXI2yrGmDLSFc0THKO/r9YEIJjqcK1KV1C6jJhTWt09ZBAgoeRCXSMn5xvdl3fc9Zrw==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.11.1 - '@sentry/utils': 7.11.1 + '@sentry/types': 7.13.0 + '@sentry/utils': 7.13.0 localforage: 1.10.0 tslib: 1.14.1 dev: false - /@sentry/node/7.8.1: - resolution: {integrity: sha512-gQHeIip7QudeK1YWrLyZPc7nfirhbVDJ3gfCfL9mLT724Sk8gKd1kcpU1niI+wwIwY7SOpJqX4Oh/F0lRKjzDQ==} + /@sentry/node/7.12.1: + resolution: {integrity: sha512-ZVT2+lLd3gbhOuCOczlFLHH2KnD4EXrq6jRp5Sb2vsSZGVHnVsbD5j7i54zbSBqcydlFqzVMqWSysGTKoYiimw==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.8.1 - '@sentry/hub': 7.8.1 - '@sentry/types': 7.8.1 - '@sentry/utils': 7.8.1 + '@sentry/core': 7.12.1 + '@sentry/hub': 7.12.1 + '@sentry/types': 7.12.1 + '@sentry/utils': 7.12.1 cookie: 0.4.2 https-proxy-agent: 5.0.1 lru_map: 0.3.3 @@ -1898,29 +1902,29 @@ packages: - supports-color dev: false - /@sentry/types/7.11.1: - resolution: {integrity: sha512-gIEhOPxC2cjrxQ0+K2SFJ1P6e/an5osSxVc9OOtekN28eHtVsXFCLB8XVWeNQnS7N2VkrVrkqORMBz1kvIcvVQ==} + /@sentry/types/7.12.1: + resolution: {integrity: sha512-VGZs39SZgMcCGv7H0VyFy1LEFGsnFZH590JUopmz6nG63EpeYQ2xzhIoPNAiLKbyUvBEwukn+faCg3u3MGqhgQ==} engines: {node: '>=8'} dev: false - /@sentry/types/7.8.1: - resolution: {integrity: sha512-LOoaeBXVI23Kh5SpIbxSRiJ6+eYZXVOFyPFH1T1mGBj95LPwRMqOdg0lUTmFJGBKbDGDB/YNjNnu1kQ7GrXBXw==} + /@sentry/types/7.13.0: + resolution: {integrity: sha512-ttckM1XaeyHRLMdr79wmGA5PFbTGx2jio9DCD/mkEpSfk6OGfqfC7gpwy7BNstDH/VKyQj/lDCJPnwvWqARMoQ==} engines: {node: '>=8'} dev: false - /@sentry/utils/7.11.1: - resolution: {integrity: sha512-tRVXNT5O9ilkV31pyHeTqA1PcPQfMV/2OR6yUYM4ah+QVISovC0f0ybhByuH5nYg6x/Gsnx1o7pc8L1GE3+O7A==} + /@sentry/utils/7.12.1: + resolution: {integrity: sha512-Dh8B13pC0u8uLM/zf+oZngyg808c6BDEO94F7H+h3IciCVVd92A0cOQwLGAEdf8srnJgpZJNAlSC8lFDhbFHzQ==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.11.1 + '@sentry/types': 7.12.1 tslib: 1.14.1 dev: false - /@sentry/utils/7.8.1: - resolution: {integrity: sha512-isUZjft4HWTOk1Z58KFJ/zzXeFtIJgP82CkYQlW464ZR2WCqPHYlXXXRWZpOHOfMnrf+gWeX9WAGS9rTAdhiSg==} + /@sentry/utils/7.13.0: + resolution: {integrity: sha512-jnR85LgRLSk7IQe2OhKOPMY4fasJCNQNW0iCXsH+S2R1qnsF+N4ksNkQ+7JyyM9E7F03YpI2qd76bKY0VIn5iA==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.8.1 + '@sentry/types': 7.13.0 tslib: 1.14.1 dev: false @@ -1959,14 +1963,14 @@ packages: /@types/adm-zip/0.5.0: resolution: {integrity: sha512-FCJBJq9ODsQZUNURo5ILAQueuA8WJhRvuihS3ke2iI25mJlfV2LK8jG2Qj2z2AWg8U0FtWWqBHVRetceLskSaw==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/body-parser/1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/cacheable-request/6.0.2: @@ -1974,14 +1978,14 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.7.13 + '@types/node': 18.7.18 '@types/responselike': 1.0.0 dev: false /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/debug/4.1.7: @@ -2014,13 +2018,13 @@ packages: /@types/express-serve-static-core/4.17.30: resolution: {integrity: sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true - /@types/express/4.17.13: - resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} + /@types/express/4.17.14: + resolution: {integrity: sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==} dependencies: '@types/body-parser': 1.19.2 '@types/express-serve-static-core': 4.17.30 @@ -2031,14 +2035,14 @@ packages: /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.0 - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/html-minifier-terser/5.1.2: @@ -2060,7 +2064,7 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 /@types/long/4.0.2: resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} @@ -2086,14 +2090,14 @@ packages: resolution: {integrity: sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==} dev: false - /@types/node/18.7.13: - resolution: {integrity: sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==} + /@types/node/18.7.18: + resolution: {integrity: sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==} /@types/plist/3.0.2: resolution: {integrity: sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==} requiresBuild: true dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 xmlbuilder: 15.1.1 dev: true optional: true @@ -2113,7 +2117,7 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 /@types/semver/7.3.12: resolution: {integrity: sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==} @@ -2123,7 +2127,7 @@ packages: resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} dependencies: '@types/mime': 3.0.1 - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/source-list-map/0.1.2: @@ -2153,7 +2157,7 @@ packages: /@types/webpack-sources/3.2.0: resolution: {integrity: sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 '@types/source-list-map': 0.1.2 source-map: 0.7.4 dev: true @@ -2161,7 +2165,7 @@ packages: /@types/webpack/4.41.32: resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 '@types/tapable': 1.0.8 '@types/uglify-js': 3.17.0 '@types/webpack-sources': 3.2.0 @@ -2172,7 +2176,7 @@ packages: /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true /@types/yargs-parser/21.0.0: @@ -2189,7 +2193,7 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 dev: true optional: true @@ -3459,6 +3463,16 @@ packages: /concat-map/0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /concat-stream/1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.7 + typedarray: 0.0.6 + dev: false + /conf/10.2.0: resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} engines: {node: '>=12'} @@ -4285,6 +4299,13 @@ packages: mkdirp: 0.5.6 dev: false + /elementtree/0.1.7: + resolution: {integrity: sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==} + engines: {node: '>= 0.4.0'} + dependencies: + sax: 1.1.4 + dev: false + /elliptic/6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -6064,7 +6085,6 @@ packages: /isarray/1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true /isbinaryfile/3.0.3: resolution: {integrity: sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==} @@ -6119,18 +6139,18 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.7.13 + '@types/node': 18.7.18 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jimp/0.16.1: - resolution: {integrity: sha512-+EKVxbR36Td7Hfd23wKGIeEyHbxShZDX6L8uJkgVW3ESA9GiTEPK08tG1XI2r/0w5Ch0HyJF5kPqF9K7EmGjaw==} + /jimp/0.16.2: + resolution: {integrity: sha512-UpItBk81a92f8oEyoGYbO3YK4QcM0hoIyuGHmShoF9Ov63P5Qo7Q/X2xsAgnODmSuDJFOtrPtJd5GSWW4LKdOQ==} dependencies: '@babel/runtime': 7.18.9 - '@jimp/custom': 0.16.1 - '@jimp/plugins': 0.16.1_@jimp+custom@0.16.1 - '@jimp/types': 0.16.1_@jimp+custom@0.16.1 + '@jimp/custom': 0.16.2 + '@jimp/plugins': 0.16.2_@jimp+custom@0.16.2 + '@jimp/types': 0.16.2_@jimp+custom@0.16.2 regenerator-runtime: 0.13.9 dev: false @@ -6891,6 +6911,11 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true + /network-address/1.1.2: + resolution: {integrity: sha512-Q6878fmvItA1mE7H9Il46lONgFgTzX2f98zkS0c2YlkCACzNjwvum/8Kq693IQpxe9zy+w+Zm/4p0wQreLEtZw==} + hasBin: true + dev: false + /nice-try/1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true @@ -7561,6 +7586,12 @@ packages: engines: {node: '>=4'} dev: true + /prettier/2.7.1: + resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-error/2.1.2: resolution: {integrity: sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==} dependencies: @@ -7570,7 +7601,6 @@ packages: /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true /process/0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} @@ -7619,7 +7649,7 @@ packages: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 18.7.13 + '@types/node': 18.7.18 long: 4.0.0 dev: false @@ -7808,7 +7838,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true /readable-stream/3.6.0: resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} @@ -8097,7 +8126,6 @@ packages: /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer/5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -8117,6 +8145,10 @@ packages: truncate-utf8-bytes: 1.0.2 dev: true + /sax/1.1.4: + resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==} + dev: false + /sax/1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -8694,7 +8726,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /string_decoder/1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -9081,8 +9112,12 @@ packages: is-typedarray: 1.0.0 dev: true - /typescript/4.7.4: - resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} + /typedarray/0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + dev: false + + /typescript/4.8.3: + resolution: {integrity: sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==} engines: {node: '>=4.2.0'} hasBin: true dev: true @@ -9838,10 +9873,10 @@ packages: got: 11.8.5 dev: false - github.com/castlabs/electron-releases/a1749958b51da6ce1596992260cbacb602c23762: - resolution: {tarball: https://codeload.github.com/castlabs/electron-releases/tar.gz/a1749958b51da6ce1596992260cbacb602c23762} + github.com/castlabs/electron-releases/5878335d0e4d27df7ac19771ab73725ede3849b6: + resolution: {tarball: https://codeload.github.com/castlabs/electron-releases/tar.gz/5878335d0e4d27df7ac19771ab73725ede3849b6} name: electron - version: 21.0.0-alpha.5 + version: 21.0.0-beta.5 engines: {node: '>= 10.17.0'} hasBin: true requiresBuild: true @@ -9878,8 +9913,8 @@ packages: - supports-color dev: false - github.com/ciderapp/node_airtunes2/63e3a34a551aa700f89b622d65d19f52f48548ce: - resolution: {tarball: https://codeload.github.com/ciderapp/node_airtunes2/tar.gz/63e3a34a551aa700f89b622d65d19f52f48548ce} + github.com/ciderapp/node_airtunes2/92dc56e0b86e02aa40b569880d58bc2fc0eee2c7: + resolution: {tarball: https://codeload.github.com/ciderapp/node_airtunes2/tar.gz/92dc56e0b86e02aa40b569880d58bc2fc0eee2c7} name: airtunes2 version: 2.3.2 hasBin: true @@ -9936,6 +9971,31 @@ packages: readable-stream: 1.1.14 dev: false + github.com/vapormusic/node-upnp-device-client/371819a856573e3624768bd0a113ac6dc084053d: + resolution: {tarball: https://codeload.github.com/vapormusic/node-upnp-device-client/tar.gz/371819a856573e3624768bd0a113ac6dc084053d} + name: upnp-device-client + version: 1.0.2 + dependencies: + concat-stream: 1.6.2 + debug: 2.6.9 + elementtree: 0.1.7 + network-address: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + github.com/vapormusic/node-upnp-mediarenderer-client/d4856931500026eb6c4c67b3eb12c1c34755627e: + resolution: {tarball: https://codeload.github.com/vapormusic/node-upnp-mediarenderer-client/tar.gz/d4856931500026eb6c4c67b3eb12c1c34755627e} + name: upnp-mediarenderer-client + version: 1.4.0 + dependencies: + debug: 2.6.9 + elementtree: 0.1.7 + upnp-device-client: github.com/vapormusic/node-upnp-device-client/371819a856573e3624768bd0a113ac6dc084053d + transitivePeerDependencies: + - supports-color + dev: false + github.com/wavesplatform/curve25519-js/cae68782564b83ffa1c7c757e40a3944cb140290: resolution: {tarball: https://codeload.github.com/wavesplatform/curve25519-js/tar.gz/cae68782564b83ffa1c7c757e40a3944cb140290} name: axlsign diff --git a/src/i18n/en_OWO.json b/src/i18n/en_OWO.json index e883695e..50207a97 100644 --- a/src/i18n/en_OWO.json +++ b/src/i18n/en_OWO.json @@ -543,6 +543,7 @@ "settings.option.connectivity.lastfmScrobble.filterLoop.description": "Pwevent wooped twacks fwom being scwobbwed ow dispwayed in the Nyow Pwaying wist on Wast.fm.", "settings.option.connectivity.lastfmScrobble.filterTypes": "Fiwtew Media Types (Wast.fm)", "settings.option.connectivity.lastfmScrobble.manualToken": "Entew Wast.fm Token Manyuawwy", + "settings.option.connectivity.lastfmScrobble.manualToken.link": "Cwick hewe to get a Wast.fm token", "settings.notyf.connectivity.lastfmScrobble.connectError": "Wast.fm Connyection Timed Out", "settings.notyf.connectivity.lastfmScrobble.connectSuccess": "Wast.fm Connyection Successfuw", "settings.notyf.connectivity.lastfmScrobble.connecting": "Connyecting to Wast.fm...", diff --git a/src/i18n/en_US.json b/src/i18n/en_US.json index 50af04eb..9163dd1b 100644 --- a/src/i18n/en_US.json +++ b/src/i18n/en_US.json @@ -10,6 +10,7 @@ "notification.updatingLibrarySongs": "Updating library songs...", "notification.updatingLibraryAlbums": "Updating library albums...", "notification.updatingLibraryArtists": "Updating library artists...", + "notification.buildingPlaylistCache": "Building Playlist Cache...", "term.variables": "Variables", "term.appleInc": "Apple Inc.", "term.appleMusic": "Apple Music", @@ -260,6 +261,7 @@ "action.removeFromQueue.success": "Removed from Queue", "action.removeFromQueue.error": "Error Removing from Queue", "action.createPlaylist": "Create a New Playlist", + "action.addToPlaylist.duplicate": "Item already exists in playlist. Do you want to continue?", "action.addToPlaylist": "Add to Playlist", "action.removeFromPlaylist": "Remove from Playlist", "action.addToFavorites": "Add to Favorites", @@ -494,6 +496,7 @@ "settings.option.visual.theme.viewInfo": "View Info", "settings.option.visual.theme.github.available": "Available", "settings.option.visual.theme.github.applied": "Applied", + "settings.notyf.visual.theme.updateAvailable": "[Themes] {{ theme }} has an update available", "settings.notyf.visual.theme.install.success": "Theme installed successfully", "settings.notyf.visual.theme.install.error": "Theme installation failed", "settings.header.visual.plugin": "Plugin", @@ -529,7 +532,13 @@ "settings.option.connectivity.discordRPC": "Discord Rich Presence", "settings.option.connectivity.discordRPC.clientName": "Client Name", "settings.option.connectivity.discordRPC.clearOnPause": "Clear Discord Rich Presence on Pause", - "settings.option.connectivity.discordRPC.hideButtons": "Hide buttons on Discord Rich Presence", + "settings.option.connectivity.discordRPC.showActivityButtons": "Show Activity Buttons", + "settings.option.connectivity.discordRPC.firstButton": "First Activity Button", + "settings.option.connectivity.discordRPC.secondButton": "Second Activity Button", + "settings.option.connectivity.discordRPC.buttons.listenOnCider": "Listen on Cider", + "settings.option.connectivity.discordRPC.buttons.viewOnAppleMusic": "View on Apple Music", + "settings.option.connectivity.discordRPC.buttons.viewOnOtherMusicServices": "View on Other Music Services", + "settings.option.connectivity.discordRPC.showSongLink": "Show Song.link button instead of Apple Music button on Discord Rich Presence", "settings.option.connectivity.discordRPC.hideTimestamp": "Hide timestamp on Discord Rich Presence", "settings.option.connectivity.discordRPC.detailsFormat": "Details Format", "settings.option.connectivity.discordRPC.stateFormat": "State Format", @@ -542,6 +551,7 @@ "settings.option.connectivity.lastfmScrobble.filterLoop": "Filter looped track (Last.fm)", "settings.option.connectivity.lastfmScrobble.filterLoop.description": "Prevent looped tracks from being scrobbled or displayed in the Now Playing list on Last.fm.", "settings.option.connectivity.lastfmScrobble.filterTypes": "Filter Media Types (Last.fm)", + "settings.option.connectivity.lastfmScrobble.filterTypes.description": "Prevent tracks of the selected media types from being scrobbled or displayed in the Now Playing list on Last.fm.", "settings.option.connectivity.lastfmScrobble.manualToken": "Enter Last.fm Token Manually", "settings.option.connectivity.lastfmScrobble.manualToken.link": "Click here to get a Last.fm token", "settings.notyf.connectivity.lastfmScrobble.connectError": "Last.fm Connection Timed Out", diff --git a/src/i18n/hu_HU.json b/src/i18n/hu_HU.json index a2dabd7a..19c4bbed 100644 --- a/src/i18n/hu_HU.json +++ b/src/i18n/hu_HU.json @@ -2,7 +2,7 @@ "i18n.languageName": "Magyar", "i18n.languageNameEnglish": "Hungarian", "i18n.category": "main", - "i18n.authors": "@Amaru", + "i18n.authors": "@Amaru @BenjaminStonawski", "app.name": "Cider", "date.format": "${y} ${m} ${d}", "dialog.cancel": "Mégsem", @@ -22,7 +22,6 @@ "term.logout": "Kijelentkezés", "term.login": "Bejelentkezés", "term.quickNav": "Főmenü", - "term.cast": "Tükrözés", "term.about": "Névjegy", "term.privateSession": "Privát hallgatás", "term.disablePrivateSession": "Privát hallgatás kikapcsolása", @@ -32,6 +31,12 @@ "term.miniplayer": "Minilejátszó", "term.history": "Előzmények", "term.search": "Keresés", + "term.scroll": "Görgetési mód", + "term.scroll.infinite": "Végtelen", + "term.scroll.paged": "${songsPerPage} oldalanként", + "term.live": "ÉLŐ", + "term.showSearch": "Keresési mező megjelenítése", + "term.hideSearch": "Keresési mező elrejtése", "term.library": "Könyvtár", "term.listenNow": "Hallgatás most", "term.browse": "Böngészés", @@ -41,8 +46,8 @@ "term.albums": "Albumok", "term.artists": "Előadók", "term.podcasts": "Podcastok", - "term.charts": "Listák", "term.playlists": "Lejátszási listák", + "term.charts": "Listák", "term.playlist": "Lejátszási lista", "term.newPlaylist": "Új lejátszási lista", "term.newPlaylistFolder": "Új mappa", @@ -52,10 +57,12 @@ "term.navigateBack": "Visszalépés", "term.navigateForward": "Előrelépés", "term.play": "Lejátszás", + "term.playpause": "Lejátszás/Megállítás", "term.pause": "Megállítás", "term.stop": "Leállítás", "term.previous": "Előző", "term.next": "Következő", + "term.skip": "Átugrás", "term.shuffle": "Keverés", "term.enableShuffle": "Keverés bekapcsolása", "term.disableShuffle": "Keverés kikapcsolása", @@ -178,6 +185,7 @@ "term.top": "Top", "term.version": "Verzió", "term.noVideos": "Nincs találat", + "term.plugins": "Plug-in-ok", "term.plugin": "Plug-in", "term.pluginMenu": "Plug-in Menü", "term.pluginMenu.none": "Nem találhatóak interaktív pluginok", @@ -194,6 +202,22 @@ "term.confirmLogout": "Biztosan ki szeretnél jelentkezni?", "term.creditDesignedBy": "Készítette ${authorUsername}", "term.discNumber": "${discNumber}. lemez", + "term.reload": "Biztosan újraindítod a Cider-t?", + "term.toggleprivate": "Privát zenehallgatás", + "term.webremote": "Távirányító", + "term.cast": "Tükrözés", + "term.cast2": "Tükrözés eszközökre", + "term.quit": "Kilépés", + "term.zoomin": "Nagyítás", + "term.zoomout": "Kicsinyítés", + "term.zoomreset": "Nagyítás visszaállítása", + "term.fullscreen": "Teljes képernyő", + "term.nowPlaying": "Jelenleg lejátszás alatt", + "home.syncFavorites": "Kedvencek szinkronizálása", + "home.syncFavorites.gettingArtists": "Kedvenc előadók betöltése..", + "action.favorite": "Kedvenc", + "action.removeFavorite": "Eltávolítás a kedvencek közül", + "action.refresh": "Frissítés", "home.title": "Kezdőlap", "home.recentlyPlayed": "Nemrég játszott", "home.recentlyAdded": "Nemrég hozzáadott", @@ -222,6 +246,7 @@ "action.delete": "Törlés", "action.edit": "Szerkesztés", "action.done": "Kész", + "action.submit": "Befejezés", "action.editTracklist": "Dalok szerkesztése", "action.addToLibrary": "Hozzáadás a Könyvtárhoz", "action.addToLibrary.success": "Hozzáadva a Könyvtárhoz", @@ -266,11 +291,7 @@ "action.export": "Exportálás", "action.showAlbum": "Teljes album megjelenítése", "action.tray.minimize": "Kicsinyítés a Tálcára", - "action.tray.quit": "Bezárás", "action.tray.show": "Cider megjelenítése", - "action.tray.playpause": "Lejátszás/Megállítás", - "action.tray.next": "Következő", - "action.tray.previous": "Előző", "action.tray.listento": "Ezt hallgatja:", "action.update": "Frissítés", "action.install": "Telepítés", @@ -290,44 +311,25 @@ "action.createNew": "Új létrehozása...", "action.openArtworkInBrowser": "Borító megnyitása a böngészőben", "action.scrollToTop": "Felülre", - "menubar.options.about": "Névjegy", - "menubar.options.settings": "Beállítások", - "menubar.options.quit": "Bezárás", - "menubar.options.view": "View ", - "menubar.options.reload": "Reload", - "menubar.options.forcereload": "Force Reload", + "menubar.options.view": "Jelenítse meg a(z) ", + "menubar.options.reload": "Újratöltés", + "menubar.options.forcereload": "Kényszerített újratöltés", "menubar.options.toggledevtools": "Fejlesztői eszközök", "menubar.options.window": "Ablak", "menubar.options.minimize": "Kicsinyítés", - "menubar.options.toggleprivate": "Privát hallgatás ki/be", - "menubar.options.webremote": "Távirányító", - "menubar.options.audio": "Hangbeállítások", "menubar.options.plugins": "Plug-in Menü", - "menubar.options.controls": "Controls", - "menubar.options.next": "Következő", - "menubar.options.playpause": "Lejátszás/Megállítás", - "menubar.options.previous": "Előző", + "menubar.options.controls": "Irányítások", "menubar.options.volumeup": "Hangerő fel", "menubar.options.volumedown": "Hangerő le", - "menubar.options.browse": "Böngészés", - "menubar.options.artists": "Előadók", - "menubar.options.search": "Keresés", - "menubar.options.albums": "Albumok", - "menubar.options.cast": "Tükrözés", "menubar.options.account": "Fiók", - "menubar.options.accountsettings": "Fiókbeállítások", "menubar.options.signout": "Kijelentkezés", "menubar.options.support": "Támogatás", - "menubar.options.discord": "Discord", - "menubar.options.github": "GitHub Wiki", - "menubar.options.report": "Report a...", - "menubar.options.bug": "Bug", - "menubar.options.feature": "Feature Request", - "menubar.options.trans": "Translation Report/Request", + "menubar.options.report": "Jelentsd be egy...", + "menubar.options.bug": "Hiba", + "menubar.options.feature": "Funkciókérés", + "menubar.options.trans": "Fordítással kapcsolatos bejelentés/kérelem", "menubar.options.license": "Licensz megtekintése", "menubar.options.conf": "Open Configuration File in Editor", - "menubar.options.listennow": "Hallgatás most", - "menubar.options.recentlyAdded": "Nemrég hozzáadott", "menubar.options.songs": "Dalok", "settings.header.general": "Általános", "settings.header.general.description": "A Cider általános beállításainak módosítása.", @@ -348,13 +350,19 @@ "settings.option.general.customizeSidebar": "Oldalsáv elemeinek testreszabása", "settings.option.general.customizeSidebar.customize": "Testreszabás", "settings.option.general.keybindings": "Billentyűparancsok", + "settings.option.general.keybindings.library": "Könyvtár", + "settings.option.general.keybindings.session": "Zenehallgatás", + "settings.option.general.keybindings.control": "Irányítások", + "settings.option.general.keybindings.interface": "Felület", + "settings.option.general.keybindings.advanced": "Haladó", "settings.option.general.keybindings.pressCombination": "Nyomj le két billentyűt a parancs frissítéséhez.", "settings.option.general.keybindings.pressEscape": "Nyomj Escape-et a visszalépéshez.", "settings.notyf.general.keybindings.update.success": "Billentyűparancs sikeresen frissítve", "settings.prompt.general.keybindings.update.success": "Billentyűparancs sikeresen frissítve. Kattints az OK-ra a Cider újraindításához!", - "settings.option.general.keybindings.open": "Megnyitás", "settings.option.general.themeUpdateNotification": "Automatikus témafrissítések keresése", "settings.option.general.showLovedTracksInline": "Show loved tracks inline", + "settings.option.general.pagination": "Elemek megjelenítése oldalanként", + "settings.options.general.pagination.description": "Ez határozza meg, hogy mennyi zeneszám/album jelenjen meg eleinte a végtelen görgetésben, vagy hány zeneszám/album jelenjen meg egyetlen oldalon", "settings.description.search": "Keresés", "settings.description.albums": "Albumkönyvtár", "settings.description.artists": "Előadókönyvtár", @@ -410,6 +418,15 @@ "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.description": "Az Atmoszféra Változató™️ modul módját változtatja meg.", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_STANDARD": "Natural (Standard)", "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.NATURAL_PLUS": "Natural (Plus)", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E68_1": "Rock Salt Cheese Foam Tea", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E68_2": "Uji Matcha Milk Tea", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.E168_1": "Jasmine Macchiato", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.Z3600": "Hokkaido Milk Tea", + "settings.option.audio.enableAdvancedFunctionality.atmosphereRealizerMode.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": "Feljavítja az AAC hang minőségét egy valós idejű algoritmus segítségével, ami kihasználja az emberi hallás pszichoakusztikus modelljeit és az AAC hang kódolási jellemzőit.", "settings.warn.audio.enableAdvancedFunctionality.ciderPPE.compatibility": "A CAP nem kompatibilis a Térbeli Hanggal. Kapcsold ki a Térbeli Hangot a folytatáshoz.", @@ -426,23 +443,22 @@ "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.description": "Előre beállított Térbeli hangzás, ami letiltja a Térbeli Hang testreszabási beállításait. A Térbeli Hangot be kell kapcsolni az engedélyezéshez.", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile": "Cider Térbeli Hangprofil", "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.description": "Megváltoztatja a Térbeli Hang előbeállítás profilját.", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "Standard", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.soundstage": "Soundstage", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation": "Separation", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimal", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiophile", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "Diffused", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "Encore", - "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "Expanded Encore", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.standard": "Alapértelmezett", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.soundstage": "Hangszínpad", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.separation": "Elválasztás", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.minimal": "Minimális", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.audiophile": "Audiofil", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.diffused": "Diffúz", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "Ráadás", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "Kiterjesztett ráadás", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "A Térbeli Hang nem kompatibilis a CAP-pal. Kapcsold ki a CAP-ot a folytatáshoz.", "settings.option.audio.dbspl.display": "dB SPL kijelzés", "settings.option.audio.dbspl.description": "(Haladó felhasználóknak) A hangerő dB SPL-ben történő kijelzése a dBFS helyett.", "settings.option.audio.dbfs.calibration": "0 dBFS kalibráció", - "settings.option.audio.dbfs.description": "Enter the peak Z-weighted dB SPL when Cider is at 0 dBFS.", + "settings.option.audio.dbfs.description": "Adja meg a csúcs Z-súlyozott dB SPL értékét, amikor a Cider 0 dBFS-en van.", "settings.header.visual": "Vizuális", "settings.header.visual.description": "A Cider vizuális beállításainak módosítása.", "settings.option.visual.windowStyle": "Ablakelrendezés", - "settings.option.visual.purplePodcastPlaybackBar": "Lila lejátszási sáv a Podcastoknál", "settings.option.visual.windowBackgroundStyle": "Ablakháttér stílusa", "settings.header.visual.windowBackgroundStyle.none": "Sehol", "settings.header.visual.windowBackgroundStyle.artwork": "Borító", @@ -465,12 +481,14 @@ "settings.option.visual.uiscale": "Nagyítás mértéke", "settings.header.visual.theme": "Téma", "settings.option.visual.theme.github.download": "Telepítés GitHub URL-ről", + "settings.option.visual.theme.github.openfolder": "Témák mappa megnyitása", "settings.option.visual.theme.github.explore": "Témák felfedezése", "settings.header.visual.theme.github.page": "Témák a GitHub-ról", "settings.option.visual.theme.github.install.confirm": "Biztosan szeretnéd telepíteni a(z) {{ repo }} témát?", "settings.prompt.visual.theme.github.URL": "Add meg a telepítendő téma URL-jét", "settings.prompt.visual.theme.uninstallTheme": "Biztos, hogy törölni szeretnéd a {{ theme }} témát?", "settings.option.visual.theme.checkForUpdates": "Frissítések keresése", + "settings.header.visual.styles": "Stílusok", "settings.option.visual.theme.manageStyles": "Témakezelő", "settings.option.visual.theme.uninstall": "Törlés", "settings.option.visual.theme.viewInfo": "Információk", @@ -522,6 +540,13 @@ "settings.option.connectivity.lastfmScrobble.nowPlaying": "Last.fm Now Playing engedélyezése", "settings.option.connectivity.lastfmScrobble.removeFeatured": "Közreműködő zenészek eltávolítása a címből (Last.fm)", "settings.option.connectivity.lastfmScrobble.filterLoop": "Ismételt zeneszám szűrése (Last.fm)", + "settings.option.connectivity.lastfmScrobble.filterLoop.description": "Ismételt zeneszámok scrobble-zésének és megjelenítésének letiltása a jelenleg lejátszás alatt listában (Last.fm)", + "settings.option.connectivity.lastfmScrobble.filterTypes": "Médiatípusok szűrése (Last.fm)", + "settings.option.connectivity.lastfmScrobble.manualToken": "Last.fm Token beírása manuálisan", + "settings.option.connectivity.lastfmScrobble.manualToken.link": "Last.fm Token legenerálásához kattints ide", + "settings.notyf.connectivity.lastfmScrobble.connectError": "A Last.fm csatlakozása során időtullépés lépett fel", + "settings.notyf.connectivity.lastfmScrobble.connectSuccess": "A Last.fm csatlakozása sikeres volt", + "settings.notyf.connectivity.lastfmScrobble.connecting": "Last.fm-hez való csatlakozás...", "settings.header.debug": "Hibaelhárítás", "settings.option.debug.copy_log": "Napló másolása a vágólapra", "settings.option.debug.openAppData": "Cider mappa megnyitása", @@ -533,17 +558,24 @@ "settings.option.experimental.unknownPlugin.description": "Engedélyezi azon pluginok telepítését is, amik nem tartoznak a Cider Plugin Gyűjteménybe, ezáltal lehetséges biztonsági kockázatot jelenthetnek.", "settings.option.experimental.compactUI": "Kompakt felület", "settings.option.window.close_button_hide": "A bezárás gomb rejtse el az alkalmazást", + "settings.option.window.maxElementScale": "Maximális elem skála", "settings.option.experimental.inline_playlists": "Ablakon belüli lejátszási listák és albumok", "settings.option.advanced.playlistTrackMapping": "Lejátszási lista feltérképezés", "settings.option.advanced.playlistTrackMapping.description": "Engedélyezi a lejátszási listák mély feltérképezését, ami meg tudja határozni, hogy egy zeneszám mely lejátszási listákba található. A lejátszási lista gyorsítótár felépítésének időtartalma nagy mértékben megnőhet.", "settings.option.visual.transparent": "Átlátszó keret", - "settings.option.visual.transparent.description": "a Témák támogatása és újraindítás szükséges", + "settings.option.visual.transparent.description": "a Témák támogatása (újraindítás szükséges)", + "settings.option.visual.customAccentColor": "Egyedi kiemelőszín", + "settings.option.visual.accentColor": "Kiemelőszín", + "settings.option.visual.purplePodcastPlaybackBar": "Lila lejátszási sáv podcastok lejátszása esetén", + "settings.option.visual.compactArtistHeader": "Kompakt előadó fejléc", + "settings.option.visual.windowColor": "Az ablak színárnyalata", + "settings.header.visual.windowBackgroundStyle.color": "Színárnyalat", "settings.header.advanced": "Haladó", - "settings.header.connect": "Sync", - "settings.option.connect.link_account": "Enable Sync with Cider Connect", - "settings.option.connect.link_account.description": "Linking your Discord account with Cider Connect allows you to store userdata including Settings, EQ's, and eventually more once finished. (Work In Progress)", + "settings.header.connect": "Szinkronizálás", + "settings.option.connect.link_account": "Szinkronizálás a Cider Connect szolgáltatással engedélyezése", + "settings.option.connect.link_account.description": "A Discord fiók összekapcsolása a Cider Connect szolgáltatással lehetővé teszi a felhasználói adatok tárolását, beleértve a beállításokat, az EQ-kat és esetleg még többet. (Fejlesztés alatt)", "spatial.notTurnedOn": "A Térbeli Hang ki van kapcsolva, használatához kapcsolja be.", - "spatial.spatialProperties": "Térbeli Hang", + "spatial.spatialProperties": "Térbeli hang", "spatial.width": "Szélesség", "spatial.height": "Magasság", "spatial.depth": "Hosszúság", @@ -574,5 +606,32 @@ "share.platform.email": "Email", "share.platform.songLink": "song.link URL másolása", "share.platform.clipboard": "Link másolása", - "about.thanks": "Köszönet a Cider Collective csapatának és minden hozzájárulónak." + "about.thanks": "Köszönet a Cider Collective csapatának és minden hozzájárulónak.", + "oobe.yes": "Igen", + "oobe.no": "Nem", + "oobe.next": "Következő", + "oobe.previous": "Előző", + "oobe.done": "Kész", + "oobe.amupsell.title": "Még mielőtt belevágnánk", + "oobe.amupsell.text": "A Cider Apple Music előfizetést igényel\nA Cider nem támogatja az Apple Music Voice Plan-t vagy más fajta promóciós próba előfizetéseket. Ha már Apple Music előfizető vagy, lépj tovább.", + "oobe.amupsell.subscribeBtn": "Előfizetés Apple Music-ra", + "oobe.amupsell.explainBtn": "Magyarázat", + "oobe.amupsell.subscribeUrl": "https://apple.co/3MdqJVQ", + "oobe.amupsell.amWebUrl": "https://beta.music.apple.com/", + "oobe.amupsell.promoExplained": "Egyes promóciós és nem egyesült államokbeli Apple Music próba-előfizetések nem férnek hozzá a szükséges Apple Music Web Player API-khoz, amelyek a Cider működéséhez szükségesek. Ha le szeretnéd ellenőrizni, hogy az aktív előfizetésed kompatibilis a Cider-rel, kattints a {{ amWebUrl }} linkre, jelentkezz be és próbálj meg lejátszani bármilyen zenét. Ha működik, akkor jó hírunk van! Készen állsz arra, hogy használd a Cider-t. Viszont ha mégsem működött, fizess elő Apple Music-ra ezen a linken: {{ subscribeUrl }}", + "oobe.intro.title": "Üdvözlünk a Cider-ben!", + "oobe.intro.subtitle": "", + "oobe.intro.text": "Állítsunk be néhány dolgot, hogy tetszés szerint használhasd a Cider-t. Ezeket a beállításokat később bármikor módosíthatod.", + "oobe.general.title": "Általános", + "oobe.general.subtitle": "", + "oobe.general.text": "", + "oobe.audio.title": "Hang", + "oobe.audio.subtitle": "", + "oobe.audio.text": "A Cider egyedi hangolású és tervezett hangkészletekkel rendelkezik, amely gazdag, kiváló minőségű hangélményt biztosít.\nKöztük van a Cider Adrenaline, Atmosphere Realizer, és a Spatialized Audio.\nA hangkészletek engedélyezéséhez a \"Fejlett audiófunkciók\"-at be kell kapcsolni.\nA fejlett audiófunkciók engedélyezése hozzáférést biztosít ezekhez a hangkészletekhez a Cider Audio Labs szolgáltatásban, amely az alkalmazás beállításai között található.", + "oobe.audio.advancedFunctionality": "", + "oobe.visual.title": "Vizuális", + "oobe.visual.subtitle": "", + "oobe.visual.text": "", + "oobe.visual.layout.text": "A Cider két különböző ablakmegjelenéssel rendelkezik.\nA Maverick egy iTunes-ra hasonló megjelenést biztosít, a zenelejátszó az ablak tetején található.\nA Mojave egy új hullám a Cider Collective által készítve.\n\nBármikor megváltoztathatod a megjelenést a beállításokban.", + "oobe.amsignin.title": "" } diff --git a/src/i18n/nl_NL.json b/src/i18n/nl_NL.json index 2f13adab..f8642343 100644 --- a/src/i18n/nl_NL.json +++ b/src/i18n/nl_NL.json @@ -132,8 +132,8 @@ "term.reset": "Resetten", "term.tracks": "nummers", "term.track": { - "one" : "nummer", - "other" : "nummers" + "one": "nummer", + "other": "nummers" }, "term.videos": "Video's", "term.menu": "Menu", diff --git a/src/i18n/source/en_US.json b/src/i18n/source/en_US.json index 50af04eb..9163dd1b 100644 --- a/src/i18n/source/en_US.json +++ b/src/i18n/source/en_US.json @@ -10,6 +10,7 @@ "notification.updatingLibrarySongs": "Updating library songs...", "notification.updatingLibraryAlbums": "Updating library albums...", "notification.updatingLibraryArtists": "Updating library artists...", + "notification.buildingPlaylistCache": "Building Playlist Cache...", "term.variables": "Variables", "term.appleInc": "Apple Inc.", "term.appleMusic": "Apple Music", @@ -260,6 +261,7 @@ "action.removeFromQueue.success": "Removed from Queue", "action.removeFromQueue.error": "Error Removing from Queue", "action.createPlaylist": "Create a New Playlist", + "action.addToPlaylist.duplicate": "Item already exists in playlist. Do you want to continue?", "action.addToPlaylist": "Add to Playlist", "action.removeFromPlaylist": "Remove from Playlist", "action.addToFavorites": "Add to Favorites", @@ -494,6 +496,7 @@ "settings.option.visual.theme.viewInfo": "View Info", "settings.option.visual.theme.github.available": "Available", "settings.option.visual.theme.github.applied": "Applied", + "settings.notyf.visual.theme.updateAvailable": "[Themes] {{ theme }} has an update available", "settings.notyf.visual.theme.install.success": "Theme installed successfully", "settings.notyf.visual.theme.install.error": "Theme installation failed", "settings.header.visual.plugin": "Plugin", @@ -529,7 +532,13 @@ "settings.option.connectivity.discordRPC": "Discord Rich Presence", "settings.option.connectivity.discordRPC.clientName": "Client Name", "settings.option.connectivity.discordRPC.clearOnPause": "Clear Discord Rich Presence on Pause", - "settings.option.connectivity.discordRPC.hideButtons": "Hide buttons on Discord Rich Presence", + "settings.option.connectivity.discordRPC.showActivityButtons": "Show Activity Buttons", + "settings.option.connectivity.discordRPC.firstButton": "First Activity Button", + "settings.option.connectivity.discordRPC.secondButton": "Second Activity Button", + "settings.option.connectivity.discordRPC.buttons.listenOnCider": "Listen on Cider", + "settings.option.connectivity.discordRPC.buttons.viewOnAppleMusic": "View on Apple Music", + "settings.option.connectivity.discordRPC.buttons.viewOnOtherMusicServices": "View on Other Music Services", + "settings.option.connectivity.discordRPC.showSongLink": "Show Song.link button instead of Apple Music button on Discord Rich Presence", "settings.option.connectivity.discordRPC.hideTimestamp": "Hide timestamp on Discord Rich Presence", "settings.option.connectivity.discordRPC.detailsFormat": "Details Format", "settings.option.connectivity.discordRPC.stateFormat": "State Format", @@ -542,6 +551,7 @@ "settings.option.connectivity.lastfmScrobble.filterLoop": "Filter looped track (Last.fm)", "settings.option.connectivity.lastfmScrobble.filterLoop.description": "Prevent looped tracks from being scrobbled or displayed in the Now Playing list on Last.fm.", "settings.option.connectivity.lastfmScrobble.filterTypes": "Filter Media Types (Last.fm)", + "settings.option.connectivity.lastfmScrobble.filterTypes.description": "Prevent tracks of the selected media types from being scrobbled or displayed in the Now Playing list on Last.fm.", "settings.option.connectivity.lastfmScrobble.manualToken": "Enter Last.fm Token Manually", "settings.option.connectivity.lastfmScrobble.manualToken.link": "Click here to get a Last.fm token", "settings.notyf.connectivity.lastfmScrobble.connectError": "Last.fm Connection Timed Out", diff --git a/src/i18n/zh_CN.json b/src/i18n/zh_CN.json index 61b59696..a678c49e 100644 --- a/src/i18n/zh_CN.json +++ b/src/i18n/zh_CN.json @@ -7,10 +7,10 @@ "date.format": "${y}年${m}月${d}日", "dialog.cancel": "取消", "dialog.ok": "好", - "notification.updatingLibrarySongs": "正在更新歌曲资料库...", - "notification.updatingLibraryAlbums": "正在更新专辑资料库...", - "notification.updatingLibraryArtists": "正在更新艺人资料库...", - "term.variables": "Variables", + "notification.updatingLibrarySongs": "正在更新歌曲资料库……", + "notification.updatingLibraryAlbums": "正在更新专辑资料库……", + "notification.updatingLibraryArtists": "正在更新艺人资料库……", + "term.variables": "变量", "term.appleInc": "Apple Inc.", "term.appleMusic": "Apple Music", "term.applePodcasts": "Apple Podcasts", @@ -131,7 +131,7 @@ "term.amLive": "Apple Music Live", "term.language": "语言", "term.funLanguages": "恶搞", - "term.noLyrics": "加载中... / 无搜索结果 / 纯音乐", + "term.noLyrics": "纯音乐 / 无歌词", "term.copyright": "版权所有", "term.rightsReserved": "保留所有权利。", "term.sponsor": "赞助", @@ -159,7 +159,7 @@ "term.defaultPresets": "默认预设", "term.userPresets": "用户预设", "term.requestError": "请求出现一个问题。", - "term.song.link.generate": "获取 song.link 共享链接...", + "term.song.link.generate": "获取 song.link 共享链接……", "term.musicVideos": "音乐视频", "term.stations": "电台", "term.curators": "策展人", @@ -199,7 +199,7 @@ "term.fullscreen": "全屏模式", "term.nowPlaying": "正在播放", "home.syncFavorites": "同步喜爱艺人", - "home.syncFavorites.gettingArtists": "获取喜爱艺人...", + "home.syncFavorites.gettingArtists": "获取喜爱艺人……", "action.favorite": "喜爱", "action.removeFavorite": "取消喜爱", "action.refresh": "刷新", @@ -212,7 +212,7 @@ "home.friendsListeningTo": "朋友正在听", "home.followedArtists": "关注的艺人", "error.appleMusicSubRequired": "需要订阅 Apple Music 以使用 Cider", - "error.connectionError": "无法连接到 Apple Music。", + "error.connectionError": "无法连接至 Apple Music。", "error.noResults": "没有结果。", "error.noResults.description": "尝试更改搜索条件。", "podcast.followOnCider": "在 Cider 中关注", @@ -281,7 +281,7 @@ "action.update": "更新", "action.install": "安装", "action.copy": "拷贝", - "action.newpreset": "新建预设...", + "action.newpreset": "新建预设……", "action.deletepreset": "删除预设", "action.open": "打开", "action.close": "关闭", @@ -292,8 +292,8 @@ "action.cast.airplay": "隔空播放", "action.cast.airplay.underdevelopment": "隔空播放仍处于开发阶段中,敬请期待。", "action.cast.scan": "搜索", - "action.cast.scanning": "搜索中...", - "action.createNew": "添加...", + "action.cast.scanning": "搜索中……", + "action.createNew": "添加……", "action.openArtworkInBrowser": "在浏览器中打开专辑封面", "action.scrollToTop": "回到顶部", "menubar.options.view": "查看", @@ -309,12 +309,12 @@ "menubar.options.account": "账户", "menubar.options.signout": "注销", "menubar.options.support": "支持", - "menubar.options.report": "报告...", + "menubar.options.report": "报告……", "menubar.options.bug": "Bug", "menubar.options.feature": "功能请求", "menubar.options.trans": "翻译报告/请求", "menubar.options.license": "查看授权", - "menubar.options.conf": "在编辑器打开配置文件", + "menubar.options.conf": "在编辑器中打开配置文件", "menubar.options.zoom": "缩放", "settings.header.general": "通用", "settings.header.general.description": "调整 Cider 的通用设置", @@ -325,14 +325,14 @@ "settings.option.general.resumebehavior.locally.description": "Cider 将还原您在这台电脑上的最后一次操作。", "settings.option.general.resumebehavior.history": "历史", "settings.option.general.resumebehavior.history.description": "Cider 将跨设备将您的整个 Apple Music 历史记录中的最后一首歌曲排队入列。", - "settings.option.general.resumetabs": "启动时打开的选项页面", - "settings.option.general.resumetabs.description": "您可以选择启动 Cider 时要默认打开的页面。", + "settings.option.general.resumetabs": "启动时打开的页面", + "settings.option.general.resumetabs.description": "您可以选择启动 Cider 时默认打开的页面。", "settings.option.general.resumetabs.dynamic": "动态", "settings.option.general.resumetabs.dynamic.description": "Cider 将自动打开您上次停留的页面。", "settings.option.general.language.main": "语言", "settings.option.general.language.fun": "恶搞语言", "settings.option.general.language.unsorted": "未分类", - "settings.option.general.customizeSidebar": "自定义侧边栏的功能", + "settings.option.general.customizeSidebar": "自定义侧边栏项目", "settings.option.general.customizeSidebar.customize": "自定义", "settings.option.general.keybindings": "快捷操作键", "settings.option.general.keybindings.library": "资料库", @@ -356,7 +356,7 @@ "settings.description.remote": "远程控制", "settings.description.audio": "音频设定", "settings.description.plugins": "插件目录", - "settings.description.cast": "投射到装置", + "settings.description.cast": "投射到设备", "settings.description.settings": "设置", "settings.description.developer": "开发者工具", "settings.description.listnow": "现在就听", @@ -384,7 +384,7 @@ "settings.header.audio.quality.standard.description": "64 kbps", "settings.option.audio.seamlessTransition": "无缝播放", "settings.option.audio.enableAdvancedFunctionality": "启用高级音频功能", - "settings.option.audio.enableAdvancedFunctionality.description": "打开 AudioContext 将启用类似音量平衡和等化器的高级设置。但这并不一定适合每部电脑,可能会发生音乐卡顿。", + "settings.option.audio.enableAdvancedFunctionality.description": "打开高级音频功能将启用类似音量平衡和等化器的高级设置。但这并不一定适合每部电脑,可能会发生音乐卡顿。", "settings.warn.audio.enableAdvancedFunctionality.lowcores": "您的电脑可能无法处理这些功能, 您确定要继续?", "settings.option.audio.audioLab": "Cider 音频实验室", "settings.option.audio.audioLab.description": "包含由 Cider 开发团队进行的各种音频优化功能。", @@ -434,11 +434,11 @@ "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.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.bplk": "安可舞台", + "settings.option.audio.enableAdvancedFunctionality.tunedAudioSpatialization.profile.hw2k": "大型安可舞台", "settings.warn.audio.enableAdvancedFunctionality.audioSpatialization.compatibility": "空间音频并不兼容数码增强音频处理器。请关闭数码增强音频处理器后再继续进行操作。", - "settings.option.audio.dbspl.display": "显示 dB SPL(声压)", - "settings.option.audio.dbspl.description": "(专业用户选项) 音量滑动条显示 dB SPL 而非 dBFS。", + "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": "外观", @@ -504,7 +504,7 @@ "settings.header.lyrics": "歌词", "settings.header.lyrics.description": "调整 Cider 的歌词设置", "settings.option.lyrics.enableMusixmatch": "启用 Musixmatch 歌词", - "settings.option.lyrics.enableMusixmatchKaraoke": "启用卡拉 OK 模式(仅 Musixmatch)", + "settings.option.lyrics.enableMusixmatchKaraoke": "启用卡拉OK模式(仅限 Musixmatch)", "settings.option.lyrics.musixmatchPreferredLanguage": "Musixmatch 歌词语言偏好", "settings.option.lyrics.enableYoutubeLyrics": "播放 MV 时使用 YouTube 歌词", "settings.option.lyrics.enableQQLyrics": "启用 QQ 音乐的歌词", @@ -514,23 +514,30 @@ "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.showActivityButtons": "显示动态按钮", + "settings.option.connectivity.discordRPC.firstButton": "第一动态按钮", + "settings.option.connectivity.discordRPC.secondButton": "第二动态按钮", + "settings.option.connectivity.discordRPC.buttons.listenOnCider": "在 Cider 中聆听", + "settings.option.connectivity.discordRPC.buttons.viewOnAppleMusic": "在 Apple Music 中查看", + "settings.option.connectivity.discordRPC.buttons.viewOnOtherMusicServices": "在其他音乐服务中查看", + "settings.option.connectivity.discordRPC.showSongLink": "在 Discord 动态中显示 Song.link 按钮而非 Apple Music 按钮", "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.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.discordRPC.reload": "重新加载 Discord 动态", + "settings.option.connectivity.discordRPC.reconnectedToUser": "Discord 动态已重新连接至用户:{{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.option.connectivity.lastfmScrobble.manualToken.link": "点击此处以获取 Last.fm 验证码", "settings.notyf.connectivity.lastfmScrobble.connectError": "Last.fm 连接超时", "settings.notyf.connectivity.lastfmScrobble.connectSuccess": "Last.fm 连接成功", - "settings.notyf.connectivity.lastfmScrobble.connecting": "正在连接至 Last.fm...", + "settings.notyf.connectivity.lastfmScrobble.connecting": "正在连接至 Last.fm……", "settings.header.debug": "调试", "settings.option.debug.copy_log": "拷贝日志至剪贴板", "settings.option.debug.openAppData": "打开 Cider 程序文件夹", diff --git a/src/main/base/app.ts b/src/main/base/app.ts index da4834d3..f59c6788 100644 --- a/src/main/base/app.ts +++ b/src/main/base/app.ts @@ -78,11 +78,15 @@ export class AppEvents { case "webgpu": console.info("WebGPU is enabled."); app.commandLine.appendSwitch("enable-unsafe-webgpu"); + if (process.platform === "linux") { + app.commandLine.appendSwitch("enable-features", "Vulkan"); + } break; case "disabled": console.info("Hardware acceleration is disabled."); app.commandLine.appendSwitch("disable-gpu"); + app.disableHardwareAcceleration(); break; } @@ -232,7 +236,7 @@ export class AppEvents { startArgs.forEach((arg) => { console.log(arg); - if (arg.includes("cider://")) { + if (arg.includes("cider://") || arg.includes("itms://") || arg.includes("itmss://") || arg.includes("music://") || arg.includes("musics://")) { console.debug("[InstanceHandler] (second-instance) Link detected with " + arg); this.LinkHandler(arg); } else if (arg.includes("--force-quit")) { @@ -328,10 +332,10 @@ export class AppEvents { { visible: visible, - label: 'track info', - enabled: false, + label: 'track info', + enabled: false, }, - + {type: 'separator'}, */ diff --git a/src/main/base/browserwindow.ts b/src/main/base/browserwindow.ts index 52d32099..c044894a 100644 --- a/src/main/base/browserwindow.ts +++ b/src/main/base/browserwindow.ts @@ -1,5 +1,5 @@ import { join } from "path"; -import { app, BrowserWindow as bw, ipcMain, ShareMenu, shell, screen, dialog } from "electron"; +import { app, BrowserWindow as bw, ipcMain, ShareMenu, shell, screen, dialog, nativeTheme } from "electron"; import * as windowStateKeeper from "electron-window-state"; import * as express from "express"; import * as getPort from "get-port"; @@ -90,6 +90,7 @@ export class BrowserWindow { "components/equalizer", "components/add-to-playlist", "components/queue", + "components/smarthints", "components/mediaitem-scroller-horizontal", "components/mediaitem-scroller-horizontal-large", "components/mediaitem-scroller-horizontal-sp", @@ -441,6 +442,8 @@ export class BrowserWindow { break; } + nativeTheme.themeSource = utils.getStoreValue("visual.overrideDisplayTheme"); + // Start the webserver for the browser window to load // LocalFiles.DB.init() this.startWebServer(); @@ -476,7 +479,7 @@ export class BrowserWindow { } } for (let i = 0; i < expectedFiles.length; i++) { - const file = join(utils.getPath("ciderCache"), expectedFiles[i]); + const file = join(join(app.getPath("userData"), "CiderCache"), expectedFiles[i]); if (!existsSync(file)) { writeFileSync(file, JSON.stringify([])); } @@ -555,15 +558,11 @@ export class BrowserWindow { res.send("Stopped"); break; case "next": - BrowserWindow.win.webContents.executeJavaScript( - "if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex);}" - ); + BrowserWindow.win.webContents.executeJavaScript("if (MusicKit.getInstance().queue.nextPlayableItemIndex != -1 && MusicKit.getInstance().queue.nextPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.nextPlayableItemIndex);}"); res.send("Next"); break; case "previous": - BrowserWindow.win.webContents.executeJavaScript( - "if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex);}" - ); + BrowserWindow.win.webContents.executeJavaScript("if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex);}"); res.send("Previous"); break; default: { @@ -619,6 +618,8 @@ export class BrowserWindow { const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; if (!this.chromecastIP.includes(ip)) { this.headerSent = false; + this.audioStream._readableState.buffer.clear(); + this.audioStream._readableState.length = 0; this.chromecastIP.push(ip); } req.socket.setTimeout(Number.MAX_SAFE_INTEGER); @@ -730,18 +731,11 @@ export class BrowserWindow { details.requestHeaders["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Cider/1.0.0 Chrome/96.0.4664.45 Electron/16.0.0 Safari/537.36"; } if (details.url.includes("https://qq.com")) { - (details.requestHeaders["Accept"] = "*/*"), - (details.requestHeaders["Accept-Encoding"] = "gzip, deflate, br"), - (details.requestHeaders["Accept-Language"] = "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"), - (details.requestHeaders["Referer"] = "https://y.qq.com/"), - (details.requestHeaders["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 ("); + (details.requestHeaders["Accept"] = "*/*"), (details.requestHeaders["Accept-Encoding"] = "gzip, deflate, br"), (details.requestHeaders["Accept-Language"] = "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"), (details.requestHeaders["Referer"] = "https://y.qq.com/"), (details.requestHeaders["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 ("); ("KHTML, like Gecko) Mobile/17D50 UCBrowser/12.8.2.1268 Mobile AliApp(TUnionSDK/0.1.20.3) "); } if (details.url.includes("https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg")) { - (details.requestHeaders["Accept"] = "*/*"), - (details.requestHeaders["Accept-Encoding"] = "gzip, deflate, br"), - (details.requestHeaders["Accept-Language"] = "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"), - (details.requestHeaders["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 ("); + (details.requestHeaders["Accept"] = "*/*"), (details.requestHeaders["Accept-Encoding"] = "gzip, deflate, br"), (details.requestHeaders["Accept-Language"] = "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"), (details.requestHeaders["User-Agent"] = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X; zh-CN) AppleWebKit/537.51.1 ("); ("KHTML, like Gecko) Mobile/17D50 UCBrowser/12.8.2.1268 Mobile AliApp(TUnionSDK/0.1.20.3) "); details.requestHeaders["Referer"] = "https://y.qq.com/portal/player.html"; } @@ -1073,13 +1067,13 @@ export class BrowserWindow { }); ipcMain.handle("put-cache", (_event, arg) => { - writeFileSync(join(utils.getPath("ciderCache"), `${arg.file}.json`), arg.data); + writeFileSync(join(join(app.getPath("userData"), "CiderCache"), `${arg.file}.json`), arg.data); }); ipcMain.on("get-cache", (event, arg) => { let read = ""; - if (existsSync(join(utils.getPath("ciderCache"), `${arg}.json`))) { - read = readFileSync(join(utils.getPath("ciderCache"), `${arg}.json`), "utf8"); + if (existsSync(join(join(app.getPath("userData"), "CiderCache"), `${arg}.json`))) { + read = readFileSync(join(join(app.getPath("userData"), "CiderCache"), `${arg}.json`), "utf8"); } event.returnValue = read; }); @@ -1135,6 +1129,11 @@ export class BrowserWindow { BrowserWindow.win.setBounds({ x, y }); }); + // Override light, dark + ipcMain.on("changeDisplayTheme", (event, theme) => { + nativeTheme.themeSource = theme; + }); + //Fullscreen ipcMain.on("setFullScreen", (_event, flag) => { BrowserWindow.win.setFullScreen(flag); @@ -1351,7 +1350,7 @@ export class BrowserWindow { .then(async (buffer) => { const metadata = await mm.parseBuffer(buffer, "audio/x-m4a"); let SoundCheckTag = metadata.native.iTunes[1].value; - console.log("sc", SoundCheckTag); + console.debug("sc", SoundCheckTag); BrowserWindow.win.webContents.send("SoundCheckTag", SoundCheckTag); }) .catch((err) => { @@ -1476,7 +1475,7 @@ export class BrowserWindow { // Set window Handler BrowserWindow.win.webContents.setWindowOpenHandler((x: any) => { - if (x.url.includes("apple") || x.url.includes("localhost")) { + if (x.url.includes("apple.com") || x.url.includes("localhost")) { return { action: "allow" }; } shell.openExternal(x.url).catch(console.error); @@ -1498,11 +1497,7 @@ export class BrowserWindow { if (details.family === "IPv4" && !details.internal) { if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ":" + alias : ""))) { if (details.address.substring(0, 8) === "192.168." || details.address.substring(0, 7) === "172.16." || details.address.substring(0, 3) === "10.") { - if ( - !ip.startsWith("192.168.") || - (String(ip2).startsWith("192.168.") && !ip.startsWith("192.168.") && String(ip2).startsWith("172.16.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.")) || - (String(ip2).startsWith("10.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.") && !ip.startsWith("10.")) - ) { + if (!ip.startsWith("192.168.") || (String(ip2).startsWith("192.168.") && !ip.startsWith("192.168.") && String(ip2).startsWith("172.16.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.")) || (String(ip2).startsWith("10.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.") && !ip.startsWith("10."))) { ip = details.address; } ++alias; diff --git a/src/main/base/plugins.ts b/src/main/base/plugins.ts index e880171a..aa90f4b9 100644 --- a/src/main/base/plugins.ts +++ b/src/main/base/plugins.ts @@ -88,6 +88,7 @@ export class Plugins { win: utils.getWindow(), dir: pluginPath, dirName: file, + express: utils.getExpress(), }; plugins[plugin.name] = new plugin(pluginEnv); } diff --git a/src/main/base/store.ts b/src/main/base/store.ts index 223c3e37..a2a98363 100644 --- a/src/main/base/store.ts +++ b/src/main/base/store.ts @@ -72,11 +72,18 @@ export class Store { discord_rpc: { enabled: true, client: "Cider", + activity: { + state_format: "by {artist}", + details_format: "{title}", + hide_timestamp: false, + buttons: { + enabled: true, + first: "listenOnCider", + second: "viewOnAppleMusic", + options: ["listenOnCider", "viewOnAppleMusic", "viewOnOtherMusicServices"], + }, + }, clear_on_pause: true, - hide_buttons: false, - hide_timestamp: false, - state_format: "by {artist}", - details_format: "{title}", }, lastfm: { enabled: false, @@ -200,6 +207,7 @@ export class Store { accentColor: "#fc3c44", purplePodcastPlaybackBar: false, maxElementScale: -1, // -1 default, anything else is a custom scale + overrideDisplayTheme: "system", // system , dark, light }, lyrics: { enable_mxm: true, @@ -232,7 +240,6 @@ export class Store { }; constructor() { - this.defaults.general.language = this.checkLocale(app.getLocale().replace("-", "_")) ?? "en_US"; Store.cfg = new ElectronStore({ name: "cider-config", defaults: this.defaults, diff --git a/src/main/base/vcomponents.json b/src/main/base/vcomponents.json index 65d7067a..2c694941 100644 --- a/src/main/base/vcomponents.json +++ b/src/main/base/vcomponents.json @@ -42,6 +42,7 @@ "components/moreinfo-modal", "components/equalizer", "components/add-to-playlist", + "components/smarthints", "components/queue", "components/mediaitem-scroller-horizontal", "components/mediaitem-scroller-horizontal-large", diff --git a/src/main/base/wsapi.ts b/src/main/base/wsapi.ts index 7fc25619..d161b279 100644 --- a/src/main/base/wsapi.ts +++ b/src/main/base/wsapi.ts @@ -208,9 +208,7 @@ export class wsapi { response.message = "Next"; break; case "previous": - this._win.webContents.executeJavaScript( - `if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex)}` - ); + this._win.webContents.executeJavaScript(`if (MusicKit.getInstance().queue.previousPlayableItemIndex != -1 && MusicKit.getInstance().queue.previousPlayableItemIndex != null) {MusicKit.getInstance().changeToMediaAtIndex(MusicKit.getInstance().queue.previousPlayableItemIndex)}`); response.message = "Previous"; break; case "musickit-api": diff --git a/src/main/index.ts b/src/main/index.ts index 8a4dc148..abae1125 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,18 +1,17 @@ require("v8-compile-cache"); +import { app, components, ipcMain } from "electron"; import { join } from "path"; -import { app } from "electron"; -if (!app.isPackaged) { - app.setPath("userData", join(app.getPath("appData"), "Cider")); -} - import { Store } from "./base/store"; import { AppEvents } from "./base/app"; import { Plugins } from "./base/plugins"; import { BrowserWindow } from "./base/browserwindow"; import { init as Sentry } from "@sentry/electron"; import { RewriteFrames } from "@sentry/integrations"; -import { components, ipcMain } from "electron"; + +if (!app.isPackaged) { + app.setPath("userData", join(app.getPath("appData"), "Cider")); +} // Analytics for debugging fun yeah. Sentry({ @@ -54,7 +53,9 @@ app.on("ready", () => { win.on("ready-to-show", () => { console.debug("[Cider] Window is Ready."); CiderPlug.callPlugins("onReady", win); - win.show(); + if (!app.commandLine.hasSwitch("hidden")) { + win.show(); + } }); }); }); @@ -62,9 +63,11 @@ app.on("ready", () => { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Renderer Event Handlers * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - +let rendererInitialized = false; ipcMain.handle("renderer-ready", (event) => { + if (rendererInitialized) return; CiderPlug.callPlugins("onRendererReady", event); + rendererInitialized = true; }); ipcMain.on("playbackStateDidChange", (_event, attributes) => { @@ -75,6 +78,10 @@ ipcMain.on("nowPlayingItemDidChange", (_event, attributes) => { CiderPlug.callPlugins("onNowPlayingItemDidChange", attributes); }); +ipcMain.on("playbackTimeDidChange", (_event, attributes) => { + CiderPlug.callPlugins("playbackTimeDidChange", attributes); +}); + app.on("before-quit", () => { CiderPlug.callPlugins("onBeforeQuit"); console.warn(`${app.getName()} exited.`); diff --git a/src/main/plugins/chromecast.ts b/src/main/plugins/chromecast.ts index 4ef8c02c..84dc9321 100644 --- a/src/main/plugins/chromecast.ts +++ b/src/main/plugins/chromecast.ts @@ -2,6 +2,7 @@ import * as electron from "electron"; import * as os from "os"; import { resolve } from "path"; import * as CiderReceiver from "../base/castreceiver"; +const MediaRendererClient = require("upnp-mediarenderer-client"); export default class ChromecastPlugin { /** @@ -28,6 +29,7 @@ export default class ChromecastPlugin { private connectedHosts: any = {}; private connectedPlayer: any; private ciderPort: any = 9000; + private scanCount: any = 0; // private server = false; // private bufcount = 0; // private bufcount2 = 0; @@ -67,19 +69,15 @@ export default class ChromecastPlugin { ssdpBrowser.search("urn:dial-multiscreen-org:device:dial:1"); - // // actual upnp devices - // if (app.cfg.get("audio.enableDLNA")) { - // let ssdpBrowser2 = new Client(); - // ssdpBrowser2.on('response', (headers, statusCode, rinfo) => { - // var location = getLocation(headers); - // if (location != null) { - // this.getServiceDescription(location, rinfo.address); - // } - - // }); - // ssdpBrowser2.search('urn:schemas-upnp-org:device:MediaRenderer:1'); - - // } + // actual upnp devices + let ssdpBrowser2 = new Client(); + ssdpBrowser2.on("response", (headers: any, statusCode: any, rinfo: any) => { + var location = getLocation(headers); + if (location != null) { + this.getServiceDescription(location, rinfo.address); + } + }); + ssdpBrowser2.search("urn:schemas-upnp-org:device:MediaRenderer:1"); } catch (e) { console.log("Search GC err", e); } @@ -202,11 +200,7 @@ export default class ChromecastPlugin { if (details.family === "IPv4" && !details.internal) { if (!/(loopback|vmware|internal|hamachi|vboxnet|virtualbox)/gi.test(dev + (alias ? ":" + alias : ""))) { if (details.address.substring(0, 8) === "192.168." || details.address.substring(0, 7) === "172.16." || details.address.substring(0, 3) === "10.") { - if ( - !ip.startsWith("192.168.") || - (ip2.startsWith("192.168.") && !ip.startsWith("192.168.") && ip2.startsWith("172.16.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.")) || - (ip2.startsWith("10.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.") && !ip.startsWith("10.")) - ) { + if (!ip.startsWith("192.168.") || (ip2.startsWith("192.168.") && !ip.startsWith("192.168.") && ip2.startsWith("172.16.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.")) || (ip2.startsWith("10.") && !ip.startsWith("192.168.") && !ip.startsWith("172.16.") && !ip.startsWith("10."))) { ip = details.address; } ++alias; @@ -257,26 +251,29 @@ export default class ChromecastPlugin { }); } else { // upnp devices - //try { - // client = new MediaRendererClient(UPNPDesc); - // const options = { - // autoplay: true, - // contentType: 'audio/x-wav', - // dlnaFeatures: 'DLNA.ORG_PN=-;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000', - // metadata: { - // title: 'Apple Music Electron', - // creator: 'Streaming ...', - // type: 'audio', // can be 'video', 'audio' or 'image' - // // url: 'http://' + getIp() + ':' + server.address().port + '/', - // // protocolInfo: 'DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000; - // } - // }; - // client.load('http://' + getIp() + ':' + server.address().port + '/a.wav', options, function (err, _result) { - // if (err) throw err; - // console.log('playing ...'); - // }); - // } catch (e) { - // } + try { + let client = new MediaRendererClient(UPNPDesc); + const options = { + autoplay: true, + contentType: "audio/x-wav", + dlnaFeatures: "DLNA.ORG_PN=-;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000", + metadata: { + title: "Cider", + creator: "Streaming ...", + type: "audio", // can be 'video', 'audio' or 'image' + // url: 'http://' + getIp() + ':' + server.address().port + '/', + // protocolInfo: 'DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000; + }, + }; + client.load("http://" + this.getIp() + ":" + this.ciderPort + "/audio.wav", options, function (err: any, _result: any) { + if (err) throw err; + console.log("playing ..."); + }); + if (!this.connectedHosts[device.host]) { + this.connectedHosts[device.host] = client; + this.activeConnections.push(client); + } + } catch (e) {} } } @@ -318,6 +315,10 @@ export default class ChromecastPlugin { }); electron.ipcMain.on("getChromeCastDevices", (_event, _data) => { + if (this.scanCount++ == 2) { + this.scanCount = 0; + this.castDevices = []; + } this.searchForGCDevices(); }); diff --git a/src/main/plugins/discordrpc.ts b/src/main/plugins/discordrpc.ts index c35947ef..9d158763 100644 --- a/src/main/plugins/discordrpc.ts +++ b/src/main/plugins/discordrpc.ts @@ -48,15 +48,21 @@ export default class DiscordRPC { * Runs on app ready */ onReady(_win: any): void { - const self = this; this.connect(); console.debug(`[Plugin][${this.name}] Ready.`); - ipcMain.on("updateRPCImage", async (_event, imageurl) => { + } + + /** + * Set up ipc listeners for the plugin + */ + onRendererReady() { + const self = this; + ipcMain.on("discordrpc:updateImage", async (_event, imageurl) => { if (!this._utils.getStoreValue("general.privateEnabled")) { let b64data = ""; let postbody = ""; if (imageurl.startsWith("/ciderlocalart")) { - let port = await _win.webContents.executeJavaScript(`app.clientPort`); + let port = await this._utils.getWindow().webContents.executeJavaScript(`app.clientPort`); console.log("http://localhost:" + port + imageurl); const response = await fetch("http://localhost:" + port + imageurl); b64data = (await response.buffer()).toString("base64"); @@ -66,7 +72,7 @@ export default class DiscordRPC { body: postbody, headers: { "Content-Type": "application/json", - "User-Agent": _win.webContents.getUserAgent(), + "User-Agent": this._utils.getWindow().webContents.getUserAgent(), }, }) .then((res) => res.json()) @@ -81,7 +87,7 @@ export default class DiscordRPC { body: postbody, headers: { "Content-Type": "application/json", - "User-Agent": _win.webContents.getUserAgent(), + "User-Agent": this._utils.getWindow().webContents.getUserAgent(), }, }) .then((res) => res.json()) @@ -92,17 +98,22 @@ export default class DiscordRPC { } } }); - ipcMain.on("reloadRPC", () => { + ipcMain.on("discordrpc:reload", (_event, configUpdate = null) => { console.log(`[DiscordRPC][reload] Reloading DiscordRPC.`); - this._client.destroy(); + if (this._client) { + this._client.destroy(); + } + + if (!this._utils.getStoreValue("connectivity.discord_rpc.enabled")) return; this._client .endlessLogin({ clientId: this._utils.getStoreValue("connectivity.discord_rpc.client") === "Cider" ? "911790844204437504" : "886578863147192350", }) .then(() => { + console.log(`[DiscordRPC][reload] DiscordRPC Reloaded.`); this.ready = true; - this._utils.getWindow().webContents.send("rpcReloaded", this._client.user); + if (configUpdate == null) this._utils.getWindow().webContents.send("rpcReloaded", this._client.user); if (this._activityCache && this._activityCache.details && this._activityCache.state) { console.info(`[DiscordRPC][reload] Restoring activity cache.`); this._client.setActivity(this._activityCache); @@ -111,6 +122,13 @@ export default class DiscordRPC { .catch((e: any) => console.error(`[DiscordRPC][reload] ${e}`)); // this.connect(true) }); + ipcMain.on("onPrivacyModeChange", (_event, enabled) => { + if (enabled && this._client) { + this._client.clearActivity(); + } else if (!enabled && this._activityCache && this._activityCache.details && this._activityCache.state) { + this._client.setActivity(this._activityCache); + } + }); } /** @@ -133,7 +151,7 @@ export default class DiscordRPC { * Runs on song change * @param attributes Music Attributes */ - onNowPlayingItemDidChange(attributes: object): void { + playbackTimeDidChange(attributes: object): void { this._attributes = attributes; this.setActivity(attributes); } @@ -158,7 +176,7 @@ export default class DiscordRPC { this._client.once("ready", () => { console.info(`[DiscordRPC][connect] Successfully Connected to Discord. Authed for user: ${this._client.user.id}.`); - if (this._activityCache && this._activityCache.details && this._activityCache.state) { + if (this._activityCache && this._activityCache.details && this._activityCache.state && !this._utils.getStoreValue("general.privateEnabled")) { console.info(`[DiscordRPC][connect] Restoring activity cache.`); this._client.setActivity(this._activityCache); } @@ -180,14 +198,14 @@ export default class DiscordRPC { * @param attributes Music Attributes */ private setActivity(attributes: any) { - if (!this._client) { + if (!this._client || !attributes) { return; } // Check if show buttons is (true) or (false) let activity: Object = { - details: this._utils.getStoreValue("connectivity.discord_rpc.details_format"), - state: this._utils.getStoreValue("connectivity.discord_rpc.state_format"), + details: this._utils.getStoreValue("connectivity.discord_rpc.activity.details_format"), + state: this._utils.getStoreValue("connectivity.discord_rpc.activity.state_format"), largeImageKey: attributes?.artwork?.url?.replace("{w}", "1024").replace("{h}", "1024"), largeImageText: attributes.albumName, instance: false, // Whether the activity is in a game session @@ -201,10 +219,14 @@ export default class DiscordRPC { return; } - // Set the activity + if (!activity) { + return; + } + if (!attributes.status && this._utils.getStoreValue("connectivity.discord_rpc.clear_on_pause")) { this._client.clearActivity(); } else if (activity && this._activityCache !== activity) { + if (this._utils.getStoreValue("general.privateEnabled")) return; this._client.setActivity(activity); } this._activityCache = activity; @@ -214,16 +236,29 @@ export default class DiscordRPC { * Filter the Discord activity object */ private filterActivity(activity: any, attributes: any): Object { - // Add the buttons if people want them - if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_buttons")) { - activity.buttons = [ - { label: "Listen on Cider", url: attributes.url.cider }, - { label: "View on Apple Music", url: attributes.url.appleMusic }, - ]; //To change attributes.url => preload/cider-preload.js + // Handling Activity Buttons + if (this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.enabled") && this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.first") != "disabled") { + const activityUrls: { [key: string]: any } = { + listenOnCider: "cider", + viewOnAppleMusic: "appleMusic", + viewOnOtherMusicServices: "songLink", + }; + + const firstActivity = this._utils.getLocale(this._utils.getStoreValue("general.language"), `settings.option.connectivity.discordRPC.buttons.${this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.first")}`); + const secondActivity = this._utils.getLocale(this._utils.getStoreValue("general.language"), `settings.option.connectivity.discordRPC.buttons.${this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.second")}`); + + if (this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.second") != "disabled") { + activity.buttons = [ + { label: firstActivity, url: attributes.url[activityUrls[this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.first")]] }, + { label: secondActivity, url: attributes.url[activityUrls[this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.second")]] }, + ]; + } else { + activity.buttons = [{ label: firstActivity, url: attributes.url[activityUrls[this._utils.getStoreValue("connectivity.discord_rpc.activity.buttons.first")]] }]; + } } // Add the timestamp if its playing and people want them - if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_timestamp") && attributes.status) { + if (!this._utils.getStoreValue("connectivity.discord_rpc.hide_timestamp") && attributes.status && new Date(attributes.endTime).getTime() > 0 && isFinite(attributes.endTime) && isFinite(attributes.startTime)) { activity.startTimestamp = Date.now() - (attributes?.durationInMillis - attributes?.remainingTime); activity.endTimestamp = attributes.endTime; } @@ -280,12 +315,6 @@ export default class DiscordRPC { activity.largeImageKey = "cider"; } - // Timestamp - if (new Date(attributes.endTime).getTime() < 0) { - delete activity.startTime; - delete activity.endTime; - } - // not sure if (!attributes.artistName) { delete activity.state; @@ -294,6 +323,10 @@ export default class DiscordRPC { if (!activity.largeImageText || activity.largeImageText.length < 2) { delete activity.largeImageText; } + + if (activity.status === "" || activity.details === "") { + return false; + } return activity; } } diff --git a/src/main/plugins/lastfm.ts b/src/main/plugins/lastfm.ts index be8380d5..bfceeea3 100644 --- a/src/main/plugins/lastfm.ts +++ b/src/main/plugins/lastfm.ts @@ -29,7 +29,9 @@ export default class lastfm { onReady(_win: Electron.BrowserWindow): void { this.initializeLastFM("", this._apiCredentials); + } + onRendererReady(): void { // Register the ipcMain handlers this._utils.getIPCMain().handle("lastfm:url", (event: any) => { console.debug(`[${lastfm.name}:url] Called.`); @@ -48,32 +50,21 @@ export default class lastfm { }); this._utils.getIPCMain().on("lastfm:nowPlayingChange", (event: any, attributes: any) => { - if (this._utils.getStoreValue("connectivity.lastfm.filter_loop") || this._utils.getStoreValue("general.privateEnabled")) return; + if (this._utils.getStoreValue("connectivity.lastfm.filter_loop") || this._utils.getStoreValue("general.privateEnabled") || attributes.type === "radioStation") return; + this.updateNowPlayingTrack(attributes); + }); + + this._utils.getIPCMain().on("lastfm:FilteredNowPlayingItemDidChange", (event: any, attributes: any) => { + if (this._utils.getStoreValue("general.privateEnabled") || attributes.type === "radioStation") return; this.updateNowPlayingTrack(attributes); }); this._utils.getIPCMain().on("lastfm:scrobbleTrack", (event: any, attributes: any) => { - if (this._utils.getStoreValue("general.privateEnabled")) return; + if (this._utils.getStoreValue("general.privateEnabled") || attributes.type === "radioStation") return; this.scrobbleTrack(attributes); }); } - /** - * Runs on playback State Change - * @param attributes Music Attributes (attributes.status = current state) - */ - onPlaybackStateDidChange(attributes: object): void {} - - /** - * Runs on song change - * @param attributes Music Attributes - * @param scrobble - */ - onNowPlayingItemDidChange(attributes: any, scrobble = false): void { - if (this._utils.getStoreValue("general.privateEnabled")) return; - this.updateNowPlayingTrack(attributes); - } - /** * Initialize LastFM * @param token @@ -179,13 +170,7 @@ export default class lastfm { return; } - 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._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.type] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._scrobbleCache.track === attributes.lfmTrack.name)) return; // Scrobble const scrobble = { @@ -225,13 +210,9 @@ export default class lastfm { 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; + if (!this._authenticated || !attributes || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.playParams.kind] || this._utils.getStoreValue("connectivity.lastfm.filter_types")[attributes.type] || (this._utils.getStoreValue("connectivity.lastfm.filter_loop") && this._nowPlayingCache.track === attributes.lfmTrack.name)) return; + + console.log(this._utils.getStoreValue("connectivity.lastfm.filter_types")); const nowPlaying = { artist: attributes.lfmTrack.artist.name, diff --git a/src/main/plugins/menubar.ts b/src/main/plugins/menubar.ts index 261605e4..dc652dec 100644 --- a/src/main/plugins/menubar.ts +++ b/src/main/plugins/menubar.ts @@ -53,20 +53,7 @@ export default class Thumbar { { label: utils.getLocale(utils.getStoreValue("general.language"), "menubar.options.view"), submenu: [ - ...(this.isMac - ? [ - { role: "reload" }, - { role: "forceReload" }, - { role: "toggleDevTools" }, - { type: "separator" }, - { role: "resetZoom" }, - { role: "zoomIn" }, - { role: "zoomOut" }, - { type: "separator" }, - { role: "togglefullscreen" }, - { type: "separator" }, - ] - : []), + ...(this.isMac ? [{ role: "reload" }, { role: "forceReload" }, { role: "toggleDevTools" }, { type: "separator" }, { role: "resetZoom" }, { role: "zoomIn" }, { role: "zoomOut" }, { type: "separator" }, { role: "togglefullscreen" }, { type: "separator" }] : []), { label: utils.getLocale(utils.getStoreValue("general.language"), "term.search"), accelerator: utils.getStoreValue("general.keybindings.search").join("+"), diff --git a/src/main/plugins/mpris.ts b/src/main/plugins/mpris.ts index d65198ae..15e08cdf 100644 --- a/src/main/plugins/mpris.ts +++ b/src/main/plugins/mpris.ts @@ -27,7 +27,6 @@ export default class mpris { */ constructor(utils: any) { mpris.utils = utils; - console.debug(`[Plugin][${mpris.name}] Loading Complete.`); } @@ -103,7 +102,7 @@ export default class mpris { */ private static updateMetaData(attributes: any) { mpris.player.metadata = { - "mpris:trackid": mpris.player.objectPath(`track/${attributes.playParams.id.replace(/[.]+/g, "")}`), + "mpris:trackid": mpris.player.objectPath(`track/${attributes.playParams.id.replace(/[^a-zA-Z 0-9]+/g, "")}`), "mpris:length": attributes.durationInMillis * 1000, // In microseconds "mpris:artUrl": attributes.artwork.url.replace("/{w}x{h}bb", "/512x512bb").replace("/2000x2000bb", "/35x35bb"), "xesam:title": `${attributes.name}`, @@ -170,7 +169,7 @@ export default class mpris { * @param attributes Music Attributes */ @mpris.linuxOnly - onNowPlayingItemDidChange(attributes: object): void { + playbackTimeDidChange(attributes: object): void { mpris.updateMetaData(attributes); } } diff --git a/src/main/plugins/raop.ts b/src/main/plugins/raop.ts index 95c17195..9140ce8e 100644 --- a/src/main/plugins/raop.ts +++ b/src/main/plugins/raop.ts @@ -23,7 +23,7 @@ export default class RAOP { private portairplay: any = ""; private airtunes: any; - private device: any; + // private device: any; private mdns = require("mdns-js"); private ok: any = 1; private devices: any = []; @@ -38,7 +38,7 @@ export default class RAOP { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads'); function getAudioConv (buffers) { - + function interleave16(leftChannel, rightChannel) { var length = leftChannel.length + rightChannel.length; var result = new Int16Array(length); @@ -89,7 +89,7 @@ export default class RAOP { private ondeviceup(name: any, host: any, port: any, addresses: any, text: any, airplay2: any = null) { // console.log(this.castDevices.findIndex((item: any) => {return (item.name == host.replace(".local","") && item.port == port )})) - let shown_name = (host ?? "Unknown").replace(".local", ""); + let shown_name = name; try { let model = text.filter((u: any) => String(u).startsWith("model=")); let manufacturer = text.filter((u: any) => String(u).startsWith("manufacturer=")); @@ -105,7 +105,7 @@ export default class RAOP { if ( this.castDevices.findIndex((item: any) => { - return item != null && item.name == shown_name && item.port == port && item.host == host_name && item.host != "Unknown"; + return item != null && item.name == shown_name && item.host == host_name && item.host != "Unknown"; }) == -1 ) { this.castDevices.push({ @@ -158,8 +158,8 @@ export default class RAOP { }); electron.ipcMain.on("getAirplayDevice", (event, data) => { - // this.castDevices = []; - console.log("scan for airplay devices"); + this.castDevices = []; + console.debug("scan for airplay devices"); const browser = this.mdns.createBrowser(this.mdns.tcp("raop")); browser.on("ready", browser.discover); @@ -168,9 +168,10 @@ export default class RAOP { if (service.addresses && service.fullname && service.fullname.includes("_raop._tcp")) { // console.log(service.txt) this._win.webContents.executeJavaScript(`console.log( - "${service.name} ${service.host}:${service.port} ${service.addresses}" + "${service.name} ${service.host}:${service.port} ${service.addresses} ${service.fullname}" )`); - this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt); + let itemname = service.fullname.substring(service.fullname.indexOf("@") + 1, service.fullname.indexOf("._raop._tcp")); + this.ondeviceup(itemname, service.host, service.port, service.addresses, service.txt); } }); @@ -183,7 +184,8 @@ export default class RAOP { this._win.webContents.executeJavaScript(`console.log( "${service.name} ${service.host}:${service.port} ${service.addresses}" )`); - this.ondeviceup(service.name, service.host, service.port, service.addresses, service.txt, true); + let itemname = service.fullname.substring(service.fullname.indexOf("@") + 1, service.fullname.indexOf("._airplay._tcp")); + this.ondeviceup(itemname, service.host, service.port, service.addresses, service.txt, true); } }); @@ -201,52 +203,73 @@ export default class RAOP { // }); }); - electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv) => { + electron.ipcMain.on("performAirplayPCM", (event, ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv, silent) => { if (ipv4 != this.ipairplay || ipport != this.portairplay) { if (this.airtunes == null) { this.airtunes = new this.u(); } this.ipairplay = ipv4; this.portairplay = ipport; - this.device = this.airtunes.add(ipv4, { - port: ipport, - volume: airplay2dv ? 30 : 50, - password: sepassword, - txt: txt, - airplay2: airplay2dv, - debug: true, + let identifier = ipv4 + ":" + ipport + "ap"; + let idx = this.devices.findIndex((a: any) => { + return a.id == identifier; }); - // console.log('lol',txt) - this.device.on("status", (status: any) => { + if (idx != -1) { + delete this.devices[idx]; + this.devices = this.devices.filter((n: any) => n); // remove old controller + } + this.devices.push({ + id: identifier, + ip: ipv4, + port: ipport, + state: 0, + controller: this.airtunes.add(ipv4, { + port: ipport, + volume: airplay2dv ? 30 : 50, + password: sepassword, + txt: txt, + airplay2: airplay2dv, + debug: null, + forceAlac: false, + }), + }); + + idx = this.devices.findIndex((a: any) => { + return a.id == identifier; + }); + + // console.log('lol', this.devices) + this.devices[idx].controller.on("status", (status: any) => { console.log("device status", status); if (status == "ready") { this._win.webContents.setAudioMuted(true); this._win.webContents.executeJavaScript(`CiderAudio.sendAudio()`).catch((err: any) => console.error(err)); } if (status == "need_password") { - this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI()`); + this._win.webContents.executeJavaScript(`app.setAirPlayCodeUI('${this.devices[idx].id}')`); } if (status == "pair_success") { - this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess()`); + this._win.webContents.executeJavaScript(`app.sendAirPlaySuccess(${silent},'${this.devices[idx].id}')`); } if (status == "pair_failed") { this._win.webContents.executeJavaScript(`app.sendAirPlayFailed()`); + this.disconnectAirplay(this.devices[idx].id); } if (status == "stopped") { - this.airtunes.stopAll(() => { - console.log("end"); - }); - this._win.webContents.executeJavaScript(`app.airplayDisconnect(true, ${[ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv]})`).catch((err: any) => console.error(err)); - this.airtunes = null; - this.device = null; - this.ipairplay = ""; - this.portairplay = ""; - this.ok = 1; + // this.airtunes.stopAll(() => { + // console.log("end"); + // }); + if (this.devices[idx]?.state != null && this.devices[idx].state != -1) this._win.webContents.executeJavaScript(`app.airplayDisconnect(true, ${JSON.stringify([ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv])})`).catch((err: any) => console.error(err)); + // this.airtunes = null; + // this.device = null; + // this.ipairplay = ""; + // this.portairplay = ""; + // this.ok = 1; } else { setTimeout(() => { if (this.ok == 1) { - console.log(this.device.key, title ?? "", artist ?? "", album ?? ""); - this.airtunes.setTrackInfo(this.device.key, title ?? "", artist ?? "", album ?? ""); + console.log(this.devices[idx].controller.key, title ?? "", artist ?? "", album ?? ""); + this.airtunes.setTrackInfo(this.devices[idx].controller.key, title ?? "", artist ?? "", album ?? ""); this.uploadImageAirplay(artworkURL); console.log("done"); this.ok == 2; @@ -257,15 +280,25 @@ export default class RAOP { } }); - electron.ipcMain.on("setAirPlayPasscode", (event, passcode) => { - if (this.device) { - this.device.setPasscode(passcode); + electron.ipcMain.on("setAirPlayPasscode", (event, passcode, identifier) => { + if (this.devices.length > 0) { + let idx = this.devices.findIndex((a: any) => { + return a.id == identifier; + }); + if (idx != -1) { + this.devices[idx].controller.setPasscode(passcode); + } } }); - electron.ipcMain.on("setAirPlayVolume", (event, volume) => { - if (this.device) { - this.device.setVolume(volume); + electron.ipcMain.on("setAirPlayVolume", (event, volume, identifier) => { + if (this.devices.length > 0) { + let idx = this.devices.findIndex((a: any) => { + return a.id == identifier; + }); + if (idx != -1) { + this.devices[idx].controller.setVolume(volume); + } } }); @@ -318,33 +351,70 @@ export default class RAOP { } }); - electron.ipcMain.on("disconnectAirplay", (event) => { - this._win.webContents.setAudioMuted(false); - this.airtunes.stopAll(function () { - console.log("end"); - }); - this._win.webContents.executeJavaScript("app.airplayDisconnect(false)").catch((err: any) => console.error(err)); - this.airtunes = null; - this.device = null; - this.ipairplay = ""; - this.portairplay = ""; - this.ok = 1; - this.i = false; + electron.ipcMain.on("disconnectAirplay", (event, identifier = "") => { + console.log("iden", identifier); + this.disconnectAirplay(identifier); }); electron.ipcMain.on("updateAirplayInfo", (event, title, artist, album, artworkURL) => { - if (this.airtunes && this.device) { - console.log(this.device.key, title, artist, album); - this.airtunes.setTrackInfo(this.device.key, title, artist, album); + if (this.airtunes && this.devices.length > 0) { + for (let i in this.devices) { + console.log(this.devices[i].controller.key, title, artist, album); + this.airtunes.setTrackInfo(this.devices[i].controller.key, title, artist, album); + } this.uploadImageAirplay(artworkURL); } }); - electron.ipcMain.on("updateRPCImage", (_event, imageurl) => { + electron.ipcMain.on("discordrpc:updateImage", (_event, imageurl) => { this.uploadImageAirplay(imageurl); }); } + private disconnectAirplay(identifier: any = "") { + console.log("awdas"); + this._win.webContents + .executeJavaScript(`app.airplayDisconnect(false, [], '${identifier}')`) + .then(() => { + if (identifier == "") { + if (this.airtunes) { + for (let i in this.devices) { + this.devices[i].state = -1; + } + this.airtunes.stopAll(() => { + console.log("endAll"); + this.airtunes = null; + this.devices = []; + }); + } else { + this.devices = []; + } + } else { + let idx = this.devices.findIndex((a: any) => { + return a.id == identifier; + }); + if (idx != -1) { + this.devices[idx].state = -1; + this.devices[idx].controller.stop(() => { + console.log(this.devices[idx].id, "stopped"); + }); + delete this.devices[idx]; + this.devices = this.devices.filter((n: any) => n); + } + } + if (this.devices.length == 0) { + console.log("cleanup airtunes"); + this._win.webContents.setAudioMuted(false); + this.airtunes = null; + + this.ipairplay = ""; + this.portairplay = ""; + this.ok = 1; + this.i = false; + } + }) + .catch((err: any) => console.error("lsdsd", err)); + } private uploadImageAirplay = (url: any) => { try { if (url != null && url != "") { @@ -352,7 +422,11 @@ export default class RAOP { fetch(url) .then((res) => res.buffer()) .then((buffer) => { - this.airtunes.setArtwork(this.device.key, buffer, "image/png"); + if (this.airtunes && this.devices.length > 0) { + for (let i in this.devices) { + this.airtunes.setArtwork(this.devices[i].controller.key, buffer, "image/png"); + } + } }) .catch((err) => { console.log(err); @@ -390,16 +464,19 @@ export default class RAOP { * @param attributes Music Attributes (attributes.status = current state) */ onPlaybackStateDidChange(attributes: any): void { - if (this.airtunes && this.device) { + if (this.airtunes && this.devices.length > 0) { let title = attributes?.name ?? ""; let artist = attributes?.artistName ?? ""; let album = attributes?.albumName ?? ""; - let artworkURL = attributes?.artwork?.url ?? null; - console.log(this.device.key, title, artist, album); - this.airtunes.setTrackInfo(this.device.key, title, artist, album); - if (artworkURL != null) { + for (let i in this.devices) { + console.log(this.devices[i].controller.key, title, artist, album); + this.airtunes.setTrackInfo(this.devices[i].controller.key, title, artist, album); + } + let artworkURL = attributes?.artwork?.url ?? null; + + if (artworkURL != null) { + this.uploadImageAirplay(artworkURL.replace("{w}", "1024").replace("{h}", "1024")); } - this.uploadImageAirplay(artworkURL.replace("{w}", "1024").replace("{h}", "1024")); } } } diff --git a/src/preload/cider-preload.js b/src/preload/cider-preload.js index c1fc7c6a..ba968b2f 100644 --- a/src/preload/cider-preload.js +++ b/src/preload/cider-preload.js @@ -9,6 +9,7 @@ const MusicKitInterop = { /* MusicKit.Events.playbackStateDidChange */ MusicKit.getInstance().addEventListener(MusicKit.Events.playbackStateDidChange, () => { const attributes = MusicKitInterop.getAttributes(); + if (!attributes) return; if (MusicKitInterop.filterTrack(attributes, true, false)) { global.ipcRenderer.send("playbackStateDidChange", attributes); global.ipcRenderer.send("wsapi-updatePlaybackState", attributes); @@ -18,11 +19,12 @@ const MusicKitInterop = { /* MusicKit.Events.playbackProgressDidChange */ MusicKit.getInstance().addEventListener(MusicKit.Events.playbackProgressDidChange, async () => { const attributes = MusicKitInterop.getAttributes(); + if (!attributes) return; // wsapi call ipcRenderer.send("wsapi-updatePlaybackState", attributes); // lastfm call if (app.mk.currentPlaybackProgress === app.cfg.connectivity.lastfm.scrobble_after / 100) { - attributes.primaryArtist = app.cfg.connectivity.lastfm.remove_featured ? await this.fetchPrimaryArtist() : attributes.artistName; + attributes.primaryArtist = app.cfg.connectivity.lastfm.remove_featured ? await this.fetchSongRelationships() : attributes.artistName; ipcRenderer.send("lastfm:scrobbleTrack", attributes); } }); @@ -30,16 +32,21 @@ const MusicKitInterop = { /* MusicKit.Events.playbackTimeDidChange */ MusicKit.getInstance().addEventListener(MusicKit.Events.playbackTimeDidChange, () => { ipcRenderer.send("mpris:playbackTimeDidChange", MusicKit.getInstance()?.currentPlaybackTime * 1000 * 1000 ?? 0); + const attributes = MusicKitInterop.getAttributes(); + if (!attributes) return; + ipcRenderer.send("playbackTimeDidChange", attributes); }); /* MusicKit.Events.nowPlayingItemDidChange */ MusicKit.getInstance().addEventListener(MusicKit.Events.nowPlayingItemDidChange, async () => { - console.debug("[cider:preload] nowPlayingItemDidChange"); const attributes = MusicKitInterop.getAttributes(); - attributes.primaryArtist = app.cfg.connectivity.lastfm.remove_featured ? await this.fetchPrimaryArtist() : attributes.artistName; + if (!attributes) return; + attributes.primaryArtist = app.cfg.connectivity.lastfm.remove_featured ? await this.fetchSongRelationships() : attributes.artistName; + + global.ipcRenderer.send("nowPlayingItemDidChange", attributes); if (MusicKitInterop.filterTrack(attributes, false, true)) { - global.ipcRenderer.send("nowPlayingItemDidChange", attributes); + global.ipcRenderer.send("lastfm:FilteredNowPlayingItemDidChange", attributes); } else if (attributes.name !== "no-title-found" && attributes.playParams.id !== "no-id-found") { global.ipcRenderer.send("lastfm:nowPlayingChange", attributes); } @@ -52,6 +59,7 @@ const MusicKitInterop = { await this.sleep(750); MusicKit.getInstance().playbackRate = app.cfg.audio.playbackRate; } + console.debug("[cider:preload] nowPlayingItemDidChange"); }); /* MusicKit.Events.authorizationStatusDidChange */ @@ -81,31 +89,47 @@ const MusicKitInterop = { }); }, - async fetchPrimaryArtist() { - const songID = app.mk.nowPlayingItem.attributes.playParams.catalogId || app.mk.nowPlayingItem.attributes.playParams.id; - const res = await MusicKit.getInstance().api.v3.music("/v1/catalog/" + MusicKit.getInstance().storefrontId + `/songs/${songID}`, { + async fetchSongRelationships({ id = this.getAttributes().songId, relationship = "primaryName" } = {}) { + if (!id) return null; + const res = await MusicKit.getInstance().api.v3.music("/v1/catalog/" + MusicKit.getInstance().storefrontId + `/songs/${id}`, { include: { songs: ["artists"], }, }); + if (!res || !res.data) { - console.warn("[cider:preload] fetchPrimaryArtist: no response"); - return app.mk.nowPlayingItem.attributes.artistName; + console.warn("[cider:preload] fetchSongRelationships: no response"); + if (id === this.getAttributes().songId) { + return this.getAttributes().artistName; + } + } + if (!res.data.data.length) { + console.error(`[cider:preload] fetchSongRelationships: Unable to locate song with id of ${id}`); + if (id === this.getAttributes().songId) { + return this.getAttributes().artistName; + } } - if (!res.data.data.length) { - console.error(`[cider:preload] fetchPrimaryArtist: Unable to locate song with id of ${songID}`); - return app.mk.nowPlayingItem.attributes.artistName; - } const songData = res.data.data[0]; const artistData = songData.relationships.artists.data; - if (artistData.length < 1) { - console.error(`[cider:preload] fetchPrimaryArtist: Unable to find artists related to the song with id of ${songID}`); - return app.mk.nowPlayingItem.attributes.artistName; - } - + const albumData = songData.relationships.albums.data; const primaryArtist = artistData[0]; - return primaryArtist.attributes.name; + + switch (relationship) { + default: + case "primaryName": + if (artistData.length < 1) { + console.error(`[cider:preload] fetchSongRelationships: Unable to find artists related to the song with id of ${id}`); + return app.mk.nowPlayingItem.attributes.artistName; + } + return primaryArtist.attributes.name; + + case "primaryArtist": + return primaryArtist; + + case "album": + return albumData[0]; + } }, getAttributes: function () { @@ -117,6 +141,7 @@ const MusicKitInterop = { const attributes = nowPlayingItem != null ? nowPlayingItem.attributes : {}; attributes.songId = attributes.songId ?? attributes.playParams?.catalogId ?? attributes.playParams?.id; + attributes.type = nowPlayingItem?.type ?? ""; attributes.status = isPlayingExport ?? null; attributes.name = attributes?.name ?? "no-title-found"; attributes.artwork = attributes?.artwork ?? { url: "" }; @@ -126,6 +151,7 @@ const MusicKitInterop = { attributes.url = { cider: `https://cider.sh/link?play/s/${nowPlayingItem?._songId ?? nowPlayingItem?.songId ?? "no-id-found"}`, appleMusic: attributes.websiteUrl ? attributes.websiteUrl : `https://music.apple.com/${mk.storefrontId}/song/${nowPlayingItem?._songId ?? nowPlayingItem?.songId ?? "no-id-found"}`, + songLink: "https://song.link/i/" + attributes.songId, }; if (attributes.playParams.id === "no-id-found") { attributes.playParams.id = nowPlayingItem?.id ?? "no-id-found"; @@ -139,6 +165,10 @@ const MusicKitInterop = { attributes.currentPlaybackProgress = currentPlaybackProgress ?? 0; attributes.startTime = Date.now(); attributes.endTime = Math.round(attributes?.playParams?.id === cache.playParams.id ? Date.now() + attributes?.remainingTime : attributes?.startTime + attributes?.durationInMillis); + + if (attributes.name === "no-title-found") { + return; + } return attributes; }, diff --git a/src/renderer/audio/audio.js b/src/renderer/audio/audio.js index 62df318d..9a993c14 100644 --- a/src/renderer/audio/audio.js +++ b/src/renderer/audio/audio.js @@ -301,9 +301,7 @@ const CiderAudio = { filterlessGain = filterlessGain * spatialProfile.gainComp; } filterlessGain = Math.pow(10, (-1 * (20 * Math.log10(filterlessGain))) / 20).toFixed(4); - filterlessGain > 1.0 - ? CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(1.0, CiderAudio.context.currentTime + 0.3) - : CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(filterlessGain, CiderAudio.context.currentTime + 0.3); + filterlessGain > 1.0 ? CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(1.0, CiderAudio.context.currentTime + 0.3) : CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(filterlessGain, CiderAudio.context.currentTime + 0.3); console.debug(`[Cider][Audio] IntelliGainComp: ${filterlessGain > 1.0 ? 0 : (20 * Math.log10(filterlessGain)).toFixed(2)} dB (${filterlessGain > 1.0 ? 1 : filterlessGain})`); return; } @@ -358,9 +356,7 @@ const CiderAudio = { maxGain = maxGain * spatialProfile.gainComp; } maxGain = Math.pow(10, (-1 * (20 * Math.log10(maxGain))) / 20).toFixed(4); - maxGain > 1.0 - ? CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(1.0, CiderAudio.context.currentTime + 0.3) - : CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(maxGain, CiderAudio.context.currentTime + 0.3); + maxGain > 1.0 ? CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(1.0, CiderAudio.context.currentTime + 0.3) : CiderAudio.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(maxGain, CiderAudio.context.currentTime + 0.3); console.debug(`[Cider][Audio] IntelliGainComp: ${maxGain > 1.0 ? 0 : (20 * Math.log10(maxGain)).toFixed(2)} dB (${maxGain > 1.0 ? 1 : maxGain})`); }, sendAudio: function () { @@ -397,7 +393,7 @@ const CiderAudio = { constructor() { super(); - this._bufferSize = 1024; + this._bufferSize = 2048; this._buffers = null; this._initBuffer(); } @@ -839,49 +835,49 @@ const CiderAudio = { switch (destination) { case "spatial": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.spatialNode); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.spatialNode); console.debug("[Cider][Audio] llpw_n1 -> Spatial"); } catch (e) {} break; case "n6": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.atmosphereRealizer2); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.atmosphereRealizer2); console.debug("[Cider][Audio] llpw_n1 -> atmosphereRealizer2"); } catch (e) {} break; case "n5": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.atmosphereRealizer1); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.atmosphereRealizer1); console.debug("[Cider][Audio] llpw_n1 -> atmosphereRealizer1"); } catch (e) {} break; case "n4": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.vibrantbassNode[0]); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.vibrantbassNode[0]); console.debug("[Cider][Audio] llpw_n1 -> vibrantbassNode"); } catch (e) {} break; case "n3": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.audioBands[0]); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.audioBands[0]); console.debug("[Cider][Audio] llpw_n1 -> audioBands"); } catch (e) {} break; case "n2": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.opportunisticCorrection); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.opportunisticCorrection); console.debug("[Cider][Audio] llpw_n1 -> opportunisticCorrection"); } catch (e) {} break; case "n1": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.audioNodes.llpw[0]); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.audioNodes.llpw[0]); console.debug("[Cider][Audio] llpw_n1 -> llpw"); } catch (e) {} break; case "n0": try { - CiderAudio.audioNodes.llpw.at(-1).connect(CiderAudio.context.destination); + CiderAudio.audioNodes.llpw[CiderAudio.audioNodes.llpw.length - 1].connect(CiderAudio.context.destination); console.debug("[Cider][Audio] llpw_n1 -> destination"); } catch (e) {} break; @@ -910,50 +906,50 @@ const CiderAudio = { switch (destination) { case "spatial": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.spatialNode); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.spatialNode); console.debug("[Cider][Audio] vibrantbass_n4 -> Spatial"); } catch (e) {} break; case "n6": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.atmosphereRealizer2); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.atmosphereRealizer2); console.debug("[Cider][Audio] vibrantbass_n4 -> atmosphereRealizer2"); } catch (e) {} break; case "n5": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.atmosphereRealizer1); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.atmosphereRealizer1); console.debug("[Cider][Audio] vibrantbass_n4 -> atmosphereRealizer1"); } catch (e) {} break; case "n4": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.vibrantbassNode[0]); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.vibrantbassNode[0]); console.debug("[Cider][Audio] vibrantbass_n4 -> vibrantbassNode"); } catch (e) {} break; case "n3": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.audioBands[0]); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.audioBands[0]); console.debug("[Cider][Audio] vibrantbass_n4 -> audioBands"); } catch (e) {} break; case "n2": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.opportunisticCorrection); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.opportunisticCorrection); console.debug("[Cider][Audio] vibrantbass_n4 -> opportunisticCorrection"); } catch (e) {} break; case "n1": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.audioNodes.llpw[0]); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.audioNodes.llpw[0]); console.debug("[Cider][Audio] vibrantbass_n4 -> llpw"); } catch (e) {} break; case "n0": try { - CiderAudio.audioNodes.vibrantbassNode.at(-1).connect(CiderAudio.context.destination); + CiderAudio.audioNodes.vibrantbassNode[CiderAudio.audioNodes.vibrantbassNode.length - 1].connect(CiderAudio.context.destination); console.debug("[Cider][Audio] vibrantbass_n4 -> destination"); } catch (e) {} break; @@ -1168,48 +1164,48 @@ const CiderAudio = { switch (destination) { case "spatial": - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.spatialNode); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.spatialNode); console.debug("[Cider][Audio] Equalizer -> Spatial"); break; case "n6": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.atmosphereRealizer2); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.atmosphereRealizer2); console.debug("[Cider][Audio] Equalizer -> atmosphereRealizer2"); } catch (e) {} break; case "n5": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.atmosphereRealizer1); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.atmosphereRealizer1); console.debug("[Cider][Audio] Equalizer -> atmosphereRealizer1"); } catch (e) {} break; case "n4": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.vibrantbassNode[0]); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.vibrantbassNode[0]); console.debug("[Cider][Audio] Equalizer -> vibrantbassNode"); } catch (e) {} break; case "n3": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.audioBands[0]); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.audioBands[0]); console.debug("[Cider][Audio] Equalizer -> audioBands"); } catch (e) {} break; case "n2": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.opportunisticCorrection); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.opportunisticCorrection); console.debug("[Cider][Audio] Equalizer -> opportunisticCorrection"); } catch (e) {} break; case "n1": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.audioNodes.llpw[0]); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.audioNodes.llpw[0]); console.debug("[Cider][Audio] Equalizer -> llpw"); } catch (e) {} break; case "n0": try { - CiderAudio.audioNodes.audioBands.at(-1).connect(CiderAudio.context.destination); + CiderAudio.audioNodes.audioBands[CiderAudio.audioNodes.audioBands.length - 1].connect(CiderAudio.context.destination); console.debug("[Cider][Audio] Equalizer -> destination"); } catch (e) {} break; diff --git a/src/renderer/audio/renderer.js b/src/renderer/audio/renderer.js index 8ab2ea60..04d6cd8a 100644 --- a/src/renderer/audio/renderer.js +++ b/src/renderer/audio/renderer.js @@ -171,9 +171,7 @@ const CiderAudioRenderer = { maxGain = maxGain * spatialProfile.gainComp; } maxGain = Math.pow(10, (-1 * (20 * Math.log10(maxGain))) / 20).toFixed(4); - maxGain > 1.0 - ? CiderAudioRenderer.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(1.0, CiderAudioRenderer.context.currentTime + 0.3) - : CiderAudioRenderer.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(maxGain, CiderAudioRenderer.context.currentTime + 0.3); + maxGain > 1.0 ? CiderAudioRenderer.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(1.0, CiderAudioRenderer.context.currentTime + 0.3) : CiderAudioRenderer.audioNodes.intelliGainComp.gain.exponentialRampToValueAtTime(maxGain, CiderAudioRenderer.context.currentTime + 0.3); console.debug(`[Cider][Audio] IntelliGainComp: ${maxGain > 1.0 ? 0 : (20 * Math.log10(maxGain)).toFixed(2)} dB (${maxGain > 1.0 ? 1 : maxGain})`); }, atmosphereRealizer2_n6: function (status, destination) { diff --git a/src/renderer/hlscider.js b/src/renderer/hlscider.js index 9e4c34da..f7da6d23 100644 --- a/src/renderer/hlscider.js +++ b/src/renderer/hlscider.js @@ -1589,9 +1589,7 @@ typeof window !== "undefined" && } var levelDetails = levelInfo.details; - var avgDuration = - (partCurrent ? (levelDetails === null || levelDetails === void 0 ? void 0 : levelDetails.partTarget) : levelDetails === null || levelDetails === void 0 ? void 0 : levelDetails.averagetargetduration) || - currentFragDuration; + var avgDuration = (partCurrent ? (levelDetails === null || levelDetails === void 0 ? void 0 : levelDetails.partTarget) : levelDetails === null || levelDetails === void 0 ? void 0 : levelDetails.averagetargetduration) || currentFragDuration; var adjustedbw = void 0; // follow algorithm captured from stagefright : // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp // Pick the highest bandwidth stream below or equal to estimated bandwidth. @@ -1607,9 +1605,7 @@ typeof window !== "undefined" && var bitrate = levels[i].maxBitrate; var fetchDuration = (bitrate * avgDuration) / adjustedbw; - _utils_logger__WEBPACK_IMPORTED_MODULE_6__["logger"].trace( - "level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: " + i + "/" + Math.round(adjustedbw) + "/" + bitrate + "/" + avgDuration + "/" + maxFetchDuration + "/" + fetchDuration - ); // if adjusted bw is greater than level bitrate AND + _utils_logger__WEBPACK_IMPORTED_MODULE_6__["logger"].trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: " + i + "/" + Math.round(adjustedbw) + "/" + bitrate + "/" + avgDuration + "/" + maxFetchDuration + "/" + fetchDuration); // if adjusted bw is greater than level bitrate AND if ( adjustedbw > bitrate && // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches @@ -1971,11 +1967,7 @@ typeof window !== "undefined" && if (this.bufferFlushed) { this.bufferFlushed = false; - this.afterBufferFlushed( - this.mediaBuffer ? this.mediaBuffer : this.media, - _loader_fragment__WEBPACK_IMPORTED_MODULE_7__["ElementaryStreamTypes"].AUDIO, - _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO - ); + this.afterBufferFlushed(this.mediaBuffer ? this.mediaBuffer : this.media, _loader_fragment__WEBPACK_IMPORTED_MODULE_7__["ElementaryStreamTypes"].AUDIO, _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO); } var bufferInfo = this.getFwdBufferInfo(this.mediaBuffer ? this.mediaBuffer : this.media, _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO); @@ -2024,10 +2016,7 @@ typeof window !== "undefined" && return; } - if ( - ((_frag$decryptdata = frag.decryptdata) === null || _frag$decryptdata === void 0 ? void 0 : _frag$decryptdata.keyFormat) === "identity" && - !((_frag$decryptdata2 = frag.decryptdata) !== null && _frag$decryptdata2 !== void 0 && _frag$decryptdata2.key) - ) { + if (((_frag$decryptdata = frag.decryptdata) === null || _frag$decryptdata === void 0 ? void 0 : _frag$decryptdata.keyFormat) === "identity" && !((_frag$decryptdata2 = frag.decryptdata) !== null && _frag$decryptdata2 !== void 0 && _frag$decryptdata2.key)) { this.loadKey(frag, trackDetails); } else { this.loadFragment(frag, trackDetails, targetBufferTime); @@ -2176,12 +2165,7 @@ typeof window !== "undefined" && var transmuxer = this.transmuxer; if (!transmuxer) { - transmuxer = this.transmuxer = new _demux_transmuxer_interface__WEBPACK_IMPORTED_MODULE_9__["default"]( - this.CiderHls, - _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO, - this._handleTransmuxComplete.bind(this), - this._handleTransmuxerFlush.bind(this) - ); + transmuxer = this.transmuxer = new _demux_transmuxer_interface__WEBPACK_IMPORTED_MODULE_9__["default"](this.CiderHls, _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this)); } // Check if we have video initPTS // If not we need to wait for it @@ -2198,9 +2182,7 @@ typeof window !== "undefined" && var chunkMeta = new _types_transmuxer__WEBPACK_IMPORTED_MODULE_10__["ChunkMetadata"](frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial); transmuxer.push(payload, initSegmentData, audioCodec, "", frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS); } else { - _utils_logger__WEBPACK_IMPORTED_MODULE_14__["logger"].log( - "Unknown video PTS for cc " + frag.cc + ", waiting for video PTS before demuxing audio frag " + frag.sn + " of [" + details.startSN + " ," + details.endSN + "],track " + trackId - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_14__["logger"].log("Unknown video PTS for cc " + frag.cc + ", waiting for video PTS before demuxing audio frag " + frag.sn + " of [" + details.startSN + " ," + details.endSN + "],track " + trackId); var _this$waitingData = (this.waitingData = this.waitingData || { frag: frag, @@ -3267,9 +3249,7 @@ typeof window !== "undefined" && state = this.state; var currentTime = media ? media.currentTime : 0; var bufferInfo = _utils_buffer_helper__WEBPACK_IMPORTED_MODULE_3__["BufferHelper"].bufferInfo(mediaBuffer || media, currentTime, config.maxBufferHole); - this.log( - "media seeking to " + (Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state - ); + this.log("media seeking to " + (Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state); if (state === State.ENDED) { this.resetLoadingState(); @@ -3619,19 +3599,7 @@ typeof window !== "undefined" && } } - this.log( - "Loading fragment " + - frag.sn + - " cc: " + - frag.cc + - " " + - (details ? "of [" + details.startSN + "-" + details.endSN + "] " : "") + - (this.logPrefix === "[stream-controller]" ? "level" : "track") + - ": " + - frag.level + - ", target: " + - parseFloat(targetBufferTime.toFixed(3)) - ); // Don't update nextLoadPosition for fragments which are not buffered + this.log("Loading fragment " + frag.sn + " cc: " + frag.cc + " " + (details ? "of [" + details.startSN + "-" + details.endSN + "] " : "") + (this.logPrefix === "[stream-controller]" ? "level" : "track") + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3))); // Don't update nextLoadPosition for fragments which are not buffered if (Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(frag.sn) && !this.bitrateTest) { this.nextLoadPosition = frag.start + frag.duration; @@ -4099,18 +4067,7 @@ typeof window !== "undefined" && if (firstLevelLoad || (!aligned && !slidingStart)) { Object(_utils_discontinuities__WEBPACK_IMPORTED_MODULE_9__["alignStream"])(fragPrevious, lastLevel, details); var alignedSlidingStart = details.fragments[0].start; - this.log( - "Live playlist sliding: " + - alignedSlidingStart.toFixed(2) + - " start-sn: " + - (previousDetails ? previousDetails.startSN : "na") + - "->" + - details.startSN + - " prev-sn: " + - (fragPrevious ? fragPrevious.sn : "na") + - " fragments: " + - length - ); + this.log("Live playlist sliding: " + alignedSlidingStart.toFixed(2) + " start-sn: " + (previousDetails ? previousDetails.startSN : "na") + "->" + details.startSN + " prev-sn: " + (fragPrevious ? fragPrevious.sn : "na") + " fragments: " + length); return alignedSlidingStart; } @@ -5767,11 +5724,7 @@ typeof window !== "undefined" && ot: ot, }; - if ( - ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].VIDEO || - ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].AUDIO || - ot == _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].MUXED - ) { + if (ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].VIDEO || ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].AUDIO || ot == _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].MUXED) { data.br = level.bitrate / 1000; data.tb = _this.getTopBandwidth(ot); data.bl = _this.getBufferLength(ot); @@ -5871,10 +5824,7 @@ typeof window !== "undefined" && // apply baseline data _extends(data, this.createData()); - var isVideo = - data.ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].INIT || - data.ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].VIDEO || - data.ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].MUXED; + var isVideo = data.ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].INIT || data.ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].VIDEO || data.ot === _types_cmcd__WEBPACK_IMPORTED_MODULE_1__["CMCDObjectType"].MUXED; if (this.starved && isVideo) { data.bs = true; @@ -8610,10 +8560,7 @@ typeof window !== "undefined" && levels = levels.filter(function (_ref2) { var audioCodec = _ref2.audioCodec, videoCodec = _ref2.videoCodec; - return ( - (!audioCodec || Object(_utils_codecs__WEBPACK_IMPORTED_MODULE_3__["isCodecSupportedInMp4"])(audioCodec, "audio")) && - (!videoCodec || Object(_utils_codecs__WEBPACK_IMPORTED_MODULE_3__["isCodecSupportedInMp4"])(videoCodec, "video")) - ); + return (!audioCodec || Object(_utils_codecs__WEBPACK_IMPORTED_MODULE_3__["isCodecSupportedInMp4"])(audioCodec, "audio")) && (!videoCodec || Object(_utils_codecs__WEBPACK_IMPORTED_MODULE_3__["isCodecSupportedInMp4"])(videoCodec, "video")); }); if (data.audioTracks) { @@ -9319,10 +9266,7 @@ typeof window !== "undefined" && ccOffset = oldFrag.cc - newFrag.cc; } - if ( - Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(oldFrag.startPTS) && - Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(oldFrag.endPTS) - ) { + if (Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(oldFrag.startPTS) && Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(oldFrag.endPTS)) { newFrag.start = newFrag.startPTS = oldFrag.startPTS; newFrag.startDTS = oldFrag.startDTS; newFrag.appendedPTS = oldFrag.appendedPTS; @@ -9902,10 +9846,7 @@ typeof window !== "undefined" && } // We want to load the key if we're dealing with an identity key, because we will decrypt // this content using the key we fetch. Other keys will be handled by the DRM CDM via EME. - if ( - ((_frag$decryptdata = frag.decryptdata) === null || _frag$decryptdata === void 0 ? void 0 : _frag$decryptdata.keyFormat) === "identity" && - !((_frag$decryptdata2 = frag.decryptdata) !== null && _frag$decryptdata2 !== void 0 && _frag$decryptdata2.key) - ) { + if (((_frag$decryptdata = frag.decryptdata) === null || _frag$decryptdata === void 0 ? void 0 : _frag$decryptdata.keyFormat) === "identity" && !((_frag$decryptdata2 = frag.decryptdata) !== null && _frag$decryptdata2 !== void 0 && _frag$decryptdata2.key)) { this.loadKey(frag, levelDetails); } else { this.loadFragment(frag, levelDetails, targetBufferTime); @@ -10257,13 +10198,7 @@ typeof window !== "undefined" && // this.log(`Transmuxing ${frag.sn} of [${details.startSN} ,${details.endSN}],level ${frag.level}, cc ${frag.cc}`); var transmuxer = (this.transmuxer = - this.transmuxer || - new _demux_transmuxer_interface__WEBPACK_IMPORTED_MODULE_8__["default"]( - this.CiderHls, - _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].MAIN, - this._handleTransmuxComplete.bind(this), - this._handleTransmuxerFlush.bind(this) - )); + this.transmuxer || new _demux_transmuxer_interface__WEBPACK_IMPORTED_MODULE_8__["default"](this.CiderHls, _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].MAIN, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this))); var partIndex = part ? part.index : -1; var partial = partIndex !== -1; var chunkMeta = new _types_transmuxer__WEBPACK_IMPORTED_MODULE_9__["ChunkMetadata"](frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial); @@ -14779,8 +14714,8 @@ typeof window !== "undefined" && */ var chromeVersion = null; var BitratesMap = [ - 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, - 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, + 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 144, 160, ]; var SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; var SamplesCoefficients = [ @@ -18354,9 +18289,7 @@ typeof window !== "undefined" && var _sourceBuffer$prototy; var sourceBuffer = getSourceBuffer(); - return ( - typeof (sourceBuffer === null || sourceBuffer === void 0 ? void 0 : (_sourceBuffer$prototy = sourceBuffer.prototype) === null || _sourceBuffer$prototy === void 0 ? void 0 : _sourceBuffer$prototy.changeType) === "function" - ); + return typeof (sourceBuffer === null || sourceBuffer === void 0 ? void 0 : (_sourceBuffer$prototy = sourceBuffer.prototype) === null || _sourceBuffer$prototy === void 0 ? void 0 : _sourceBuffer$prototy.changeType) === "function"; } /***/ @@ -18743,10 +18676,7 @@ typeof window !== "undefined" && var start = segment.byteRangeStartOffset; var end = segment.byteRangeEndOffset; - if ( - Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(start) && - Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(end) - ) { + if (Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(start) && Object(_home_runner_work_CiderHls_js_CiderHls_js_src_polyfills_number__WEBPACK_IMPORTED_MODULE_0__["isFiniteNumber"])(end)) { loaderContext.rangeStart = start; loaderContext.rangeEnd = end; } @@ -18854,12 +18784,7 @@ typeof window !== "undefined" && this._url = null; this.baseurl = void 0; this.relurl = void 0; - this.elementaryStreams = - ((_this$elementaryStrea = {}), - (_this$elementaryStrea[ElementaryStreamTypes.AUDIO] = null), - (_this$elementaryStrea[ElementaryStreamTypes.VIDEO] = null), - (_this$elementaryStrea[ElementaryStreamTypes.AUDIOVIDEO] = null), - _this$elementaryStrea); + this.elementaryStreams = ((_this$elementaryStrea = {}), (_this$elementaryStrea[ElementaryStreamTypes.AUDIO] = null), (_this$elementaryStrea[ElementaryStreamTypes.VIDEO] = null), (_this$elementaryStrea[ElementaryStreamTypes.AUDIOVIDEO] = null), _this$elementaryStrea); this.baseurl = baseurl; } // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array @@ -19730,11 +19655,7 @@ typeof window !== "undefined" && function isMP4Url(url) { var _URLToolkit$parseURL$, _URLToolkit$parseURL; - return MP4_REGEX_SUFFIX.test( - (_URLToolkit$parseURL$ = (_URLToolkit$parseURL = url_toolkit__WEBPACK_IMPORTED_MODULE_1__["parseURL"](url)) === null || _URLToolkit$parseURL === void 0 ? void 0 : _URLToolkit$parseURL.path) != null - ? _URLToolkit$parseURL$ - : "" - ); + return MP4_REGEX_SUFFIX.test((_URLToolkit$parseURL$ = (_URLToolkit$parseURL = url_toolkit__WEBPACK_IMPORTED_MODULE_1__["parseURL"](url)) === null || _URLToolkit$parseURL === void 0 ? void 0 : _URLToolkit$parseURL.path) != null ? _URLToolkit$parseURL$ : ""); } var M3U8Parser = /*#__PURE__*/ (function () { @@ -20819,9 +20740,7 @@ typeof window !== "undefined" && timeout = false; } - _utils_logger__WEBPACK_IMPORTED_MODULE_3__["logger"].warn( - "[playlist-loader]: A network " + (timeout ? "timeout" : "error") + " occurred while loading " + context.type + " level: " + context.level + " id: " + context.id + ' group-id: "' + context.groupId + '"' - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_3__["logger"].warn("[playlist-loader]: A network " + (timeout ? "timeout" : "error") + " occurred while loading " + context.type + " level: " + context.level + " id: " + context.id + ' group-id: "' + context.groupId + '"'); var details = _errors__WEBPACK_IMPORTED_MODULE_2__["ErrorDetails"].UNKNOWN; var fatal = false; var loader = this.getInternalLoader(context); @@ -21004,20 +20923,20 @@ typeof window !== "undefined" && if (channelCount === 1) { // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac return new Uint8Array([ - 0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, + 0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, ]); } else if (channelCount === 2) { // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac return new Uint8Array([ - 0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, + 0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, ]); } else if (channelCount === 3) { // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac return new Uint8Array([ - 0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, + 0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e, ]); } @@ -22334,13 +22253,7 @@ typeof window !== "undefined" && initSegment = this.generateIS(audioTrack, videoTrack, timeOffset); } - audio = this.remuxAudio( - audioTrack, - audioTimeOffset, - this.isAudioContiguous, - accurateTimeOffset, - hasVideo || enoughVideoSamples || playlistType === _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO ? videoTimeOffset : undefined - ); + audio = this.remuxAudio(audioTrack, audioTimeOffset, this.isAudioContiguous, accurateTimeOffset, hasVideo || enoughVideoSamples || playlistType === _types_loader__WEBPACK_IMPORTED_MODULE_6__["PlaylistLevelType"].AUDIO ? videoTimeOffset : undefined); if (enoughVideoSamples) { var audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0; // if initSegment was generated without video samples, regenerate it again @@ -22531,9 +22444,7 @@ typeof window !== "undefined" && if (ptsDtsShift < averageSampleDuration * -2) { // Fix for "CNN special report, with CC" in test-streams (including Safari browser) // With large PTS < DTS errors such as this, we want to correct CTS while maintaining increasing DTS values - _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn( - "PTS < DTS detected in video samples, offsetting DTS from PTS by " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(-averageSampleDuration, true) + " ms" - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn("PTS < DTS detected in video samples, offsetting DTS from PTS by " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(-averageSampleDuration, true) + " ms"); var lastDts = ptsDtsShift; for (var _i = 0; _i < nbSamples; _i++) { @@ -22543,9 +22454,7 @@ typeof window !== "undefined" && } else { // Fix for "Custom IV with bad PTS DTS" in test-streams // With smaller PTS < DTS errors we can simply move all DTS back. This increases CTS without causing buffer gaps or decode errors in Safari - _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn( - "PTS < DTS detected in video samples, shifting DTS by " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(ptsDtsShift, true) + " ms to overcome this issue" - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn("PTS < DTS detected in video samples, shifting DTS by " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(ptsDtsShift, true) + " ms to overcome this issue"); for (var _i2 = 0; _i2 < nbSamples; _i2++) { inputSamples[_i2].dts = inputSamples[_i2].dts + ptsDtsShift; @@ -22563,13 +22472,9 @@ typeof window !== "undefined" && if (foundHole || foundOverlap) { if (foundHole) { - _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn( - "AVC: " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(delta, true) + " ms (" + delta + "dts) hole between fragments detected, filling it" - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn("AVC: " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(delta, true) + " ms (" + delta + "dts) hole between fragments detected, filling it"); } else { - _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn( - "AVC: " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected" - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn("AVC: " + Object(_utils_timescale_conversion__WEBPACK_IMPORTED_MODULE_7__["toMsFromMpegTsClock"])(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected"); } firstDTS = nextAvcDts; @@ -22682,9 +22587,7 @@ typeof window !== "undefined" && mp4SampleDuration = lastFrameDuration; } - _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].log( - "[mp4-remuxer]: It is approximately " + deltaToFrameEnd / 90 + " ms to the next segment; using duration " + mp4SampleDuration / 90 + " ms for the last video frame." - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].log("[mp4-remuxer]: It is approximately " + deltaToFrameEnd / 90 + " ms to the next segment; using duration " + mp4SampleDuration / 90 + " ms for the last video frame."); } else { mp4SampleDuration = lastFrameDuration; } @@ -22759,10 +22662,7 @@ typeof window !== "undefined" && var timeOffsetMpegTS = timeOffset * inputTimeScale; this.isAudioContiguous = contiguous = - contiguous || - (inputSamples.length && - nextAudioPts > 0 && - ((accurateTimeOffset && Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000) || Math.abs(normalizePts(inputSamples[0].pts - initPTS, timeOffsetMpegTS) - nextAudioPts) < 20 * inputSampleDuration)); // compute normalized PTS + contiguous || (inputSamples.length && nextAudioPts > 0 && ((accurateTimeOffset && Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000) || Math.abs(normalizePts(inputSamples[0].pts - initPTS, timeOffsetMpegTS) - nextAudioPts) < 20 * inputSampleDuration)); // compute normalized PTS inputSamples.forEach(function (sample) { sample.pts = normalizePts(sample.pts - initPTS, timeOffsetMpegTS); @@ -22833,9 +22733,7 @@ typeof window !== "undefined" && this.nextAudioPts = nextAudioPts = nextPts; } - _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn( - "[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round((1000 * delta) / inputTimeScale) + " ms gap." - ); + _utils_logger__WEBPACK_IMPORTED_MODULE_5__["logger"].warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round((1000 * delta) / inputTimeScale) + " ms gap."); for (var j = 0; j < missing; j++) { var newStamp = Math.max(nextPts, 0); diff --git a/src/renderer/less/elements.less b/src/renderer/less/elements.less index 19f960e7..52d77735 100644 --- a/src/renderer/less/elements.less +++ b/src/renderer/less/elements.less @@ -334,6 +334,15 @@ width: 42px; height: 42px; flex: 0 0 auto; + &.circle { + border-radius: 50%; + .mediaitem-artwork { + border-radius: 50%; + } + img { + border-radius: 50%; + } + } } &:hover { @@ -349,6 +358,11 @@ color: #eee; } + &.hintactive { + background: var(--keyColor); + border-radius: 6px; + } + .queue-info { justify-content: center; display: flex; @@ -392,6 +406,38 @@ } } +.search-hints .cd-queue-item { + &:hover { + background: var(--selected); + border-radius: 6px; + + .circular-play-button { + opacity: 1; + } + } + &:active { + background: var(--selected-click); + color: #eee; + border-radius: 6px; + } +} + +/* Circle Play Button */ +.circular-play-button { + position: relative; + opacity: 0; + top: -34px; + z-index: 5; + left: 8px; + align-items: center; + background: rgba(100, 100, 100, 0.5); + border: none; + cursor: pointer; + border-radius: 100%; + height: 26px; + box-shadow: var(--ciderShadow-Generic); +} + /* horizontal media scroller */ .cd-hmedia-scroller { &::-webkit-scrollbar-thumb { @@ -1842,6 +1888,12 @@ input[type="checkbox"][switch]:checked:active::before { background-position: center; } +.playback-button.stop { + background-image: url("./assets/cider-icons/stop.svg"); + background-size: 38px; + background-position: center; +} + .playback-button.play { background-image: url("./assets/cider-icons/play.svg"); background-size: 38px; diff --git a/src/renderer/main/events.js b/src/renderer/main/events.js index 08df18ae..d90b54c2 100644 --- a/src/renderer/main/events.js +++ b/src/renderer/main/events.js @@ -58,6 +58,10 @@ const Events = { try { app.mk._services.mediaItemPlayback._currentPlayer.destroy(); } catch (e) {} + try { + this.radiohls.destroy(); + this.radiohls = null; + } catch (_) {} try { let searchInt = setInterval(function () { if (document.getElementById("apple-music-player")) { diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js index ebdd3899..1bf457d5 100644 --- a/src/renderer/main/vueapp.js +++ b/src/renderer/main/vueapp.js @@ -20,11 +20,13 @@ const app = new Vue({ pluginMenuEntries: [], lz: ipcRenderer.sendSync("get-i18n", "en_US"), lzListing: ipcRenderer.sendSync("get-i18n-listing"), + radiohls: null, search: { term: "", cursor: -1, hints: [], showHints: false, + showSearchView: false, results: {}, resultsSocial: {}, resultsLibrary: {}, @@ -44,11 +46,7 @@ const app = new Vue({ browsepage: [], listennow: [], madeforyou: [], - radio: { - personal: {}, - recent: {}, - amlive: {}, - }, + radio: [], mklang: "en", webview: { url: "", @@ -159,6 +157,7 @@ const app = new Vue({ miniTmpY: "", tmpVar: [], notification: false, + hintscontext: false, chrome: { sidebarCollapsed: false, nativeControls: false, @@ -247,6 +246,8 @@ const app = new Vue({ idleTimer: null, idleState: false, appVisible: true, + currentAirPlayCodeID: "", + airplayTrys: [], }, watch: { cfg: { @@ -256,6 +257,12 @@ const app = new Vue({ }, deep: true, }, + "cfg.connectivity.discord_rpc.enabled"(newValue) { + ipcRenderer.send("discordrpc:reload", newValue); + }, + "mk.privateEnabled"(newValue) { + ipcRenderer.send("onPrivacyModeChange", newValue); + }, page: () => { document.getElementById("app-content").scrollTo(0, 0); app.resetState(); @@ -287,6 +294,12 @@ const app = new Vue({ }, async oobeInit() { this.appMode = "oobe"; + for (const [k, v] of Object.entries(ipcRenderer.sendSync("get-i18n-listing"))) { + if (v.code === navigator.language.replace("-", "_")) { + this.cfg.general.language = v.code; + break; + } + } this.setLz(this.cfg.general.language); this.setLzManual(); clearTimeout(this.hangtimer); @@ -569,7 +582,24 @@ const app = new Vue({ window.location.hash = `#charts/top`; } else { const id = url.split("id=")[1]; - window.location.hash = `#groupings/${id}`; + if (id != null) { + window.location.hash = `#groupings/${id}`; + } else { + const params = new Proxy(new URLSearchParams(new URL(url).search), { + get: (searchParams, prop) => searchParams.get(prop), + }); + let id = params.fcId; + app + .getTypeFromID("room", id, false, { + platform: "web", + extend: "editorialArtwork,uber,lockupStyle", + }) + .then(() => { + let kind = "multiroom"; + window.location.hash = `${kind}/${id}`; + document.querySelector("#app-content").scrollTop = 0; + }); + } } }, navigateForward() { @@ -633,9 +663,46 @@ const app = new Vue({ this.modals.addToPlaylist = false; app.newPlaylist(app.getLz("term.newPlaylist"), pl_items); }, + async isSongInPlaylist(song_ids, playlist_id) { + let isInPlaylist = false; + const playlistTracks = ( + await app.mk.api.v3.music(`/v1/me/library/playlists/${playlist_id}/tracks`, { + platform: "web", + l: app.mklang, + }) + ).data?.data; + + playlistTracks.forEach((track) => { + if (song_ids.includes(track.id)) { + isInPlaylist = true; + } + }); + return isInPlaylist; + }, + addToPlaylist(pid, pitems) { + app.mk.api.v3 + .music( + `/v1/me/library/playlists/${pid}/tracks`, + {}, + { + fetchOptions: { + method: "POST", + body: JSON.stringify({ + data: pitems, + }), + }, + } + ) + .then(() => { + if (app.page === "playlist_" + pid) { + app.getPlaylistFromID(app.showingPlaylist.id, true); + } + }); + }, async addSelectedToPlaylist(playlist_id) { let self = this; let pl_items = []; + const song_ids = []; for (let i = 0; i < self.selectedMediaItems.length; i++) { if (self.selectedMediaItems[i].kind == "song" || self.selectedMediaItems[i].kind == "songs") { self.selectedMediaItems[i].kind = "songs"; @@ -643,6 +710,7 @@ const app = new Vue({ id: self.selectedMediaItems[i].id, type: self.selectedMediaItems[i].kind, }); + song_ids.push(self.selectedMediaItems[i].id); } else if ((self.selectedMediaItems[i].kind == "album" || self.selectedMediaItems[i].kind == "albums") && self.selectedMediaItems[i].isLibrary != true) { self.selectedMediaItems[i].kind = "albums"; let res = await self.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/albums/${self.selectedMediaItems[i].id}/tracks`); @@ -650,12 +718,14 @@ const app = new Vue({ return { id: i.id, type: i.type }; }); pl_items = pl_items.concat(ids); + song_ids.push(...ids.map((id) => id.id)); } else if (self.selectedMediaItems[i].kind == "library-song" || self.selectedMediaItems[i].kind == "library-songs") { self.selectedMediaItems[i].kind = "library-songs"; pl_items.push({ id: self.selectedMediaItems[i].id, type: self.selectedMediaItems[i].kind, }); + song_ids.push(self.selectedMediaItems[i].id); } else if (self.selectedMediaItems[i].kind == "library-album" || self.selectedMediaItems[i].kind == "library-albums" || (self.selectedMediaItems[i].kind == "album" && self.selectedMediaItems[i].isLibrary == true)) { self.selectedMediaItems[i].kind = "library-albums"; let res = await self.mk.api.v3.music(`/v1/me/library/albums/${self.selectedMediaItems[i].id}/tracks`); @@ -663,32 +733,26 @@ const app = new Vue({ return { id: i.id, type: i.type }; }); pl_items = pl_items.concat(ids); + song_ids.push(...ids.map((id) => id.id)); } else { pl_items.push({ id: self.selectedMediaItems[i].id, type: self.selectedMediaItems[i].kind, }); + song_ids.push(self.selectedMediaItems[i].id); } } this.modals.addToPlaylist = false; - await app.mk.api.v3 - .music( - `/v1/me/library/playlists/${playlist_id}/tracks`, - {}, - { - fetchOptions: { - method: "POST", - body: JSON.stringify({ - data: pl_items, - }), - }, - } - ) - .then(() => { - if (this.page == "playlist_" + this.showingPlaylist.id) { - this.getPlaylistFromID(this.showingPlaylist.id, true); + + if (await this.isSongInPlaylist(song_ids, playlist_id)) { + app.confirm(app.getLz("action.addToPlaylist.duplicate"), (result) => { + if (result === true) { + app.addToPlaylist(playlist_id, pl_items); } }); + } else { + app.addToPlaylist(playlist_id, pl_items); + } }, async init() { let self = this; @@ -773,6 +837,8 @@ const app = new Vue({ this.mk.volume = -1; } + // Restore mk + // load cached library let librarySongs = await CiderCache.getCache("library-songs"); let libraryAlbums = await CiderCache.getCache("library-albums"); @@ -995,6 +1061,11 @@ const app = new Vue({ return; } // EVIL EMPTY OBJECTS BE GONE + try { + this.radiohls.destroy(); + this.radiohls = null; + } catch (_) {} + try { if ((MusicKit.getInstance().nowPlayingItem["type"] ?? "").includes("ideo")) { setTimeout(() => { @@ -1141,8 +1212,6 @@ const app = new Vue({ if (this.cfg.general.themeUpdateNotification && !this.isDev) { this.checkForThemeUpdates(); } - - ipcRenderer.invoke("scanLibrary"); }, setWindowScaleFactor() { let scale = (((window.devicePixelRatio * window.innerWidth) / 1280) * window.innerHeight) / 720; @@ -1181,7 +1250,7 @@ const app = new Vue({ const notify = notyf.open({ className: "notyf-info", type: "info", - message: `[Themes] ${theme.name} has an update available.`, + message: app.stringTemplateParser(app.getLz("settings.notyf.visual.theme.updateAvailable"), { theme: theme.name }), }); notify.on("click", () => { app.openSettingsPage("github-themes"); @@ -1457,15 +1526,15 @@ const app = new Vue({ const cachedTrackMapping = await CiderCache.getCache("library-playlists-tracks"); if (cachedPlaylist) { - console.debug("using cached playlists"); + console.debug("[CiderCache] Using cached playlist"); this.playlists.listing = cachedPlaylist; self.sortPlaylists(); } else { - console.debug("playlist has no cache"); + console.debug("[CiderCache] Playlist has no cache"); } if (cachedTrackMapping) { - console.debug("using cached track mapping"); + console.debug("[CiderCache] Using cached track mapping"); this.playlists.trackMapping = cachedTrackMapping; } if (localOnly) { @@ -1473,7 +1542,7 @@ const app = new Vue({ } } - this.library.backgroundNotification.message = "Building playlist cache..."; + this.library.backgroundNotification.message = app.getLz("notification.buildingPlaylistCache"); this.library.backgroundNotification.show = true; async function deepScan(parent = "p.playlistsroot") { @@ -1895,10 +1964,31 @@ const app = new Vue({ async getSearchHints() { if (this.search.term == "") { this.search.hints = []; + this.search.showHints = true; + this.search.showSearchView = false; return; } - let hints = await (await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search/hints?term=${this.search.term}`)).data.results; - this.search.hints = hints ? hints.terms : []; + let hints = await ( + await app.mk.api.v3.music(`/v1/catalog/${app.mk.storefrontId}/search/suggestions?term=${encodeURIComponent(this.search.term)}`, { + "fields[albums]": "artwork,name,playParams,url,artistName,id", + "fields[artists]": "url,name,artwork,id", + "fields[songs]": "artwork,name,playParams,url,artistName,id", + kinds: "terms,topResults", + l: this.mklang, + "limit[results:terms]": 5, + "limit[results:topResults]": 5, + "omit[resource]": "autos", + platform: "web", + types: "activities,albums,artists,editorial-items,music-movies,playlists,record-labels,songs,stations", + }) + ).data.results; + let shints = hints ? hints.suggestions : []; + for (let item in shints) { + if ((shints[item]?.displayTerm ?? "").includes("?fields[")) { + shints[item].displayTerm = shints[item].searchTerm = shints[item].displayTerm.split("?fields[")[0]; + } + } + this.search.hints = shints; }, getSongProgress() { if (this.playerLCD.userInteraction) { @@ -1916,6 +2006,7 @@ const app = new Vue({ * @memberOf app */ convertTime(seconds, format = "short") { + if (app.mk?.nowPlayingItem?.type === "radioStation") return; if (isNaN(seconds) || seconds === Infinity) { seconds = 0; } @@ -1996,26 +2087,36 @@ const app = new Vue({ } if (kind.toString().includes("apple-curator")) { kind = "appleCurator"; - app.getTypeFromID("appleCurator", id, false, { - platform: "web", - include: "grouping,playlists", - extend: "editorialArtwork", - "art[url]": "f", - }); - window.location.hash = `${kind}/${id}`; - document.querySelector("#app-content").scrollTop = 0; + app + .getTypeFromID("appleCurator", id, false, { + platform: "web", + include: "grouping,playlists", + extend: "editorialArtwork", + "art[url]": "f", + }) + .then(() => { + kind = "appleCurator"; + window.location.hash = `${kind}/${id}`; + document.querySelector("#app-content").scrollTop = 0; + }); } else if (kind == "editorial-elements" || kind == "editorial-items") { console.debug(item); if (item.relationships?.contents?.data != null && item.relationships?.contents?.data.length > 0) { this.routeView(item.relationships.contents.data[0]); } else if (item.attributes?.link?.url != null) { - if (item.attributes.link.url.includes("viewMultiRoom")) { + if (item.attributes.link.url.includes("viewMultiRoom") || item.attributes.link.url.includes("/collection/")) { const params = new Proxy(new URLSearchParams(new URL(item.attributes.link.url).search), { get: (searchParams, prop) => searchParams.get(prop), }); id = params.fcId; + kind = "multiroom"; + if (item.attributes.link.url.includes("viewMultiRoom")) { + kind = "multiroom"; + } else { + kind = "room"; + } app - .getTypeFromID("multiroom", id, false, { + .getTypeFromID(kind, id, false, { platform: "web", extend: "editorialArtwork,uber,lockupStyle", }) @@ -2116,6 +2217,17 @@ const app = new Vue({ } // app.getTypeFromID((kind), (id), (isLibrary), params); + } else if (kind.toString().includes("song")) { + const albumUrl = new Promise(async (resolve, reject) => { + resolve(await MusicKitInterop.fetchSongRelationships({ id: id, relationship: "album" })); + }); + albumUrl.then((data) => { + if (data && data.type === "albums" && data.id) { + window.location.hash = `album/${data.id}${isLibrary ? "/" + isLibrary : ""}`; + } else { + app.playMediaItemById(id, kind, isLibrary, item.attributes.url ?? ""); + } + }); } else { app.playMediaItemById(id, kind, isLibrary, item.attributes.url ?? ""); } @@ -2184,6 +2296,11 @@ const app = new Vue({ artistId = item.relationships.artists.data[0].id; } } + if (item.relationships.albums && item.relationships.albums.data.length > 0) { + if (item.relationships.albums.data[0].attributes.artistUrl) { + artistId = item.relationships.albums.data[0].attributes.artistUrl.split("/").pop(); + } + } if (artistId == "") { const url = item.relationships.catalog.data[0].attributes.artistUrl; artistId = url.substring(url.lastIndexOf("/") + 1); @@ -2324,7 +2441,7 @@ const app = new Vue({ } finally { if (kind == "appleCurator") { app.appleCurator = a.data.data[0]; - } else if (kind == "multiroom") { + } else if (kind == "multiroom" || kind == "room") { app.multiroom = a.data.data[0]; } else { this.getPlaylistContinuous(a, true); @@ -2333,7 +2450,7 @@ const app = new Vue({ } finally { if (kind == "appleCurator") { app.appleCurator = a.data.data[0]; - } else if (kind == "multiroom") { + } else if (kind == "multiroom" || kind == "room") { app.multiroom = a.data.data[0]; } else { this.getPlaylistContinuous(a, true); @@ -2344,28 +2461,32 @@ const app = new Vue({ let self = this; let prefs = this.cfg.libraryPrefs.songs; - const albumAdded = {}; - - for (const listing of self.library?.albums?.listing ?? []) { - albumAdded[listing.id] = listing.attributes?.dateAdded; - } - - let startTime = new Date().getTime(); - function sortSongs() { // sort this.library.songs.displayListing by song.attributes[self.library.songs.sorting] in descending or ascending order based on alphabetical order and numeric order // check if song.attributes[self.library.songs.sorting] is a number and if so, sort by number if not, sort by alphabetical order ignoring case self.library.songs.displayListing.sort((a, b) => { let aa = a.attributes[prefs.sort]; let bb = b.attributes[prefs.sort]; - if (prefs.sort == "genre") { + if (prefs.sort === "genre") { aa = a.attributes.genreNames[0]; bb = b.attributes.genreNames[0]; - } else if (prefs.sort == "dateAdded") { - let albumida = a.relationships?.albums?.data[0]?.id; - let albumidb = b.relationships?.albums?.data[0]?.id; - aa = startTime - new Date(albumAdded[albumida] ?? "1970-01-01T00:01:01Z").getTime(); - bb = startTime - new Date(albumAdded[albumidb] ?? "1970-01-01T00:01:01Z").getTime(); + } else if (prefs.sort === "dateAdded") { + aa = a.relationships?.albums?.data[0]?.attributes?.dateAdded; + bb = b.relationships?.albums?.data[0]?.attributes?.dateAdded; + } else if (prefs.sort === "artistName") { + if (a.relationships?.artists?.data[0]?.id === b.relationships?.artists?.data[0]?.id) { + aa = a.attributes.albumName; + bb = b.attributes.albumName; + } + if (a.relationships?.albums?.data[0]?.id === b.relationships?.albums?.data[0]?.id) { + aa = a.attributes.trackNumber; + bb = b.attributes.trackNumber; + } + } else if (prefs.sort === "albumName") { + if (a.relationships?.albums?.data[0]?.id === b.relationships?.albums?.data[0]?.id) { + aa = a.attributes.trackNumber; + bb = b.attributes.trackNumber; + } } if (aa == null) { aa = ""; @@ -2373,13 +2494,13 @@ const app = new Vue({ if (bb == null) { bb = ""; } - if (prefs.sortOrder == "asc") { + if (prefs.sortOrder === "asc") { if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) { return aa - bb; } else { return aa.toString().toLowerCase().localeCompare(bb.toString().toLowerCase()); } - } else if (prefs.sortOrder == "desc") { + } else if (prefs.sortOrder === "desc") { if (aa.toString().match(/^\d+$/) && bb.toString().match(/^\d+$/)) { return bb - aa; } else { @@ -2576,7 +2697,7 @@ const app = new Vue({ } let truemethod = !method.endsWith("s") ? method + "s" : method; try { - if (method.includes(`multiroom`)) { + if (method.includes(`room`)) { return await this.mk.api.v3.music(`v1/editorial/${app.mk.storefrontId}/${truemethod}/${term.toString()}`, params, params2); } else if (library) { return await this.mk.api.v3.music(`v1/me/library/${truemethod}/${term.toString()}`, params, params2); @@ -2972,6 +3093,38 @@ const app = new Vue({ this.getListenNow(attempt + 1); } }, + async getRadioPage(attempt = 0) { + if (this.radio.timestamp > Date.now() - 120000) { + return; + } + if (attempt > 3) { + return; + } + try { + app.mk.api.v3 + .music(`/v1/editorial/${app.mk.storefrontId}/groupings`, { + platform: "web", + name: "radio", + "omit[resource:artists]": "relationships", + "include[albums]": "artists", + "include[songs]": "artists", + "include[music-videos]": "artists", + extend: "editorialArtwork,artistUrl", + "fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes", + "art[url]": "f", + l: app.mklang, + }) + .then((radio) => { + app.radio = radio.data.data[0]; + console.debug(app.radio); + }); + + this.radio.timestamp = Date.now(); + } catch (e) { + console.log(e); + this.getRadioPage(attempt + 1); + } + }, async getBrowsePage(attempt = 0) { if (this.browsepage.timestamp > Date.now() - 120000) { return; @@ -2990,7 +3143,7 @@ const app = new Vue({ extend: "editorialArtwork,artistUrl", "fields[artists]": "name,url,artwork,editorialArtwork,genreNames,editorialNotes", "art[url]": "f", - l: this.mklang, + l: app.mklang, }); this.browsepage = browse.data.data[0]; this.browsepage.timestamp = Date.now(); @@ -3005,9 +3158,7 @@ const app = new Vue({ return; } try { - let mfu = await app.mk.api.v3.music( - "/v1/me/library/playlists?platform=web&extend=editorialVideo&fields%5Bplaylists%5D=lastModifiedDate&filter%5Bfeatured%5D=made-for-you&include%5Blibrary-playlists%5D=catalog&fields%5Blibrary-playlists%5D=artwork%2Cname%2CplayParams%2CdateAdded" - ); + let mfu = await app.mk.api.v3.music("/v1/me/library/playlists?platform=web&extend=editorialVideo&fields%5Bplaylists%5D=lastModifiedDate&filter%5Bfeatured%5D=made-for-you&include%5Blibrary-playlists%5D=catalog&fields%5Blibrary-playlists%5D=artwork%2Cname%2CplayParams%2CdateAdded"); this.madeforyou = mfu.data; } catch (e) { console.log(e); @@ -3187,10 +3338,7 @@ const app = new Vue({ 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 - ) { + 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"]; @@ -3880,15 +4028,22 @@ const app = new Vue({ if (e.keyCode == "40") { if (this.search.hints.length - 1 < this.search.cursor + 1) return; this.search.cursor++; - this.search.term = this.search.hints[this.search.cursor]; + let item = this.search.hints[this.search.cursor]; + this.search.term = item.content ? item.content?.attributes?.name ?? "" : item.displayTerm; } else if (e.keyCode == "38") { if (this.search.cursor == 0) return; this.search.cursor--; - this.search.term = this.search.hints[this.search.cursor]; + let item = this.search.hints[this.search.cursor]; + this.search.term = item.content ? item.content?.attributes?.name ?? "" : item.displayTerm; } }, async searchQuery(term = this.search.term) { let self = this; + if (typeof term === "object") { + this.routeView(term); + this.search.term = ""; + return; + } if (term == "") { return; } @@ -4016,23 +4171,28 @@ const app = new Vue({ } }, getMediaItemArtwork(url, height = 64, width) { - if (typeof url == "undefined" || url == "") { + try { + if (typeof url == "undefined" || url == "") { + return "./assets/MissingArtwork.svg"; + } + height = parseInt(height * window.devicePixelRatio); + if (width) { + width = parseInt(width * window.devicePixelRatio); + } + let newurl = `${(url ?? "") + .replace("{w}", width ?? height) + .replace("{h}", height) + .replace("{f}", "webp") + .replace("{c}", width === 900 || width === 380 || width === 600 ? "sr" : "cc")}`; + + if (newurl.includes("900x516")) { + newurl = newurl.replace("900x516cc", "900x516sr").replace("900x516bb", "900x516sr"); + } + return newurl; + } catch (e) { + console.log(url); return "./assets/MissingArtwork.svg"; } - height = parseInt(height * window.devicePixelRatio); - if (width) { - width = parseInt(width * window.devicePixelRatio); - } - let newurl = `${url - .replace("{w}", width ?? height) - .replace("{h}", height) - .replace("{f}", "webp") - .replace("{c}", width === 900 || width === 380 || width === 600 ? "sr" : "cc")}`; - - if (newurl.includes("900x516")) { - newurl = newurl.replace("900x516cc", "900x516sr").replace("900x516bb", "900x516sr"); - } - return newurl; }, _rgbToRgb(rgb = [0, 0, 0]) { // if rgb @@ -4091,13 +4251,7 @@ const app = new Vue({ } this.currentArtUrl = ""; this.currentArtUrlRaw = ""; - if ( - app.mk.nowPlayingItem != null && - app.mk.nowPlayingItem.attributes != null && - app.mk.nowPlayingItem.attributes.artwork != null && - app.mk.nowPlayingItem.attributes.artwork.url != null && - app.mk.nowPlayingItem.attributes.artwork.url != "" - ) { + if (app.mk.nowPlayingItem != null && app.mk.nowPlayingItem.attributes != null && app.mk.nowPlayingItem.attributes.artwork != null && app.mk.nowPlayingItem.attributes.artwork.url != null && app.mk.nowPlayingItem.attributes.artwork.url != "") { this.currentArtUrlRaw = this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? ""; this.currentArtUrl = (this.mk["nowPlayingItem"]["attributes"]["artwork"]["url"] ?? "").replace("{w}", artworkSize).replace("{h}", artworkSize); if (this.mk.nowPlayingItem._assets[0].artworkURL) { @@ -4115,7 +4269,7 @@ const app = new Vue({ if (this.mk.nowPlayingItem._assets[0].artworkURL) { this.currentArtUrl = this.mk.nowPlayingItem._assets[0].artworkURL; } - ipcRenderer.send("updateRPCImage", this.currentArtUrl ?? ""); + ipcRenderer.send("discordrpc:updateImage", this.currentArtUrl ?? ""); try { // document.querySelector('.app-playback-controls .artwork').style.setProperty('--artwork', `url("${this.currentArtUrl}")`); } catch (e) {} @@ -4333,24 +4487,62 @@ const app = new Vue({ // tracks are found in relationship.data }, - setAirPlayCodeUI() { + setAirPlayCodeUI(identifier) { this.modals.airplayPW = true; + this.currentAirPlayCodeID = identifier; }, - sendAirPlaySuccess() { - notyf.success("Device paired successfully!"); + sendAirPlaySuccess(silent = false, identifier = "") { + if (!silent) { + notyf.success("Device paired successfully!"); + } + console.log("delete idx-pre", identifier); + let idx = this.airplayTrys.findIndex((a) => { + return a.id == identifier; + }); + console.log("delete idx", idx); + if (idx != -1) delete this.airplayTrys[idx]; + this.airplayTrys = this.airplayTrys.filter((n) => n); }, sendAirPlayFailed() { notyf.success("Device paring failed!"); }, - airplayDisconnect(dropped, array = []) { - console.log("airplay dropped", dropped, array); - // if (dropped) { - // let [ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv] = array; - // ipcRenderer.send("performAirplayPCM", ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv); - // } else { - // app.activeCasts = []; - // notyf.error("Devices disconnected!"); - // } + airplayDisconnect(dropped, array = [], identifier = "") { + console.log("airplay dropped", dropped, array, identifier); + if (dropped) { + let [ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv] = array; + console.log(ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv); + let idx = this.airplayTrys.findIndex((a) => { + return a.id == ipv4 + ":" + ipport + "ap"; + }); + if (idx == -1) { + this.airplayTrys.push({ + id: ipv4 + ":" + ipport + "ap", + attempts: 1, + }); + } + idx = this.airplayTrys.findIndex((a) => { + return a.id == ipv4 + ":" + ipport + "ap"; + }); + if (this.airplayTrys[idx].attempts > 3) { + delete this.airplayTrys[idx]; + this.airplayTrys = this.airplayTrys.filter((n) => n); + console.log("delete idx", idx); + return; + } else { + this.airplayTrys[idx].attempts = this.airplayTrys[idx].attempts + 1; + setTimeout(() => { + ipcRenderer.send("performAirplayPCM", ipv4, ipport, sepassword, title, artist, album, artworkURL, txt, airplay2dv, true); + }, 1000); + } + } else { + if (identifier == "") { + app.activeCasts = []; + notyf.error("Devices disconnected!"); + } else { + app.activeCasts; + notyf.error("Device disconnected!"); + } + } }, windowFocus(val) { if (val) { @@ -4446,7 +4638,7 @@ const app = new Vue({ name: app.getLz("action.removeFromLibrary"), hidden: true, action: function () { - self.removeFromLibrary(); + self.removeFromLibrary(app.mk.nowPlayingItem.type, MusicKitInterop.getAttributes().songId); }, }, { @@ -4469,8 +4661,13 @@ const app = new Vue({ { icon: "./assets/feather/user.svg", name: app.getLz("action.goToArtist"), - action: function () { - app.appRoute(`artist/${app.mk.nowPlayingItem.relationships.artists.data[0].id}`); + action: async function () { + if (app.mk.nowPlayingItem.relationships.artists.data[0].id) { + app.appRoute(`artist/${app.mk.nowPlayingItem.relationships.artists.data[0].id}`); + } else { + const primaryArtist = await MusicKitInterop.fetchSongRelationships({ relationship: "primaryArtist" }); + app.appRoute(`artist/${primaryArtist.id}`); + } }, }, { @@ -4918,7 +5115,36 @@ const app = new Vue({ }); // Load first source let src = sources[0]; - app.mk._services.mediaItemPlayback._currentPlayer._playAssetURL(src, false); + if (src.includes("http")) { + app.mk._services.mediaItemPlayback._currentPlayer._playAssetURL(src, false); + } else { + if (Hls.isSupported()) { + let d = "WIDEVINE_SOFTWARE"; + let h = { + initDataTypes: ["cenc", "keyids"], + distinctiveIdentifier: "optional", + persistentState: "required", + }; + let p = { + platformInfo: { requiresCDMAttachOnStart: !0, maxSecurityLevel: d, keySystemConfig: h }, + appData: { serviceName: "Apple Music" }, + }; + if (app.radiohls != null && app.radiohls.destroy != null) { + app.radiohls.destroy(); + app.radiohls = null; + app.radiohls = new CiderHls(); + app.radiohls.loadSource(e); + app.radiohls.attachMedia(app.mk._services.mediaItemPlayback._currentPlayer._targetElement); + app.mk._services.mediaItemPlayback._currentPlayer._targetElement.play(); + } else { + app.radiohls = null; + app.radiohls = new CiderHls(); + app.radiohls.loadSource(e); + app.radiohls.attachMedia(app.mk._services.mediaItemPlayback._currentPlayer._targetElement); + app.mk._services.mediaItemPlayback._currentPlayer._targetElement.play(); + } + } + } } } }, diff --git a/src/renderer/style.less b/src/renderer/style.less index 23014cf2..3403196e 100644 --- a/src/renderer/style.less +++ b/src/renderer/style.less @@ -724,7 +724,7 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { .search-hints-container { top: 44px; - background: rgb(30 30 30); + // background: rgb(30 30 30); #cmenu.container(); .search-hints { @@ -1339,6 +1339,12 @@ body[platform="darwin"] .app-chrome .app-chrome-item > .window-controls > div.cl flex: 1; } + .live-icon { + filter: none !important; + margin-left: 0.5em !important; + flex: 1; + } + .chrome-icon-container { display: flex; position: absolute; diff --git a/src/renderer/views/app/chrome-bottom.ejs b/src/renderer/views/app/chrome-bottom.ejs index dab5dcb8..02cd2643 100644 --- a/src/renderer/views/app/chrome-bottom.ejs +++ b/src/renderer/views/app/chrome-bottom.ejs @@ -69,6 +69,7 @@ :title="$root.getLz('settings.option.audio.enableAdvancedFunctionality.ciderPPE')" v-b-tooltip.hover > + @@ -101,10 +102,8 @@ - - {{ convertTime(getSongProgress()) }} - - --:-- + + {{ convertTime(getSongProgress()) }} - + + - + {{ convertTime(getSongProgress()) }} @@ -277,10 +278,10 @@ - {search.showHints = false}, 300)" - v-on:keyup.enter="searchQuery();search.showHints = false;search.cursor = -1" @change="$root.appRoute('search');" + @blur="setTimeout(()=>{if(hintscontext != true){search.showHints = false} }, 300)" + v-on:keyup.enter="searchQuery(search.hints[search.cursor]?.content ?? search.hints[search.cursor]?.searchTerm ?? search.term);search.showHints = false;search.showSearchView = true;search.cursor = -1" @change="$root.appRoute('search');" v-on:keyup="searchCursor" @input="getSearchHints()" :placeholder="$root.getLz('term.search') + '...'" v-model="search.term" ref="searchInput" @@ -288,10 +289,13 @@ - - {{ hint }} + + {{ hint.displayTerm }} + + + diff --git a/src/renderer/views/components/add-to-playlist.ejs b/src/renderer/views/components/add-to-playlist.ejs index 9789b37c..d3f778d6 100644 --- a/src/renderer/views/components/add-to-playlist.ejs +++ b/src/renderer/views/components/add-to-playlist.ejs @@ -13,7 +13,7 @@ {{app.getLz('action.createPlaylist')}} + v-for="item in $root.getPlaylistFolderChildren('p.playlistsroot')" v-bind:key="item.id" :item="item"> diff --git a/src/renderer/views/components/airplay-modal.ejs b/src/renderer/views/components/airplay-modal.ejs index 43ecc3e2..1d14ea76 100644 --- a/src/renderer/views/components/airplay-modal.ejs +++ b/src/renderer/views/components/airplay-modal.ejs @@ -37,7 +37,7 @@ }, enterPassword() { console.log('Entered passCode: ', this.passcode) - ipcRenderer.send("setAirPlayPasscode", this.passcode) + ipcRenderer.send("setAirPlayPasscode", this.passcode, this.$root.currentAirPlayCodeID) this.close() } } diff --git a/src/renderer/views/components/castmenu.ejs b/src/renderer/views/components/castmenu.ejs index ff2c0256..12381625 100644 --- a/src/renderer/views/components/castmenu.ejs +++ b/src/renderer/views/components/castmenu.ejs @@ -54,9 +54,13 @@ {{ device.host }} - Connected + + + + + @@ -135,8 +139,27 @@ ipcRenderer.send('performGCCast', device, "Cider", "Playing ...", "Test build", ''); }, setAirPlayCast(device) { + if (!this.activeCasts.some(item => { return item.host == device.host && item.name == device.name && item.port == device.port})) { this.activeCasts.push(device); - ipcRenderer.send("performAirplayPCM", device.host, device.port, null, "", "", "", "", device.txt, device.airplay2) + ipcRenderer.send("performAirplayPCM", device.host, device.port, null, "", "", "", "", device.txt, device.airplay2, false)} + }, + disconnectAirPlayCast(device) { + app.confirm("Do you want to disconnect this device?",(res) => { + if (res){ + ipcRenderer.send('disconnectAirplay', device.host +":"+ device.port+"ap"); + console.log('disconnectAirplay', device.host +":"+ device.port+"ap") + let idx = this.activeCasts.findIndex(((a) => {return a.host == device.host && a.port == device.port})) + console.log(idx) + if (idx != -1) { + delete this.activeCasts[idx] + delete this.$root.activeCasts[idx] + this.activeCasts = this.activeCasts.filter(a => {return !(a.host == device.host && a.port == device.port)}) + console.log(this.activeCasts) + if (this.activeCasts.length == 0){ + this.stopCasting() + } + }} + }) }, stopCasting() { CiderAudio.stopAudio(); diff --git a/src/renderer/views/components/fullscreen.ejs b/src/renderer/views/components/fullscreen.ejs index 74ccb487..728710aa 100644 --- a/src/renderer/views/components/fullscreen.ejs +++ b/src/renderer/views/components/fullscreen.ejs @@ -39,7 +39,7 @@ - + + diff --git a/src/renderer/views/components/listitem-horizontal.ejs b/src/renderer/views/components/listitem-horizontal.ejs index 3c12ee01..6a09353f 100644 --- a/src/renderer/views/components/listitem-horizontal.ejs +++ b/src/renderer/views/components/listitem-horizontal.ejs @@ -4,6 +4,7 @@ diff --git a/src/renderer/views/components/mediaitem-list-item.ejs b/src/renderer/views/components/mediaitem-list-item.ejs index 532e0154..e7c0ad5e 100644 --- a/src/renderer/views/components/mediaitem-list-item.ejs +++ b/src/renderer/views/components/mediaitem-list-item.ejs @@ -494,6 +494,10 @@ "name": `${app.getLz('action.share')} (song.link)`, "action": async function() { let item = self.item + if(item.type.startsWith('library-')) { + self.item.attributes.url = self.item.relationships.catalog.data[0].attributes.url + item.attributes.url = item.relationships.catalog.data[0].attributes.url + } if (!item.attributes.url) { if (item.type.includes("library")) { let result = (await app.mk.api.v3.music(`/v1/me/library/${item.type.replace("library-", '')}/${item.id}/catalog`)).data.data[0] diff --git a/src/renderer/views/components/mediaitem-mvview-sp.ejs b/src/renderer/views/components/mediaitem-mvview-sp.ejs index ea0bc5aa..1c938e8a 100644 --- a/src/renderer/views/components/mediaitem-mvview-sp.ejs +++ b/src/renderer/views/components/mediaitem-mvview-sp.ejs @@ -1,5 +1,5 @@ + diff --git a/src/renderer/views/main.ejs b/src/renderer/views/main.ejs index 970bab81..6c5ddf4b 100644 --- a/src/renderer/views/main.ejs +++ b/src/renderer/views/main.ejs @@ -63,7 +63,6 @@ - diff --git a/src/renderer/views/pages/apple-account-settings.ejs b/src/renderer/views/pages/apple-account-settings.ejs index 6b970995..72634919 100644 --- a/src/renderer/views/pages/apple-account-settings.ejs +++ b/src/renderer/views/pages/apple-account-settings.ejs @@ -1,7 +1,7 @@ diff --git a/src/renderer/views/pages/apple-curator.ejs b/src/renderer/views/pages/apple-curator.ejs index 0e11bea5..f6e9d055 100644 --- a/src/renderer/views/pages/apple-curator.ejs +++ b/src/renderer/views/pages/apple-curator.ejs @@ -1,6 +1,6 @@ diff --git a/src/renderer/views/pages/artist-feed.ejs b/src/renderer/views/pages/artist-feed.ejs index 24e7e1ae..2a42bdb5 100644 --- a/src/renderer/views/pages/artist-feed.ejs +++ b/src/renderer/views/pages/artist-feed.ejs @@ -41,7 +41,7 @@ - + diff --git a/src/renderer/views/pages/cider-multiroom.ejs b/src/renderer/views/pages/cider-multiroom.ejs index 6fdbf4d2..e333b11a 100644 --- a/src/renderer/views/pages/cider-multiroom.ejs +++ b/src/renderer/views/pages/cider-multiroom.ejs @@ -8,7 +8,22 @@ {{data.attributes?.title ?? ""}} - + + + + + + + @@ -37,7 +52,7 @@ :items="recom.relationships.children ? recom.relationships.children.data.limit(10) : recom.relationships.contents.data.limit(10)"> - + diff --git a/src/renderer/views/pages/cider-playlist.ejs b/src/renderer/views/pages/cider-playlist.ejs index 5169f9bd..e67e0093 100644 --- a/src/renderer/views/pages/cider-playlist.ejs +++ b/src/renderer/views/pages/cider-playlist.ejs @@ -243,6 +243,7 @@ :showIndex="true" :showIndexPlaylist="(data.attributes.playParams?.kind ?? data.type ?? '').includes('playlist')" :context-ext="buildContextMenu()" + v-bind:key="item.id" v-for="(item,index) in currentSlice"> @@ -255,6 +256,7 @@ :showIndex="true" :showIndexPlaylist="(data.attributes.playParams?.kind ?? data.type ?? '').includes('playlist')" :context-ext="buildContextMenu()" + v-bind:key="item.id" v-for="(item,index) in disc.tracks"> @@ -645,6 +647,7 @@ return { normal: [ { + icon: "./assets/feather/x-circle.svg", name: app.getLz('action.removeFromPlaylist'), action: () => { self.remove() @@ -653,6 +656,7 @@ ], multiple: [ { + icon: "./assets/feather/x-circle.svg", name: app.getLz('action.removeFromPlaylist'), action: () => { self.remove() diff --git a/src/renderer/views/pages/home.ejs b/src/renderer/views/pages/home.ejs index 89e15a27..204f2630 100644 --- a/src/renderer/views/pages/home.ejs +++ b/src/renderer/views/pages/home.ejs @@ -17,7 +17,7 @@ + :item="item" v-bind:key="item.id"> @@ -39,7 +39,7 @@ - + {{app.getLz('home.artistsFeed.noArtist')}} diff --git a/src/renderer/views/pages/library-albums.ejs b/src/renderer/views/pages/library-albums.ejs index f63cd631..c91fbad4 100644 --- a/src/renderer/views/pages/library-albums.ejs +++ b/src/renderer/views/pages/library-albums.ejs @@ -81,7 +81,7 @@ + :show-library-status="false" v-bind:key="item.id" :item="item" v-for="item in currentSlice"> diff --git a/src/renderer/views/pages/library-recentlyadded.ejs b/src/renderer/views/pages/library-recentlyadded.ejs index ff5f58da..33d9ae9f 100644 --- a/src/renderer/views/pages/library-recentlyadded.ejs +++ b/src/renderer/views/pages/library-recentlyadded.ejs @@ -2,11 +2,11 @@ {{$root.getLz('term.recentlyAdded')}} - + + :item="item" v-bind:key="item.id"> diff --git a/src/renderer/views/pages/library-songs.ejs b/src/renderer/views/pages/library-songs.ejs index c18c7902..7f14e3ca 100644 --- a/src/renderer/views/pages/library-songs.ejs +++ b/src/renderer/views/pages/library-songs.ejs @@ -84,12 +84,12 @@ Library contains no songs. diff --git a/src/renderer/views/pages/madeforyou.ejs b/src/renderer/views/pages/madeforyou.ejs index bdbaebad..0428c772 100644 --- a/src/renderer/views/pages/madeforyou.ejs +++ b/src/renderer/views/pages/madeforyou.ejs @@ -5,7 +5,7 @@ - + diff --git a/src/renderer/views/pages/oobe.ejs b/src/renderer/views/pages/oobe.ejs index e4486f35..88057756 100644 --- a/src/renderer/views/pages/oobe.ejs +++ b/src/renderer/views/pages/oobe.ejs @@ -153,7 +153,9 @@ diff --git a/src/renderer/views/pages/playlist-inline.ejs b/src/renderer/views/pages/playlist-inline.ejs index 056337db..1c306440 100644 --- a/src/renderer/views/pages/playlist-inline.ejs +++ b/src/renderer/views/pages/playlist-inline.ejs @@ -171,6 +171,7 @@ :showIndex="true" :showIndexPlaylist="(data.attributes.playParams.kind ?? data.type ?? '').includes('playlist')" :context-ext="buildContextMenu()" + v-bind:key="item.id" v-for="(item,index) in data.relationships.tracks.data"> @@ -183,6 +184,7 @@ :showIndex="true" :showIndexPlaylist="(data.attributes.playParams.kind ?? data.type ?? '').includes('playlist')" :context-ext="buildContextMenu()" + v-bind:key="item.id" v-for="(item,index) in disc.tracks"> @@ -197,6 +199,7 @@ { self.remove() @@ -451,6 +455,7 @@ ], multiple: [ { + icon: "./assets/feather/x-circle.svg", name: app.getLz('action.removeFromPlaylist'), action: () => { self.remove() diff --git a/src/renderer/views/pages/radio.ejs b/src/renderer/views/pages/radio.ejs index 793f8a27..0d029f53 100644 --- a/src/renderer/views/pages/radio.ejs +++ b/src/renderer/views/pages/radio.ejs @@ -1,28 +1,76 @@ @@ -33,59 +81,30 @@ data: function() { return { app: this.$root, - radio: { personal: [], recent: [], am: [] } - } + recent: [] + }; }, - async mounted() { - this.radio.personal = await this.getPersonalStations() - this.radio.recent = await this.getRecentStations() - this.radio.am = await this.getAmStations() - console.log(this.radio) - // this.getPersonalStations(); - // this.getAmStations(); + mounted() { + this.$root.getRadioPage(); + // debugger + this.getRecentlyPlayed(); + // debugger }, methods: { - async getPersonalStations(attempts = 0) { - if (attempts > 3) { - return [] + getRecentlyPlayed: async function (next = null) { + const recent = await app.mk.api.v3.music(`${next ?? "/v1/me/recent/radio-stations"}`, { + "platform": "web", + "art[url]": "f", + l: app.mklang + }) + + console.debug(recent.data.data) + this.recent = this.recent.concat(recent.data.data); + + if (recent.data.next) { + this.getRecentlyPlayed(recent.data.next); } - try { - return (await app.mk.api.v3.music(`/v1/catalog/${app.mk.api.v3.storefrontId}/stations`, { - "filter[identity]": "personal", - })).data.data - } catch (e) { - console.error(`Failed to get personal stations: ${e}`) - await this.getPersonalStations(attempts + 1) - } - }, - async getRecentStations(attempts = 0) { - if (attempts > 3) { - return [] - } - try { - return (await app.mk.api.v3.music(`/v1/me/recent/radio-stations`, { - "platform": "web", - "art[url]": "f", - l: app.mklang - })).data.data - } catch (e) { - console.error(`Failed to get recent stations: ${e}`) - await this.getRecentStations(attempts + 1) - } - }, - async getAmStations(attempt = 0) { - if (attempt > 3) { - return [] - } - try { - return (await app.mk.api.v3.music(`/v1/catalog/${app.mk.api.v3.storefrontId}/stations`, { - "filter[featured]": "apple-music-live-radio", - })).data.data - } catch (e) { - console.error(`Failed to get AM stations: ${e}`) - await this.getAmStations(attempt + 1) - } - }, + } } - }) + }); diff --git a/src/renderer/views/pages/replay.ejs b/src/renderer/views/pages/replay.ejs index 8550a66a..1d5e7f3d 100644 --- a/src/renderer/views/pages/replay.ejs +++ b/src/renderer/views/pages/replay.ejs @@ -23,8 +23,10 @@ - {{ convertToHours(loaded.attributes.listenTimeInMinutes) }} - {{$root.getLz('term.time.hours')}} + {{convertToHours(loaded.attributes.listenTimeInMinutes)}} + {{$root.getLz('term.time.hours')}} + {{hourshow ? "" : (loaded.attributes.listenTimeInMinutes % 60) }} + {{hourshow ? "" : $root.getLz('term.time.minutes')}} {{ loaded.attributes.uniqueAlbumCount }} {{$root.getLz('term.uniqueAlbums')}} {{ loaded.attributes.uniqueArtistCount }} {{$root.getLz('term.uniqueArtists')}} {{ loaded.attributes.uniqueSongCount }} {{$root.getLz('term.uniqueSongs')}} @@ -101,6 +103,7 @@ loaded: { id: -1 }, + hourshow: true, musicTypeGenre: "" } }, diff --git a/src/renderer/views/pages/search.ejs b/src/renderer/views/pages/search.ejs index 0bdd63de..79e13093 100644 --- a/src/renderer/views/pages/search.ejs +++ b/src/renderer/views/pages/search.ejs @@ -3,18 +3,22 @@ {$root.search.showHints = false}, 300)" - v-on:keyup.enter="$root.searchQuery();$root.search.showHints = false" @input="$root.getSearchHints()" + @blur="$root.setTimeout(()=>{if($root.hintscontext != true){$root.search.showHints = false} }, 300)" + v-on:keyup.enter="$root.searchQuery($root.search.hints[$root.search.cursor]?.content ?? $root.search.hints[$root.search.cursor]?.searchTerm ?? $root.search.term);$root.search.showHints = false; $root.search.showSearchView = true" @input="$root.getSearchHints()" :placeholder="$root.getLz('term.search') + '...'" v-model="$root.search.term" class="search-input" /> - - {{ hint }} + + {{ hint.displayTerm }} + + + + @@ -29,16 +33,12 @@ $root.getLz("term.library") }} - + {{app.getLz('term.topResult')}} + :items="search?.results[search?.results?.meta?.results?.order[0]]?.data"> - - {{app.getLz('error.noResults')}} - {{app.getLz('error.noResults.description')}} - @@ -55,6 +55,10 @@ + + {{app.getLz('error.noResults')}} + {{app.getLz('error.noResults.description')}} + @@ -131,21 +135,21 @@ + v-if="categoriesView != null && categoriesView != [] && categoriesView[0]?.attributes != null && categoriesView[0]?.attributes.title != null"> {{$root.getLz('home.recentlyPlayed')}} - {{categoriesView[0].attributes.title.stringForDisplay ?? ""}} + {{categoriesView[0]?.attributes?.title?.stringForDisplay ?? ""}} - + v-for="item of getFlattenedCategories()"/> diff --git a/src/web-remote/assets/MissingArtwork.svg b/src/web-remote/assets/MissingArtwork.svg new file mode 100644 index 00000000..cca3ca5b --- /dev/null +++ b/src/web-remote/assets/MissingArtwork.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/web-remote/style.css b/src/web-remote/style.css index 8ead094a..9c1ce5ab 100644 --- a/src/web-remote/style.css +++ b/src/web-remote/style.css @@ -507,6 +507,11 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { cursor: pointer; } +.list-entry.artist-song { + padding: 5px; + width: 100%; +} + .list-entry-image { --artwork: url(""); width: 64px; @@ -519,6 +524,11 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { box-shadow: var(--mediaItemShadow); } +.artist-song .list-entry-image { + width: 45px; + height: 45px; +} + .list-entry-image.artist { border-radius: 50%; } @@ -586,6 +596,10 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { width: 100%; } +.mediaitem-scroller-horizontal::-webkit-scrollbar { + display: none; +} + .album-placeholder { height: 180px; width: 180px; @@ -643,6 +657,10 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { overflow-y: hidden; } +.search-tab-container::-webkit-scrollbar { + display: none; +} + .search-body-container { position: relative; width: 100%; @@ -704,7 +722,7 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { } .albumpage-album-notes > .notes-preview { - height: 60px; + height: 48px; overflow: hidden; -webkit-mask-image: -webkit-gradient(linear, left 95%, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); } @@ -757,13 +775,13 @@ input[type="range"].web-slider::-webkit-slider-runnable-track { } .artist-header { - height: 400px; + /* height: 400px; */ width: 100%; - margin: 12px auto; + /* margin: 12px auto; */ display: flex; justify-content: center; align-items: center; - padding: 12px; + /* padding: 12px; */ flex-direction: column; background: rgb(0 0 0 / 40%); } diff --git a/src/web-remote/views/components/mediaitem-artwork.ejs b/src/web-remote/views/components/mediaitem-artwork.ejs index bac5cfdd..e4760eb8 100644 --- a/src/web-remote/views/components/mediaitem-artwork.ejs +++ b/src/web-remote/views/components/mediaitem-artwork.ejs @@ -1,7 +1,7 @@
{{ convertTime(getSongProgress()) }}
{{app.getLz('error.noResults.description')}}