mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-12 11:01:45 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa160b2e90 | |||
| 308ac38e6b | |||
| a62cafb601 | |||
| bf9e3b5f48 | |||
| 3c6b3aeff0 | |||
| 37181a7b5e | |||
| 0b363d6487 | |||
| e9398adac3 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -117,7 +117,7 @@ jobs:
|
|||||||
if: ${{ env.VERSION_HASH == '' }}
|
if: ${{ env.VERSION_HASH == '' }}
|
||||||
uses: irongut/EditRelease@v1.2.0
|
uses: irongut/EditRelease@v1.2.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
id: ${{ steps.get_draft_release.outputs.id }}
|
id: ${{ steps.get_draft_release.outputs.id }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|||||||
20
.github/workflows/winget-cla.yml
vendored
Normal file
20
.github/workflows/winget-cla.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: Submit CLA to Winget PR
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
pr_url:
|
||||||
|
description: "Specific PR URL"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
name: Comment to PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Submit CLA to Windows Package Manager Community Repository Pull Request
|
||||||
|
run: gh pr comment $PR_URL --body "@microsoft-github-policy-service agree"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.WINGET_ACC_TOKEN }}
|
||||||
|
PR_URL: ${{ inputs.pr_url }}
|
||||||
2
.github/workflows/winget-submission.yml
vendored
2
.github/workflows/winget-submission.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
uses: vedantmgoyal2009/winget-releaser@v2
|
uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
with:
|
with:
|
||||||
identifier: th-ch.YouTubeMusic
|
identifier: th-ch.YouTubeMusic
|
||||||
installers-regex: '^YouTube-Music-Setup-[\d\.]+\.exe$'
|
installers-regex: '^YouTube-Music-Web-Setup-[\d\.]+\.exe$'
|
||||||
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
||||||
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
||||||
token: ${{ secrets.WINGET_ACC_TOKEN }}
|
token: ${{ secrets.WINGET_ACC_TOKEN }}
|
||||||
|
|||||||
10
changelog.md
10
changelog.md
@ -2,8 +2,18 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||||
|
|
||||||
|
#### [v2.0.4](https://github.com/th-ch/youtube-music/compare/v2.0.3...v2.0.4)
|
||||||
|
|
||||||
|
- hotfix(adblocker): fix `ipcRenderer.sendSync() with ...` [`#1301`](https://github.com/th-ch/youtube-music/pull/1301)
|
||||||
|
- fix(downloader): Korean filename is broken on non-macOS devices [`#1297`](https://github.com/th-ch/youtube-music/pull/1297)
|
||||||
|
- chore(deps): bump deps [`b6894dc`](https://github.com/th-ch/youtube-music/commit/b6894dca2974c63fa2945d3a4995665d11eb2a78)
|
||||||
|
- fix: bump dependencies [`7aa970c`](https://github.com/th-ch/youtube-music/commit/7aa970cebc8e1407ff6937b402ba303e14c73efd)
|
||||||
|
- fix(downloader): private playlist download [`1d5b299`](https://github.com/th-ch/youtube-music/commit/1d5b2997bd0c72c1c007c57b145509e4a8f77fef)
|
||||||
|
|
||||||
#### [v2.0.3](https://github.com/th-ch/youtube-music/compare/v2.0.2...v2.0.3)
|
#### [v2.0.3](https://github.com/th-ch/youtube-music/compare/v2.0.2...v2.0.3)
|
||||||
|
|
||||||
|
> 10 October 2023
|
||||||
|
|
||||||
- feat(discord): add `Hide GitHub link Button` [`#1293`](https://github.com/th-ch/youtube-music/pull/1293)
|
- feat(discord): add `Hide GitHub link Button` [`#1293`](https://github.com/th-ch/youtube-music/pull/1293)
|
||||||
- feat(deps): bundle `youtubei.js` (temporary solution) [`#1292`](https://github.com/th-ch/youtube-music/pull/1292)
|
- feat(deps): bundle `youtubei.js` (temporary solution) [`#1292`](https://github.com/th-ch/youtube-music/pull/1292)
|
||||||
- fix(mpris): fixed an issue where MPRIS information was incorrect [`#1291`](https://github.com/th-ch/youtube-music/pull/1291)
|
- fix(mpris): fixed an issue where MPRIS information was incorrect [`#1291`](https://github.com/th-ch/youtube-music/pull/1291)
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { blockers } from '../plugins/adblocker/blocker-types';
|
import { blockers } from '../plugins/adblocker/blocker-types';
|
||||||
|
|
||||||
|
import { DefaultPresetList } from '../plugins/downloader/types';
|
||||||
|
|
||||||
export interface WindowSizeConfig {
|
export interface WindowSizeConfig {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@ -111,14 +113,19 @@ const defaultConfig = {
|
|||||||
},
|
},
|
||||||
'downloader': {
|
'downloader': {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
ffmpegArgs: ['-b:a', '256k'], // E.g. ["-b:a", "192k"] for an audio bitrate of 192kb/s
|
|
||||||
downloadFolder: undefined as string | undefined, // Custom download folder (absolute path)
|
downloadFolder: undefined as string | undefined, // Custom download folder (absolute path)
|
||||||
preset: 'mp3',
|
selectedPreset: 'mp3 (256kbps)', // Selected preset
|
||||||
|
customPresetSetting: DefaultPresetList['mp3 (256kbps)'], // Presets
|
||||||
skipExisting: false,
|
skipExisting: false,
|
||||||
playlistMaxItems: undefined as number | undefined,
|
playlistMaxItems: undefined as number | undefined,
|
||||||
},
|
},
|
||||||
'exponential-volume': {},
|
'exponential-volume': {},
|
||||||
'in-app-menu': {},
|
'in-app-menu': {
|
||||||
|
/**
|
||||||
|
* true in Windows, false in Linux and macOS (see youtube-music/config/store.ts)
|
||||||
|
*/
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
'last-fm': {
|
'last-fm': {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
token: undefined as string | undefined, // Token used for authentication
|
token: undefined as string | undefined, // Token used for authentication
|
||||||
|
|||||||
@ -1,8 +1,18 @@
|
|||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
import Conf from 'conf';
|
import Conf from 'conf';
|
||||||
|
import is from 'electron-is';
|
||||||
|
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
|
|
||||||
|
import { DefaultPresetList, type Preset } from '../plugins/downloader/types';
|
||||||
|
|
||||||
|
const getDefaults = () => {
|
||||||
|
if (is.windows()) {
|
||||||
|
defaults.plugins['in-app-menu'].enabled = true;
|
||||||
|
}
|
||||||
|
return defaults;
|
||||||
|
};
|
||||||
|
|
||||||
const setDefaultPluginOptions = (store: Conf<Record<string, unknown>>, plugin: keyof typeof defaults.plugins) => {
|
const setDefaultPluginOptions = (store: Conf<Record<string, unknown>>, plugin: keyof typeof defaults.plugins) => {
|
||||||
if (!store.get(`plugins.${plugin}`)) {
|
if (!store.get(`plugins.${plugin}`)) {
|
||||||
store.set(`plugins.${plugin}`, defaults.plugins[plugin]);
|
store.set(`plugins.${plugin}`, defaults.plugins[plugin]);
|
||||||
@ -10,6 +20,26 @@ const setDefaultPluginOptions = (store: Conf<Record<string, unknown>>, plugin: k
|
|||||||
};
|
};
|
||||||
|
|
||||||
const migrations = {
|
const migrations = {
|
||||||
|
'>=2.1.0'(store: Conf<Record<string, unknown>>) {
|
||||||
|
const originalPreset = store.get('plugins.downloader.preset') as string | undefined;
|
||||||
|
if (originalPreset) {
|
||||||
|
if (originalPreset !== 'opus') {
|
||||||
|
store.set('plugins.downloader.selectedPreset', 'Custom');
|
||||||
|
store.set('plugins.downloader.customPresetSetting', {
|
||||||
|
extension: 'mp3',
|
||||||
|
ffmpegArgs: store.get('plugins.downloader.ffmpegArgs') as string[] ?? DefaultPresetList['mp3 (256kbps)'].ffmpegArgs,
|
||||||
|
} satisfies Preset);
|
||||||
|
} else {
|
||||||
|
store.set('plugins.downloader.selectedPreset', 'Source');
|
||||||
|
store.set('plugins.downloader.customPresetSetting', {
|
||||||
|
extension: null,
|
||||||
|
ffmpegArgs: store.get('plugins.downloader.ffmpegArgs') as string[] ?? [],
|
||||||
|
} satisfies Preset);
|
||||||
|
}
|
||||||
|
store.delete('plugins.downloader.preset');
|
||||||
|
store.delete('plugins.downloader.ffmpegArgs');
|
||||||
|
}
|
||||||
|
},
|
||||||
'>=1.20.0'(store: Conf<Record<string, unknown>>) {
|
'>=1.20.0'(store: Conf<Record<string, unknown>>) {
|
||||||
setDefaultPluginOptions(store, 'visualizer');
|
setDefaultPluginOptions(store, 'visualizer');
|
||||||
|
|
||||||
@ -118,7 +148,7 @@ const migrations = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default new Store({
|
export default new Store({
|
||||||
defaults,
|
defaults: getDefaults(),
|
||||||
clearInvalidConfig: false,
|
clearInvalidConfig: false,
|
||||||
migrations,
|
migrations,
|
||||||
});
|
});
|
||||||
|
|||||||
46
package-lock.json
generated
46
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "youtube-music",
|
"name": "youtube-music",
|
||||||
"version": "2.0.4",
|
"version": "2.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "youtube-music",
|
"name": "youtube-music",
|
||||||
"version": "2.0.4",
|
"version": "2.1.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"ytpl": "2.3.0"
|
"ytpl": "2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.38.1",
|
"@playwright/test": "1.39.0",
|
||||||
"@rollup/plugin-commonjs": "25.0.5",
|
"@rollup/plugin-commonjs": "25.0.5",
|
||||||
"@rollup/plugin-image": "3.0.3",
|
"@rollup/plugin-image": "3.0.3",
|
||||||
"@rollup/plugin-json": "6.0.1",
|
"@rollup/plugin-json": "6.0.1",
|
||||||
@ -65,10 +65,10 @@
|
|||||||
"eslint-plugin-prettier": "5.0.1",
|
"eslint-plugin-prettier": "5.0.1",
|
||||||
"node-gyp": "9.4.0",
|
"node-gyp": "9.4.0",
|
||||||
"patch-package": "8.0.0",
|
"patch-package": "8.0.0",
|
||||||
"playwright": "1.38.1",
|
"playwright": "1.39.0",
|
||||||
"rollup": "4.0.2",
|
"rollup": "4.0.2",
|
||||||
"rollup-plugin-copy": "3.5.0",
|
"rollup-plugin-copy": "3.5.0",
|
||||||
"rollup-plugin-import-css": "3.3.4",
|
"rollup-plugin-import-css": "3.3.5",
|
||||||
"rollup-plugin-string": "3.0.0",
|
"rollup-plugin-string": "3.0.0",
|
||||||
"typescript": "5.2.2"
|
"typescript": "5.2.2"
|
||||||
},
|
},
|
||||||
@ -269,9 +269,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.23.1",
|
"version": "7.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
|
||||||
"integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==",
|
"integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
},
|
},
|
||||||
@ -1163,12 +1163,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.38.1",
|
"version": "1.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz",
|
||||||
"integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
|
"integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.38.1"
|
"playwright": "1.39.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@ -8060,12 +8060,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.38.1",
|
"version": "1.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz",
|
||||||
"integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
|
"integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.38.1"
|
"playwright-core": "1.39.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@ -8078,9 +8078,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.38.1",
|
"version": "1.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
|
||||||
"integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
|
"integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
@ -8574,9 +8574,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup-plugin-import-css": {
|
"node_modules/rollup-plugin-import-css": {
|
||||||
"version": "3.3.4",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/rollup-plugin-import-css/-/rollup-plugin-import-css-3.3.5.tgz",
|
||||||
"integrity": "sha512-w5p1Dd1CavAht/P82zB3WX2RVy7O47MlJGSmgrWXTBPAkWHTbOBh/nUPz94IczCD0HLxpuT4AhF24cix7CpZWA==",
|
"integrity": "sha512-wSfzveEzvUDlVevo70kmVD5Mk785UN55NG4C7VVnrmdE0qZ8apcVVFajyCPfFYSNxq5YkccOcrGUT2T/2HnEcQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rollup/pluginutils": "^5.0.4"
|
"@rollup/pluginutils": "^5.0.4"
|
||||||
@ -8585,7 +8585,7 @@
|
|||||||
"node": ">=16"
|
"node": ">=16"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"rollup": "^2.x.x || ^3.x.x"
|
"rollup": "^2.x.x || ^3.x.x || ^4.x.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup-plugin-string": {
|
"node_modules/rollup-plugin-string": {
|
||||||
|
|||||||
10
package.json
10
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "youtube-music",
|
"name": "youtube-music",
|
||||||
"productName": "YouTube Music",
|
"productName": "YouTube Music",
|
||||||
"version": "2.0.4",
|
"version": "2.1.0",
|
||||||
"description": "YouTube Music Desktop App - including custom plugins",
|
"description": "YouTube Music Desktop App - including custom plugins",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -150,10 +150,10 @@
|
|||||||
"xml2js": "0.6.2",
|
"xml2js": "0.6.2",
|
||||||
"node-fetch": "2.7.0",
|
"node-fetch": "2.7.0",
|
||||||
"@electron/universal": "1.4.2",
|
"@electron/universal": "1.4.2",
|
||||||
"@babel/runtime": "7.23.1"
|
"@babel/runtime": "7.23.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "1.38.1",
|
"@playwright/test": "1.39.0",
|
||||||
"@rollup/plugin-commonjs": "25.0.5",
|
"@rollup/plugin-commonjs": "25.0.5",
|
||||||
"@rollup/plugin-image": "3.0.3",
|
"@rollup/plugin-image": "3.0.3",
|
||||||
"@rollup/plugin-json": "6.0.1",
|
"@rollup/plugin-json": "6.0.1",
|
||||||
@ -176,10 +176,10 @@
|
|||||||
"eslint-plugin-prettier": "5.0.1",
|
"eslint-plugin-prettier": "5.0.1",
|
||||||
"node-gyp": "9.4.0",
|
"node-gyp": "9.4.0",
|
||||||
"patch-package": "8.0.0",
|
"patch-package": "8.0.0",
|
||||||
"playwright": "1.38.1",
|
"playwright": "1.39.0",
|
||||||
"rollup": "4.0.2",
|
"rollup": "4.0.2",
|
||||||
"rollup-plugin-copy": "3.5.0",
|
"rollup-plugin-copy": "3.5.0",
|
||||||
"rollup-plugin-import-css": "3.3.4",
|
"rollup-plugin-import-css": "3.3.5",
|
||||||
"rollup-plugin-string": "3.0.0",
|
"rollup-plugin-string": "3.0.0",
|
||||||
"typescript": "5.2.2"
|
"typescript": "5.2.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -8,12 +8,11 @@ import is from 'electron-is';
|
|||||||
import filenamify from 'filenamify';
|
import filenamify from 'filenamify';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
import { createFFmpeg } from '@ffmpeg.wasm/main';
|
import { createFFmpeg } from '@ffmpeg.wasm/main';
|
||||||
|
|
||||||
import NodeID3, { TagConstants } from 'node-id3';
|
import NodeID3, { TagConstants } from 'node-id3';
|
||||||
|
|
||||||
import { cropMaxWidth, getFolder, presets, sendFeedback as sendFeedback_, setBadge } from './utils';
|
import { cropMaxWidth, getFolder, sendFeedback as sendFeedback_, setBadge } from './utils';
|
||||||
|
|
||||||
import config from './config';
|
import config from './config';
|
||||||
|
import { YoutubeFormatList, type Preset, DefaultPresetList } from './types';
|
||||||
|
|
||||||
import style from './style.css';
|
import style from './style.css';
|
||||||
|
|
||||||
@ -221,13 +220,32 @@ async function downloadSongUnsafe(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const preset = config.get('preset') ?? 'mp3';
|
const selectedPreset = config.get('selectedPreset') ?? 'mp3 (256kbps)';
|
||||||
let presetSetting: { extension: string; ffmpegArgs: string[] } | null = null;
|
let presetSetting: Preset;
|
||||||
if (preset === 'opus') {
|
if (selectedPreset === 'Custom') {
|
||||||
presetSetting = presets[preset];
|
presetSetting = config.get('customPresetSetting') ?? DefaultPresetList['Custom'];
|
||||||
|
} else if (selectedPreset === 'Source') {
|
||||||
|
presetSetting = DefaultPresetList['Source'];
|
||||||
|
} else {
|
||||||
|
presetSetting = DefaultPresetList['mp3 (256kbps)'];
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = filenamify(`${name}.${presetSetting?.extension ?? 'mp3'}`, {
|
const downloadOptions: FormatOptions = {
|
||||||
|
type: 'audio', // Audio, video or video+audio
|
||||||
|
quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
|
||||||
|
format: 'any', // Media container format
|
||||||
|
};
|
||||||
|
|
||||||
|
const format = info.chooseFormat(downloadOptions);
|
||||||
|
|
||||||
|
let targetFileExtension: string;
|
||||||
|
if (!presetSetting?.extension) {
|
||||||
|
targetFileExtension = YoutubeFormatList.find((it) => it.itag === format.itag)?.container ?? 'mp3';
|
||||||
|
} else {
|
||||||
|
targetFileExtension = presetSetting?.extension ?? 'mp3';
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = filenamify(`${name}.${targetFileExtension}`, {
|
||||||
replacement: '_',
|
replacement: '_',
|
||||||
maxLength: 255,
|
maxLength: 255,
|
||||||
});
|
});
|
||||||
@ -241,13 +259,6 @@ async function downloadSongUnsafe(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadOptions: FormatOptions = {
|
|
||||||
type: 'audio', // Audio, video or video+audio
|
|
||||||
quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
|
|
||||||
format: 'any', // Media container format
|
|
||||||
};
|
|
||||||
|
|
||||||
const format = info.chooseFormat(downloadOptions);
|
|
||||||
const stream = await info.download(downloadOptions);
|
const stream = await info.download(downloadOptions);
|
||||||
|
|
||||||
console.info(
|
console.info(
|
||||||
@ -260,39 +271,20 @@ async function downloadSongUnsafe(
|
|||||||
mkdirSync(dir);
|
mkdirSync(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ffmpegArgs = config.get('ffmpegArgs');
|
const fileBuffer = await iterableStreamToTargetFile(
|
||||||
|
iterableStream,
|
||||||
|
targetFileExtension,
|
||||||
|
metadata,
|
||||||
|
presetSetting?.ffmpegArgs ?? [],
|
||||||
|
format.content_length ?? 0,
|
||||||
|
sendFeedback,
|
||||||
|
increasePlaylistProgress,
|
||||||
|
);
|
||||||
|
|
||||||
if (presetSetting && presetSetting?.extension !== 'mp3') {
|
if (fileBuffer) {
|
||||||
const file = createWriteStream(filePath);
|
if (targetFileExtension !== 'mp3') {
|
||||||
let downloaded = 0;
|
createWriteStream(filePath).write(fileBuffer);
|
||||||
const total: number = format.content_length ?? 1;
|
} else {
|
||||||
|
|
||||||
for await (const chunk of iterableStream) {
|
|
||||||
downloaded += chunk.length;
|
|
||||||
const ratio = downloaded / total;
|
|
||||||
const progress = Math.floor(ratio * 100);
|
|
||||||
sendFeedback(`Download: ${progress}%`, ratio);
|
|
||||||
increasePlaylistProgress(ratio);
|
|
||||||
file.write(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
await ffmpegWriteTags(
|
|
||||||
filePath,
|
|
||||||
metadata,
|
|
||||||
presetSetting.ffmpegArgs,
|
|
||||||
ffmpegArgs,
|
|
||||||
);
|
|
||||||
sendFeedback(null, -1);
|
|
||||||
} else {
|
|
||||||
const fileBuffer = await iterableStreamToMP3(
|
|
||||||
iterableStream,
|
|
||||||
metadata,
|
|
||||||
ffmpegArgs,
|
|
||||||
format.content_length ?? 0,
|
|
||||||
sendFeedback,
|
|
||||||
increasePlaylistProgress,
|
|
||||||
);
|
|
||||||
if (fileBuffer) {
|
|
||||||
const buffer = await writeID3(Buffer.from(fileBuffer), metadata, sendFeedback);
|
const buffer = await writeID3(Buffer.from(fileBuffer), metadata, sendFeedback);
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
writeFileSync(filePath, buffer);
|
writeFileSync(filePath, buffer);
|
||||||
@ -304,10 +296,11 @@ async function downloadSongUnsafe(
|
|||||||
console.info(`Done: "${filePath}"`);
|
console.info(`Done: "${filePath}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function iterableStreamToMP3(
|
async function iterableStreamToTargetFile(
|
||||||
stream: AsyncGenerator<Uint8Array, void>,
|
stream: AsyncGenerator<Uint8Array, void>,
|
||||||
|
extension: string,
|
||||||
metadata: CustomSongInfo,
|
metadata: CustomSongInfo,
|
||||||
ffmpegArgs: string[],
|
presetFfmpegArgs: string[],
|
||||||
contentLength: number,
|
contentLength: number,
|
||||||
sendFeedback: (str: string, value?: number) => void,
|
sendFeedback: (str: string, value?: number) => void,
|
||||||
increasePlaylistProgress: (value: number) => void = () => {
|
increasePlaylistProgress: (value: number) => void = () => {
|
||||||
@ -347,13 +340,14 @@ async function iterableStreamToMP3(
|
|||||||
increasePlaylistProgress(0.15 + (ratio * 0.85));
|
increasePlaylistProgress(0.15 + (ratio * 0.85));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const safeVideoNameWithExtension = `${safeVideoName}.${extension}`;
|
||||||
try {
|
try {
|
||||||
await ffmpeg.run(
|
await ffmpeg.run(
|
||||||
'-i',
|
'-i',
|
||||||
safeVideoName,
|
safeVideoName,
|
||||||
...ffmpegArgs,
|
...presetFfmpegArgs,
|
||||||
...getFFmpegMetadataArgs(metadata),
|
...getFFmpegMetadataArgs(metadata),
|
||||||
`${safeVideoName}.mp3`,
|
safeVideoNameWithExtension,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
ffmpeg.FS('unlink', safeVideoName);
|
ffmpeg.FS('unlink', safeVideoName);
|
||||||
@ -362,9 +356,9 @@ async function iterableStreamToMP3(
|
|||||||
sendFeedback('Saving…');
|
sendFeedback('Saving…');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return ffmpeg.FS('readFile', `${safeVideoName}.mp3`);
|
return ffmpeg.FS('readFile', safeVideoNameWithExtension);
|
||||||
} finally {
|
} finally {
|
||||||
ffmpeg.FS('unlink', `${safeVideoName}.mp3`);
|
ffmpeg.FS('unlink', safeVideoNameWithExtension);
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
sendError(error as Error, safeVideoName);
|
sendError(error as Error, safeVideoName);
|
||||||
@ -544,29 +538,6 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ffmpegWriteTags(filePath: string, metadata: CustomSongInfo, presetFFmpegArgs: string[] = [], ffmpegArgs: string[] = []) {
|
|
||||||
const releaseFFmpegMutex = await ffmpegMutex.acquire();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!ffmpeg.isLoaded()) {
|
|
||||||
await ffmpeg.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
await ffmpeg.run(
|
|
||||||
'-i',
|
|
||||||
filePath,
|
|
||||||
...getFFmpegMetadataArgs(metadata),
|
|
||||||
...presetFFmpegArgs,
|
|
||||||
...ffmpegArgs,
|
|
||||||
filePath,
|
|
||||||
);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
sendError(error as Error);
|
|
||||||
} finally {
|
|
||||||
releaseFFmpegMutex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFFmpegMetadataArgs(metadata: CustomSongInfo) {
|
function getFFmpegMetadataArgs(metadata: CustomSongInfo) {
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@ -47,7 +47,7 @@ const menuObserver = new MutationObserver(() => {
|
|||||||
(global as any).download = () => {
|
(global as any).download = () => {
|
||||||
let videoUrl = getSongMenu()
|
let videoUrl = getSongMenu()
|
||||||
// Selector of first button which is always "Start Radio"
|
// Selector of first button which is always "Start Radio"
|
||||||
?.querySelector('ytmusic-menu-navigation-item-renderer[tabindex="0"] #navigation-endpoint')
|
?.querySelector('ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint')
|
||||||
?.getAttribute('href');
|
?.getAttribute('href');
|
||||||
if (videoUrl) {
|
if (videoUrl) {
|
||||||
if (videoUrl.startsWith('watch?')) {
|
if (videoUrl.startsWith('watch?')) {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { dialog } from 'electron';
|
import { dialog } from 'electron';
|
||||||
|
|
||||||
import { downloadPlaylist } from './back';
|
import { downloadPlaylist } from './back';
|
||||||
import { defaultMenuDownloadLabel, getFolder, presets } from './utils';
|
import { defaultMenuDownloadLabel, getFolder } from './utils';
|
||||||
|
import { DefaultPresetList } from './types';
|
||||||
import config from './config';
|
import config from './config';
|
||||||
|
|
||||||
import { MenuTemplate } from '../../menu';
|
import { MenuTemplate } from '../../menu';
|
||||||
@ -25,12 +26,12 @@ export default (): MenuTemplate => [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Presets',
|
label: 'Presets',
|
||||||
submenu: Object.keys(presets).map((preset) => ({
|
submenu: Object.keys(DefaultPresetList).map((preset) => ({
|
||||||
label: preset,
|
label: preset,
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
checked: config.get('preset') === preset,
|
checked: config.get('selectedPreset') === preset,
|
||||||
click() {
|
click() {
|
||||||
config.set('preset', preset);
|
config.set('selectedPreset', preset);
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
|
|||||||
116
plugins/downloader/types.ts
Normal file
116
plugins/downloader/types.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
export interface Preset {
|
||||||
|
extension?: string | null;
|
||||||
|
ffmpegArgs: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Presets for FFmpeg
|
||||||
|
export const DefaultPresetList: Record<string, Preset> = {
|
||||||
|
'mp3 (256kbps)': {
|
||||||
|
extension: 'mp3',
|
||||||
|
ffmpegArgs: ['-b:a', '256k'],
|
||||||
|
},
|
||||||
|
'Source': {
|
||||||
|
extension: undefined,
|
||||||
|
ffmpegArgs: ['-acodec', 'copy'],
|
||||||
|
},
|
||||||
|
'Custom': {
|
||||||
|
extension: null,
|
||||||
|
ffmpegArgs: [],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface YouTubeFormat {
|
||||||
|
itag: number;
|
||||||
|
container: string;
|
||||||
|
content: string;
|
||||||
|
resolution: string;
|
||||||
|
bitrate: string;
|
||||||
|
range: string;
|
||||||
|
vrOr3D: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// converted from https://gist.github.com/sidneys/7095afe4da4ae58694d128b1034e01e2#file-youtube_format_code_itag_list-md
|
||||||
|
export const YoutubeFormatList: YouTubeFormat[] = [
|
||||||
|
{ itag: 5, container: 'flv', content: 'audio/video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 6, container: 'flv', content: 'audio/video', resolution: '270p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 17, container: '3gp', content: 'audio/video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 18, container: 'mp4', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 22, container: 'mp4', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 34, container: 'flv', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 35, container: 'flv', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 36, container: '3gp', content: 'audio/video', resolution: '180p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 37, container: 'mp4', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 38, container: 'mp4', content: 'audio/video', resolution: '3072p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 43, container: 'webm', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 44, container: 'webm', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 45, container: 'webm', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 46, container: 'webm', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 82, container: 'mp4', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 83, container: 'mp4', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 84, container: 'mp4', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 85, container: 'mp4', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 91, container: 'hls', content: 'audio/video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 92, container: 'hls', content: 'audio/video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 93, container: 'hls', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 94, container: 'hls', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 95, container: 'hls', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 96, container: 'hls', content: 'audio/video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '-' },
|
||||||
|
{ itag: 100, container: 'webm', content: 'audio/video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 101, container: 'webm', content: 'audio/video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 102, container: 'webm', content: 'audio/video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '3D' },
|
||||||
|
{ itag: 132, container: 'hls', content: 'audio/video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 133, container: 'mp4', content: 'video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 134, container: 'mp4', content: 'video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 135, container: 'mp4', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 136, container: 'mp4', content: 'video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 137, container: 'mp4', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 138, container: 'mp4', content: 'video', resolution: '2160p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 139, container: 'm4a', content: 'audio', resolution: '-', bitrate: '48k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 140, container: 'm4a', content: 'audio', resolution: '-', bitrate: '128k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 141, container: 'm4a', content: 'audio', resolution: '-', bitrate: '256k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 151, container: 'hls', content: 'audio/video', resolution: '72p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 160, container: 'mp4', content: 'video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 167, container: 'webm', content: 'video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 168, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 169, container: 'webm', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 171, container: 'webm', content: 'audio', resolution: '-', bitrate: '128k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 218, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 219, container: 'webm', content: 'video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 242, container: 'webm', content: 'video', resolution: '240p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 243, container: 'webm', content: 'video', resolution: '360p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 244, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 245, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 246, container: 'webm', content: 'video', resolution: '480p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 247, container: 'webm', content: 'video', resolution: '720p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 248, container: 'webm', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 249, container: 'webm', content: 'audio', resolution: '-', bitrate: '50k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 250, container: 'webm', content: 'audio', resolution: '-', bitrate: '70k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 251, container: 'webm', content: 'audio', resolution: '-', bitrate: '160k', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 264, container: 'mp4', content: 'video', resolution: '1440p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 266, container: 'mp4', content: 'video', resolution: '2160p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 271, container: 'webm', content: 'video', resolution: '1440p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 272, container: 'webm', content: 'video', resolution: '4320p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 278, container: 'webm', content: 'video', resolution: '144p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 298, container: 'mp4', content: 'video', resolution: '720p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 299, container: 'mp4', content: 'video', resolution: '1080p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 302, container: 'webm', content: 'video', resolution: '720p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 303, container: 'webm', content: 'video', resolution: '1080p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 308, container: 'webm', content: 'video', resolution: '1440p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 313, container: 'webm', content: 'video', resolution: '2160p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 315, container: 'webm', content: 'video', resolution: '2160p60', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 330, container: 'webm', content: 'video', resolution: '144p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 331, container: 'webm', content: 'video', resolution: '240p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 332, container: 'webm', content: 'video', resolution: '360p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 333, container: 'webm', content: 'video', resolution: '480p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 334, container: 'webm', content: 'video', resolution: '720p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 335, container: 'webm', content: 'video', resolution: '1080p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 336, container: 'webm', content: 'video', resolution: '1440p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 337, container: 'webm', content: 'video', resolution: '2160p60', bitrate: '-', range: 'hdr', vrOr3D: '' },
|
||||||
|
{ itag: 272, container: 'webm', content: 'video', resolution: '2880p/4320p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 399, container: 'mp4', content: 'video', resolution: '1080p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 400, container: 'mp4', content: 'video', resolution: '1440p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 401, container: 'mp4', content: 'video', resolution: '2160p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 402, container: 'mp4', content: 'video', resolution: '2880p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 571, container: 'mp4', content: 'video', resolution: '3840p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
{ itag: 702, container: 'mp4', content: 'video', resolution: '3840p', bitrate: '-', range: '-', vrOr3D: '' },
|
||||||
|
];
|
||||||
@ -10,7 +10,7 @@ export const sendFeedback = (win: BrowserWindow, message?: unknown) => {
|
|||||||
|
|
||||||
export const cropMaxWidth = (image: Electron.NativeImage) => {
|
export const cropMaxWidth = (image: Electron.NativeImage) => {
|
||||||
const imageSize = image.getSize();
|
const imageSize = image.getSize();
|
||||||
// Standart youtube artwork width with margins from both sides is 280 + 720 + 280
|
// Standart YouTube artwork width with margins from both sides is 280 + 720 + 280
|
||||||
if (imageSize.width === 1280 && imageSize.height === 720) {
|
if (imageSize.width === 1280 && imageSize.height === 720) {
|
||||||
return image.crop({
|
return image.crop({
|
||||||
x: 280,
|
x: 280,
|
||||||
@ -23,15 +23,6 @@ export const cropMaxWidth = (image: Electron.NativeImage) => {
|
|||||||
return image;
|
return image;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Presets for FFmpeg
|
|
||||||
export const presets = {
|
|
||||||
'None (defaults to mp3)': undefined,
|
|
||||||
'opus': {
|
|
||||||
extension: 'opus',
|
|
||||||
ffmpegArgs: ['-acodec', 'libopus'],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setBadge = (n: number) => {
|
export const setBadge = (n: number) => {
|
||||||
if (is.linux() || is.macOS()) {
|
if (is.linux() || is.macOS()) {
|
||||||
app.setBadgeCount(n);
|
app.setBadgeCount(n);
|
||||||
|
|||||||
@ -61,5 +61,7 @@ export default (win: BrowserWindow) => {
|
|||||||
ipcMain.handle('window-close', () => win.close());
|
ipcMain.handle('window-close', () => win.close());
|
||||||
ipcMain.handle('window-minimize', () => win.minimize());
|
ipcMain.handle('window-minimize', () => win.minimize());
|
||||||
ipcMain.handle('window-maximize', () => win.maximize());
|
ipcMain.handle('window-maximize', () => win.maximize());
|
||||||
|
win.on('maximize', () => win.webContents.send('window-maximize'));
|
||||||
ipcMain.handle('window-unmaximize', () => win.unmaximize());
|
ipcMain.handle('window-unmaximize', () => win.unmaximize());
|
||||||
|
win.on('unmaximize', () => win.webContents.send('window-unmaximize'));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export default async () => {
|
|||||||
let hideMenu = config.get('options.hideMenu');
|
let hideMenu = config.get('options.hideMenu');
|
||||||
const titleBar = document.createElement('title-bar');
|
const titleBar = document.createElement('title-bar');
|
||||||
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
|
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');
|
||||||
|
let maximizeButton: HTMLButtonElement;
|
||||||
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
|
if (isMacOS) titleBar.style.setProperty('--offset-left', '70px');
|
||||||
|
|
||||||
logo.classList.add('title-bar-icon');
|
logo.classList.add('title-bar-icon');
|
||||||
@ -55,7 +56,7 @@ export default async () => {
|
|||||||
minimizeButton.appendChild(minimize);
|
minimizeButton.appendChild(minimize);
|
||||||
minimizeButton.onclick = () => ipcRenderer.invoke('window-minimize');
|
minimizeButton.onclick = () => ipcRenderer.invoke('window-minimize');
|
||||||
|
|
||||||
const maximizeButton = document.createElement('button');
|
maximizeButton = document.createElement('button');
|
||||||
if (await ipcRenderer.invoke('window-is-maximized')) {
|
if (await ipcRenderer.invoke('window-is-maximized')) {
|
||||||
maximizeButton.classList.add('window-control');
|
maximizeButton.classList.add('window-control');
|
||||||
maximizeButton.appendChild(unmaximize);
|
maximizeButton.appendChild(unmaximize);
|
||||||
@ -135,8 +136,14 @@ export default async () => {
|
|||||||
|
|
||||||
document.title = 'Youtube Music';
|
document.title = 'Youtube Music';
|
||||||
|
|
||||||
ipcRenderer.on('refreshMenu', () => {
|
ipcRenderer.on('refreshMenu', () => updateMenu());
|
||||||
updateMenu();
|
ipcRenderer.on('window-maximize', () => {
|
||||||
|
maximizeButton.removeChild(maximizeButton.firstChild!);
|
||||||
|
maximizeButton.appendChild(unmaximize);
|
||||||
|
});
|
||||||
|
ipcRenderer.on('window-unmaximize', () => {
|
||||||
|
maximizeButton.removeChild(maximizeButton.firstChild!);
|
||||||
|
maximizeButton.appendChild(maximize);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isEnabled('picture-in-picture')) {
|
if (isEnabled('picture-in-picture')) {
|
||||||
|
|||||||
Reference in New Issue
Block a user