Compare commits

...

20 Commits

Author SHA1 Message Date
a33a03f512 Bump version to 3.7.1 2024-12-27 01:44:11 +09:00
f8a53f0d61 fix(api-server): fix swagger 2024-12-27 01:43:03 +09:00
748d77d1c0 fix(downloader): fix #2234 2024-12-27 01:30:09 +09:00
725ad0d630 fix(deps): update dependency node-html-parser to v7 (#2776)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 01:15:58 +09:00
bdbab17772 chore(deps): update dependency vite to v6.0.6 (#2774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 01:15:18 +09:00
57d2fa372d fix(synced-lyrics): use blur instead of static background color 2024-12-27 01:13:53 +09:00
80471b0ca4 fix: use networkManager.fetch instead of fetch 2024-12-27 01:05:55 +09:00
22fdfe3342 fix(downloader): fix #2769 2024-12-27 00:50:18 +09:00
5ecfa2a1f7 fix: fix #2645, fix #2741 2024-12-26 23:49:19 +09:00
b9beea810e fix(music-together): fix crash 2024-12-26 23:14:42 +09:00
f0e77812e7 fix: fix login CORS issue 2024-12-26 23:13:28 +09:00
6d1237c2a2 chore(i18n): Translated using Weblate (Hindi)
Currently translated at 29.8% (121 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/hi/
2024-12-26 06:04:43 +00:00
b43c92386e chore(i18n): Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pt_BR/
2024-12-26 06:04:42 +00:00
017476a81b chore(i18n): Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/zh_Hant/
2024-12-26 06:04:41 +00:00
9b047d9c54 chore(i18n): Translated using Weblate (Russian)
Currently translated at 100.0% (406 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/ru/
2024-12-26 06:04:40 +00:00
31f009d3c4 chore(i18n): Translated using Weblate (Polish)
Currently translated at 98.7% (401 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/pl/
2024-12-26 06:04:40 +00:00
8504f2c086 chore(i18n): Translated using Weblate (Italian)
Currently translated at 99.2% (403 of 406 strings)

Translation: th-ch/youtube-music/i18n
Translate-URL: https://hosted.weblate.org/projects/youtube-music/i18n/it/
2024-12-26 06:04:39 +00:00
1d6251baea feat(api-server): Add queue api (#2767) 2024-12-25 18:55:24 +09:00
3ea13a2a22 fix(api-server): repeat event listener 2024-12-25 13:08:02 +09:00
1cc153084d Update changelog for v3.7.0 2024-12-25 01:45:32 +00:00
26 changed files with 758 additions and 357 deletions

View File

@ -2,8 +2,166 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v3.7.0](https://github.com/th-ch/youtube-music/compare/v3.6.2...v3.7.0)
- feat(amuse): song query api (add amuse plugin) [`#2723`](https://github.com/th-ch/youtube-music/pull/2723)
- feat(api-server): add absolute seek endpoint [`#2748`](https://github.com/th-ch/youtube-music/pull/2748)
- feat(api-server): Add repeat mode and seek time API [`#2630`](https://github.com/th-ch/youtube-music/pull/2630)
- feat(synced-lyrics): Better-Lyrics Styling for Synced-Lyrics [`#2554`](https://github.com/th-ch/youtube-music/pull/2554)
- feat(synced-lyrics): multiple lyric sources [`#2383`](https://github.com/th-ch/youtube-music/pull/2383)
- chore(deps): update dependency typescript-eslint to v8.18.2 [`#2763`](https://github.com/th-ch/youtube-music/pull/2763)
- chore(deps): update dependency discord-api-types to v0.37.114 [`#2761`](https://github.com/th-ch/youtube-music/pull/2761)
- chore(deps): update dependency discord-api-types to v0.37.113 [`#2759`](https://github.com/th-ch/youtube-music/pull/2759)
- fix: Set correct window class for X11 and Wayland [`#2758`](https://github.com/th-ch/youtube-music/pull/2758)
- feat: Specify flatpak runtime [`#2755`](https://github.com/th-ch/youtube-music/pull/2755)
- chore(deps): update dependency rollup to v4.29.1 [`#2749`](https://github.com/th-ch/youtube-music/pull/2749)
- chore(deps): update dependency esbuild to v0.24.2 [`#2742`](https://github.com/th-ch/youtube-music/pull/2742)
- fix: Add Flatpak permissions needed for MPRIS and tray icon [`#2754`](https://github.com/th-ch/youtube-music/pull/2754)
- chore(deps): update dependency vite-plugin-inspect to v0.10.6 [`#2756`](https://github.com/th-ch/youtube-music/pull/2756)
- chore(deps): update dependency vite to v6.0.5 [`#2745`](https://github.com/th-ch/youtube-music/pull/2745)
- fix(deps): update dependency i18next to v24.2.0 [`#2744`](https://github.com/th-ch/youtube-music/pull/2744)
- chore(deps): update dependency vite-plugin-inspect to v0.10.4 [`#2743`](https://github.com/th-ch/youtube-music/pull/2743)
- chore(deps): update dependency discord-api-types to v0.37.112 [`#2740`](https://github.com/th-ch/youtube-music/pull/2740)
- fix(discord): Fix Album Art failing on Discord RPC [`#2666`](https://github.com/th-ch/youtube-music/pull/2666)
- feat: Add equalizer plugin with presets (e.g. bass booster) [`#2575`](https://github.com/th-ch/youtube-music/pull/2575)
- chore(deps): update dependency vite to v6.0.4 [`#2738`](https://github.com/th-ch/youtube-music/pull/2738)
- fix: Fixed #1796 [`#2736`](https://github.com/th-ch/youtube-music/pull/2736)
- chore(deps): update dependency electron-devtools-installer to v4 [`#2734`](https://github.com/th-ch/youtube-music/pull/2734)
- Revert "chore(deps): update dependency electron-builder to v25" [`#2732`](https://github.com/th-ch/youtube-music/pull/2732)
- chore(deps): update dependency electron-builder to v25 [`#2490`](https://github.com/th-ch/youtube-music/pull/2490)
- fix(deps): update dependency i18next to v24.1.2 [`#2727`](https://github.com/th-ch/youtube-music/pull/2727)
- chore(deps): update dependency electron-devtools-installer to v3.2.1 [`#2731`](https://github.com/th-ch/youtube-music/pull/2731)
- chore(deps): update dependency typescript-eslint to v8.18.1 [`#2724`](https://github.com/th-ch/youtube-music/pull/2724)
- fix: tab misalignment [`#2713`](https://github.com/th-ch/youtube-music/pull/2713)
- fix(deps): update dependency @hono/zod-validator to v0.4.2 [`#2709`](https://github.com/th-ch/youtube-music/pull/2709)
- chore(deps): update eslint monorepo to v9.17.0 [`#2712`](https://github.com/th-ch/youtube-music/pull/2712)
- fix(deps): update dependency hono to v4.6.14 [`#2716`](https://github.com/th-ch/youtube-music/pull/2716)
- fix: discord rich presence connection status [`#2714`](https://github.com/th-ch/youtube-music/pull/2714)
- fix: Laggy scrolling behaviour in large playlists [`#2708`](https://github.com/th-ch/youtube-music/pull/2708)
- fix(deps): update dependency youtubei.js to v12.2.0 [`#2705`](https://github.com/th-ch/youtube-music/pull/2705)
- fix(deps): update dependency i18next to v24.1.0 [`#2698`](https://github.com/th-ch/youtube-music/pull/2698)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.12.1 [`#2697`](https://github.com/th-ch/youtube-music/pull/2697)
- fix(deps): update dependency zod to v3.24.1 [`#2694`](https://github.com/th-ch/youtube-music/pull/2694)
- fix(deps): update dependency youtubei.js to v12.1.0 [`#2695`](https://github.com/th-ch/youtube-music/pull/2695)
- chore(deps): update dependency discord-api-types to v0.37.111 [`#2690`](https://github.com/th-ch/youtube-music/pull/2690)
- chore(deps): update dependency typescript-eslint to v8.18.0 [`#2692`](https://github.com/th-ch/youtube-music/pull/2692)
- chore(deps): update playwright monorepo to v1.49.1 [`#2693`](https://github.com/th-ch/youtube-music/pull/2693)
- fix(deps): update dependency hono to v4.6.13 [`#2682`](https://github.com/th-ch/youtube-music/pull/2682)
- chore(deps): update dependency rollup to v4.28.1 [`#2683`](https://github.com/th-ch/youtube-music/pull/2683)
- fix(deps): update dependency conf to v13.1.0 [`#2686`](https://github.com/th-ch/youtube-music/pull/2686)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.12.0 [`#2689`](https://github.com/th-ch/youtube-music/pull/2689)
- fix(deps): update dependency youtubei.js to v12 [`#2681`](https://github.com/th-ch/youtube-music/pull/2681)
- chore(deps): update dependency vite to v6.0.3 [`#2680`](https://github.com/th-ch/youtube-music/pull/2680)
- fix(album-actions): Fixed #2312 [`#2676`](https://github.com/th-ch/youtube-music/pull/2676)
- chore(deps): update dependency eslint-import-resolver-typescript to v3.7.0 [`#2672`](https://github.com/th-ch/youtube-music/pull/2672)
- chore(deps): update dependency node-gyp to v11 [`#2678`](https://github.com/th-ch/youtube-music/pull/2678)
- fix(deps): update dependency i18next to v24.0.5 [`#2669`](https://github.com/th-ch/youtube-music/pull/2669)
- fix(deps): update dependency i18next to v24.0.4 [`#2668`](https://github.com/th-ch/youtube-music/pull/2668)
- chore(deps): update dependency vite to v6.0.2 [`#2662`](https://github.com/th-ch/youtube-music/pull/2662)
- chore(deps): update dependency node-gyp to v10.3.1 [`#2665`](https://github.com/th-ch/youtube-music/pull/2665)
- chore(deps): update dependency typescript-eslint to v8.17.0 [`#2664`](https://github.com/th-ch/youtube-music/pull/2664)
- chore(deps): update dependency vite-plugin-inspect to v0.10.3 [`#2667`](https://github.com/th-ch/youtube-music/pull/2667)
- chore(deps): update dependency rollup to v4.28.0 [`#2661`](https://github.com/th-ch/youtube-music/pull/2661)
- chore(deps): update dependency discord-api-types to v0.37.110 [`#2653`](https://github.com/th-ch/youtube-music/pull/2653)
- fix(deps): update dependency @hono/zod-openapi to v0.18.3 [`#2654`](https://github.com/th-ch/youtube-music/pull/2654)
- chore(deps): update eslint monorepo to v9.16.0 [`#2656`](https://github.com/th-ch/youtube-music/pull/2656)
- chore(deps): update dependency vite-plugin-inspect to v0.10.2 [`#2657`](https://github.com/th-ch/youtube-music/pull/2657)
- fix(youtube-music.css): Fixed #2514 [`#2659`](https://github.com/th-ch/youtube-music/pull/2659)
- fix: Fixed Skip Disliked Song not working [`#2651`](https://github.com/th-ch/youtube-music/pull/2651)
- fix(deps): update dependency @hono/zod-openapi to v0.18.2 [`#2650`](https://github.com/th-ch/youtube-music/pull/2650)
- chore(deps): update dependency vite-plugin-inspect to v0.10.1 [`#2652`](https://github.com/th-ch/youtube-music/pull/2652)
- chore(deps): update dependency electron to v33.2.1 [`#2649`](https://github.com/th-ch/youtube-music/pull/2649)
- chore(deps): update dependency vite-plugin-inspect to v0.10.0 [`#2646`](https://github.com/th-ch/youtube-music/pull/2646)
- chore(deps): update dependency vite to v6 [`#2644`](https://github.com/th-ch/youtube-music/pull/2644)
- fix(deps): update dependency @hono/swagger-ui to v0.5.0 [`#2643`](https://github.com/th-ch/youtube-music/pull/2643)
- chore(deps): update dependency discord-api-types to v0.37.109 [`#2642`](https://github.com/th-ch/youtube-music/pull/2642)
- chore(deps): update dependency vite-plugin-solid to v2.11.0 [`#2641`](https://github.com/th-ch/youtube-music/pull/2641)
- fix(deps): update dependency hono to v4.6.12 [`#2636`](https://github.com/th-ch/youtube-music/pull/2636)
- fix(deps): update dependency i18next to v24.0.2 [`#2637`](https://github.com/th-ch/youtube-music/pull/2637)
- chore(deps): update dependency discord-api-types to v0.37.108 [`#2638`](https://github.com/th-ch/youtube-music/pull/2638)
- chore(deps): update dependency typescript-eslint to v8.16.0 [`#2639`](https://github.com/th-ch/youtube-music/pull/2639)
- chore(deps): update dependency rollup to v4.27.4 [`#2632`](https://github.com/th-ch/youtube-music/pull/2632)
- fix(deps): update dependency i18next to v24 [`#2633`](https://github.com/th-ch/youtube-music/pull/2633)
- chore(deps): update dependency typescript to v5.7.2 [`#2629`](https://github.com/th-ch/youtube-music/pull/2629)
- chore(deps): update dependency discord-api-types to v0.37.107 [`#2627`](https://github.com/th-ch/youtube-music/pull/2627)
- fix(deps): update dependency @hono/zod-openapi to v0.18.0 [`#2626`](https://github.com/th-ch/youtube-music/pull/2626)
- fix(deps): update dependency i18next to v23.16.8 [`#2625`](https://github.com/th-ch/youtube-music/pull/2625)
- chore(deps): update dependency vite-plugin-inspect to v0.8.8 [`#2623`](https://github.com/th-ch/youtube-music/pull/2623)
- fix(deps): update dependency hono to v4.6.11 [`#2624`](https://github.com/th-ch/youtube-music/pull/2624)
- chore(deps): update playwright monorepo to v1.49.0 [`#2617`](https://github.com/th-ch/youtube-music/pull/2617)
- chore(deps): update dependency rollup to v4.27.3 [`#2610`](https://github.com/th-ch/youtube-music/pull/2610)
- chore(deps): update dependency typescript-eslint to v8.15.0 [`#2611`](https://github.com/th-ch/youtube-music/pull/2611)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.11.0 [`#2618`](https://github.com/th-ch/youtube-music/pull/2618)
- chore(deps): update dependency discord-api-types to v0.37.105 [`#2603`](https://github.com/th-ch/youtube-music/pull/2603)
- chore(deps): update dependency rollup to v4.27.2 [`#2604`](https://github.com/th-ch/youtube-music/pull/2604)
- chore(deps): update eslint monorepo to v9.15.0 [`#2607`](https://github.com/th-ch/youtube-music/pull/2607)
- fix(deps): update dependency @hono/zod-openapi to v0.17.1 [`#2608`](https://github.com/th-ch/youtube-music/pull/2608)
- fix(ambient-mode): fix ambient-mode overlapping other elements [`#2609`](https://github.com/th-ch/youtube-music/pull/2609)
- fix: Allow media playback control (MPRIS) for flatpak [`#2606`](https://github.com/th-ch/youtube-music/pull/2606)
- fix(deps): update dependency @hono/node-server to v1.13.7 [`#2598`](https://github.com/th-ch/youtube-music/pull/2598)
- chore(deps): update dependency rollup to v4.26.0 [`#2600`](https://github.com/th-ch/youtube-music/pull/2600)
- fix(deps): update dependency hono to v4.6.10 [`#2601`](https://github.com/th-ch/youtube-music/pull/2601)
- fix(deps): update dependency @hono/node-server to v1.13.6 [`#2594`](https://github.com/th-ch/youtube-music/pull/2594)
- chore(deps): update dependency vite to v5.4.11 [`#2595`](https://github.com/th-ch/youtube-music/pull/2595)
- chore(deps): update dependency typescript-eslint to v8.14.0 [`#2596`](https://github.com/th-ch/youtube-music/pull/2596)
- chore(deps): update dependency electron to v33.2.0 [`#2591`](https://github.com/th-ch/youtube-music/pull/2591)
- fix(deps): update dependency @hono/zod-openapi to v0.17.0 [`#2592`](https://github.com/th-ch/youtube-music/pull/2592)
- fix(deps): update dependency i18next to v23.16.5 [`#2589`](https://github.com/th-ch/youtube-music/pull/2589)
- fix(deps): update dependency @hono/node-server to v1.13.5 [`#2578`](https://github.com/th-ch/youtube-music/pull/2578)
- fix(deps): update dependency hono to v4.6.9 [`#2579`](https://github.com/th-ch/youtube-music/pull/2579)
- chore(deps): update dependency discord-api-types to v0.37.104 [`#2588`](https://github.com/th-ch/youtube-music/pull/2588)
- chore(deps): update dependency typescript-eslint to v8.13.0 [`#2581`](https://github.com/th-ch/youtube-music/pull/2581)
- chore(deps): update dependency rollup to v4.25.0 [`#2580`](https://github.com/th-ch/youtube-music/pull/2580)
- chore(docs): Update screenshot [`#2587`](https://github.com/th-ch/youtube-music/pull/2587)
- chore(docs): Specify full path to xattr for macOS, fixes #2583 [`#2586`](https://github.com/th-ch/youtube-music/pull/2586)
- fix: callback for time-changed event [`#2577`](https://github.com/th-ch/youtube-music/pull/2577)
- chore(deps): update eslint monorepo to v9.14.0 [`#2573`](https://github.com/th-ch/youtube-music/pull/2573)
- chore(deps): update dependency utf-8-validate to v6.0.5 [`#2572`](https://github.com/th-ch/youtube-music/pull/2572)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.10.1 [`#2571`](https://github.com/th-ch/youtube-music/pull/2571)
- fix(deps): update dependency @hono/node-server to v1.13.4 [`#2570`](https://github.com/th-ch/youtube-music/pull/2570)
- chore(deps): update dependency @stylistic/eslint-plugin-js to v2.10.0 [`#2569`](https://github.com/th-ch/youtube-music/pull/2569)
- fix(deps): update dependency @floating-ui/dom to v1.6.12 [`#2568`](https://github.com/th-ch/youtube-music/pull/2568)
- chore(deps): update dependency rollup to v4.24.3 [`#2565`](https://github.com/th-ch/youtube-music/pull/2565)
- fix(deps): update dependency hono to v4.6.8 [`#2564`](https://github.com/th-ch/youtube-music/pull/2564)
- chore(deps): update dependency typescript-eslint to v8.12.2 [`#2563`](https://github.com/th-ch/youtube-music/pull/2563)
- chore(deps): update dependency typescript-eslint to v8.12.0 [`#2561`](https://github.com/th-ch/youtube-music/pull/2561)
- fix(deps): update dependency youtubei.js to v11 [`#2562`](https://github.com/th-ch/youtube-music/pull/2562)
- chore(deps): update dependency rollup to v4.24.2 [`#2559`](https://github.com/th-ch/youtube-music/pull/2559)
- fix(deps): update dependency @hono/node-server to v1.13.3 [`#2560`](https://github.com/th-ch/youtube-music/pull/2560)
- fix(deps): update dependency i18next to v23.16.4 [`#2550`](https://github.com/th-ch/youtube-music/pull/2550)
- chore(deps): update playwright monorepo to v1.48.2 [`#2551`](https://github.com/th-ch/youtube-music/pull/2551)
- fix(deps): update dependency hono to v4.6.7 [`#2552`](https://github.com/th-ch/youtube-music/pull/2552)
- chore(deps): update dependency @babel/runtime to v7.26.0 [`#2548`](https://github.com/th-ch/youtube-music/pull/2548)
- chore(deps): update dependency @types/color to v4 [`#2547`](https://github.com/th-ch/youtube-music/pull/2547)
- fix(deps): update dependency i18next to v23.16.3 [`#2545`](https://github.com/th-ch/youtube-music/pull/2545)
- fix(deps): update dependency solid-js to v1.9.3 [`#2541`](https://github.com/th-ch/youtube-music/pull/2541)
- chore(deps): update dependency vite to v5.4.10 [`#2542`](https://github.com/th-ch/youtube-music/pull/2542)
- chore(deps): update dependency electron to v33.0.2 [`#2537`](https://github.com/th-ch/youtube-music/pull/2537)
- chore(deps): update dependency @babel/runtime to v7.25.9 [`#2538`](https://github.com/th-ch/youtube-music/pull/2538)
- chore(deps): update dependency discord-api-types to v0.37.103 [`#2532`](https://github.com/th-ch/youtube-music/pull/2532)
- chore(deps): update dependency typescript-eslint to v8.11.0 [`#2534`](https://github.com/th-ch/youtube-music/pull/2534)
- fix(deps): update dependency hono to v4.6.6 [`#2536`](https://github.com/th-ch/youtube-music/pull/2536)
- fix(tuna-obs): Added song url to tuna-obs plugin [`#2524`](https://github.com/th-ch/youtube-music/pull/2524)
- fix(deps): update dependency i18next to v23.16.2 [`#2530`](https://github.com/th-ch/youtube-music/pull/2530)
- fix(deps): update dependency i18next to v23.16.1 [`#2529`](https://github.com/th-ch/youtube-music/pull/2529)
- chore(deps): update eslint monorepo to v9.13.0 [`#2528`](https://github.com/th-ch/youtube-music/pull/2528)
- chore(deps): update dependency typescript-eslint to v8.10.0 [`#2527`](https://github.com/th-ch/youtube-music/pull/2527)
- chore(deps): update playwright monorepo to v1.48.1 [`#2516`](https://github.com/th-ch/youtube-music/pull/2516)
- chore(deps): update dependency electron to v33.0.1 [`#2523`](https://github.com/th-ch/youtube-music/pull/2523)
- fix: disable gpu memory buffer video frames [`#2519`](https://github.com/th-ch/youtube-music/pull/2519)
- fix: use HEAD instead of GET in songInfo.imageSrc validation step [`#2766`](https://github.com/th-ch/youtube-music/issues/2766)
- fix: Fixed #1796 (#2736) [`#1796`](https://github.com/th-ch/youtube-music/issues/1796)
- fix(album-actions): Fixed #2312 (#2676) [`#2312`](https://github.com/th-ch/youtube-music/issues/2312) [`#2312`](https://github.com/th-ch/youtube-music/issues/2312)
- fix(youtube-music.css): Fixed #2514 (#2659) [`#2514`](https://github.com/th-ch/youtube-music/issues/2514)
- chore(docs): Specify full path to xattr for macOS, fixes #2583 (#2586) [`#2583`](https://github.com/th-ch/youtube-music/issues/2583)
- fix: fix pnpm-lock.yaml [`3208bf4`](https://github.com/th-ch/youtube-music/commit/3208bf4a6d47d824875b06bd031299694482f02d)
- Revert "feat: use swc and lightningcss" [`3b50cbc`](https://github.com/th-ch/youtube-music/commit/3b50cbcb6e3163115d52f05075af5d6f25b80660)
- feat: use swc and lightningcss [`ae3a289`](https://github.com/th-ch/youtube-music/commit/ae3a28900576ea388666747bc4794577e1d57e23)
#### [v3.6.2](https://github.com/th-ch/youtube-music/compare/v3.6.1...v3.6.2)
> 16 October 2024
- fix(deps): update dependency serve to v14.2.4 [`#2515`](https://github.com/th-ch/youtube-music/pull/2515)
- fix(deps): update dependency hono to v4.6.5 [`#2509`](https://github.com/th-ch/youtube-music/pull/2509)
- chore(deps): update dependency vite to v5.4.9 [`#2500`](https://github.com/th-ch/youtube-music/pull/2500)

View File

@ -2,7 +2,7 @@
"name": "youtube-music",
"desktopName": "com.github.th_ch.youtube_music",
"productName": "YouTube Music",
"version": "3.7.0",
"version": "3.7.1",
"description": "YouTube Music Desktop App - including custom plugins",
"main": "./dist/main/index.js",
"license": "MIT",
@ -275,7 +275,7 @@
"jimp": "1.6.0",
"keyboardevent-from-electron-accelerator": "2.0.0",
"keyboardevents-areequal": "0.2.2",
"node-html-parser": "6.1.13",
"node-html-parser": "7.0.1",
"node-id3": "0.2.6",
"peerjs": "1.5.4",
"semver": "7.6.3",
@ -326,7 +326,7 @@
"typescript": "5.7.2",
"typescript-eslint": "8.18.2",
"utf-8-validate": "6.0.5",
"vite": "6.0.5",
"vite": "6.0.6",
"vite-plugin-inspect": "0.10.6",
"vite-plugin-resolve": "2.5.2",
"vite-plugin-solid": "2.11.0",

294
pnpm-lock.yaml generated
View File

@ -151,8 +151,8 @@ importers:
specifier: 0.2.2
version: 0.2.2
node-html-parser:
specifier: 6.1.13
version: 6.1.13
specifier: 7.0.1
version: 7.0.1
node-id3:
specifier: 0.2.6
version: 0.2.6
@ -255,7 +255,7 @@ importers:
version: 4.0.0
electron-vite:
specifier: 2.3.0
version: 2.3.0(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1))
version: 2.3.0(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1))
esbuild:
specifier: 0.24.2
version: 0.24.2
@ -299,17 +299,17 @@ importers:
specifier: 6.0.5
version: 6.0.5
vite:
specifier: 6.0.5
version: 6.0.5(@types/node@22.9.3)(yaml@2.6.1)
specifier: 6.0.6
version: 6.0.6(@types/node@22.9.3)(yaml@2.6.1)
vite-plugin-inspect:
specifier: 0.10.6
version: 0.10.6(rollup@4.29.1)(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1))
version: 0.10.6(rollup@4.29.1)(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1))
vite-plugin-resolve:
specifier: 2.5.2
version: 2.5.2
vite-plugin-solid:
specifier: 2.11.0
version: 2.11.0(solid-js@1.9.3)(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1))
version: 2.11.0(solid-js@1.9.3)(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1))
ws:
specifier: 8.18.0
version: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
@ -478,12 +478,6 @@ packages:
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.24.0':
resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.24.2':
resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
engines: {node: '>=18'}
@ -496,12 +490,6 @@ packages:
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.24.0':
resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.24.2':
resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
engines: {node: '>=18'}
@ -514,12 +502,6 @@ packages:
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.24.0':
resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.24.2':
resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
engines: {node: '>=18'}
@ -532,12 +514,6 @@ packages:
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.24.0':
resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.24.2':
resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
engines: {node: '>=18'}
@ -550,12 +526,6 @@ packages:
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.24.0':
resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.24.2':
resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
engines: {node: '>=18'}
@ -568,12 +538,6 @@ packages:
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.24.0':
resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.24.2':
resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
engines: {node: '>=18'}
@ -586,12 +550,6 @@ packages:
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.24.0':
resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.24.2':
resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
engines: {node: '>=18'}
@ -604,12 +562,6 @@ packages:
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.24.0':
resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.24.2':
resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
engines: {node: '>=18'}
@ -622,12 +574,6 @@ packages:
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.24.0':
resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.24.2':
resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
engines: {node: '>=18'}
@ -640,12 +586,6 @@ packages:
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.24.0':
resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.24.2':
resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
engines: {node: '>=18'}
@ -658,12 +598,6 @@ packages:
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.24.0':
resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.24.2':
resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
engines: {node: '>=18'}
@ -676,12 +610,6 @@ packages:
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.24.0':
resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.24.2':
resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
engines: {node: '>=18'}
@ -694,12 +622,6 @@ packages:
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.24.0':
resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.24.2':
resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
engines: {node: '>=18'}
@ -712,12 +634,6 @@ packages:
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.24.0':
resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.24.2':
resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
engines: {node: '>=18'}
@ -730,12 +646,6 @@ packages:
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.24.0':
resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.24.2':
resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
engines: {node: '>=18'}
@ -748,12 +658,6 @@ packages:
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.24.0':
resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.24.2':
resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
engines: {node: '>=18'}
@ -766,12 +670,6 @@ packages:
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.24.0':
resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.24.2':
resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
engines: {node: '>=18'}
@ -790,24 +688,12 @@ packages:
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.24.0':
resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.24.2':
resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.24.0':
resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-arm64@0.24.2':
resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
engines: {node: '>=18'}
@ -820,12 +706,6 @@ packages:
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.24.0':
resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.24.2':
resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
engines: {node: '>=18'}
@ -838,12 +718,6 @@ packages:
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.24.0':
resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.24.2':
resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
engines: {node: '>=18'}
@ -856,12 +730,6 @@ packages:
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.24.0':
resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.24.2':
resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
engines: {node: '>=18'}
@ -874,12 +742,6 @@ packages:
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.24.0':
resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.24.2':
resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
engines: {node: '>=18'}
@ -892,12 +754,6 @@ packages:
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.24.0':
resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.24.2':
resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
engines: {node: '>=18'}
@ -2336,11 +2192,6 @@ packages:
engines: {node: '>=12'}
hasBin: true
esbuild@0.24.0:
resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==}
engines: {node: '>=18'}
hasBin: true
esbuild@0.24.2:
resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
engines: {node: '>=18'}
@ -3430,8 +3281,8 @@ packages:
engines: {node: ^18.17.0 || >=20.5.0}
hasBin: true
node-html-parser@6.1.13:
resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
node-html-parser@7.0.1:
resolution: {integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==}
node-id3@0.2.6:
resolution: {integrity: sha512-w8GuKXLlPpDjTxLowCt/uYMhRQzED3cg2GdSG1i6RSGKeDzPvxlXeLQuQInKljahPZ0aDnmyX7FX8BbJOM7REg==}
@ -4320,8 +4171,8 @@ packages:
'@testing-library/jest-dom':
optional: true
vite@6.0.5:
resolution: {integrity: sha512-akD5IAH/ID5imgue2DYhzsEwCi0/4VKY31uhMLEYJwPP4TiUp8pL5PIK+Wo7H8qT8JY9i+pVfPydcFPYD1EL7g==}
vite@6.0.6:
resolution: {integrity: sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
@ -4727,153 +4578,102 @@ snapshots:
'@esbuild/aix-ppc64@0.21.5':
optional: true
'@esbuild/aix-ppc64@0.24.0':
optional: true
'@esbuild/aix-ppc64@0.24.2':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
'@esbuild/android-arm64@0.24.0':
optional: true
'@esbuild/android-arm64@0.24.2':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
'@esbuild/android-arm@0.24.0':
optional: true
'@esbuild/android-arm@0.24.2':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
'@esbuild/android-x64@0.24.0':
optional: true
'@esbuild/android-x64@0.24.2':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
'@esbuild/darwin-arm64@0.24.0':
optional: true
'@esbuild/darwin-arm64@0.24.2':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
'@esbuild/darwin-x64@0.24.0':
optional: true
'@esbuild/darwin-x64@0.24.2':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
'@esbuild/freebsd-arm64@0.24.0':
optional: true
'@esbuild/freebsd-arm64@0.24.2':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
'@esbuild/freebsd-x64@0.24.0':
optional: true
'@esbuild/freebsd-x64@0.24.2':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
'@esbuild/linux-arm64@0.24.0':
optional: true
'@esbuild/linux-arm64@0.24.2':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
'@esbuild/linux-arm@0.24.0':
optional: true
'@esbuild/linux-arm@0.24.2':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
'@esbuild/linux-ia32@0.24.0':
optional: true
'@esbuild/linux-ia32@0.24.2':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
'@esbuild/linux-loong64@0.24.0':
optional: true
'@esbuild/linux-loong64@0.24.2':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
'@esbuild/linux-mips64el@0.24.0':
optional: true
'@esbuild/linux-mips64el@0.24.2':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
'@esbuild/linux-ppc64@0.24.0':
optional: true
'@esbuild/linux-ppc64@0.24.2':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
'@esbuild/linux-riscv64@0.24.0':
optional: true
'@esbuild/linux-riscv64@0.24.2':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
'@esbuild/linux-s390x@0.24.0':
optional: true
'@esbuild/linux-s390x@0.24.2':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
'@esbuild/linux-x64@0.24.0':
optional: true
'@esbuild/linux-x64@0.24.2':
optional: true
@ -4883,60 +4683,39 @@ snapshots:
'@esbuild/netbsd-x64@0.21.5':
optional: true
'@esbuild/netbsd-x64@0.24.0':
optional: true
'@esbuild/netbsd-x64@0.24.2':
optional: true
'@esbuild/openbsd-arm64@0.24.0':
optional: true
'@esbuild/openbsd-arm64@0.24.2':
optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
'@esbuild/openbsd-x64@0.24.0':
optional: true
'@esbuild/openbsd-x64@0.24.2':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
'@esbuild/sunos-x64@0.24.0':
optional: true
'@esbuild/sunos-x64@0.24.2':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
'@esbuild/win32-arm64@0.24.0':
optional: true
'@esbuild/win32-arm64@0.24.2':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
'@esbuild/win32-ia32@0.24.0':
optional: true
'@esbuild/win32-ia32@0.24.2':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
'@esbuild/win32-x64@0.24.0':
optional: true
'@esbuild/win32-x64@0.24.2':
optional: true
@ -6587,7 +6366,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron-vite@2.3.0(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1)):
electron-vite@2.3.0(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1)):
dependencies:
'@babel/core': 7.26.0
'@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0)
@ -6595,7 +6374,7 @@ snapshots:
esbuild: 0.21.5
magic-string: 0.30.13
picocolors: 1.1.1
vite: 6.0.5(@types/node@22.9.3)(yaml@2.6.1)
vite: 6.0.6(@types/node@22.9.3)(yaml@2.6.1)
transitivePeerDependencies:
- supports-color
@ -6753,33 +6532,6 @@ snapshots:
'@esbuild/win32-ia32': 0.21.5
'@esbuild/win32-x64': 0.21.5
esbuild@0.24.0:
optionalDependencies:
'@esbuild/aix-ppc64': 0.24.0
'@esbuild/android-arm': 0.24.0
'@esbuild/android-arm64': 0.24.0
'@esbuild/android-x64': 0.24.0
'@esbuild/darwin-arm64': 0.24.0
'@esbuild/darwin-x64': 0.24.0
'@esbuild/freebsd-arm64': 0.24.0
'@esbuild/freebsd-x64': 0.24.0
'@esbuild/linux-arm': 0.24.0
'@esbuild/linux-arm64': 0.24.0
'@esbuild/linux-ia32': 0.24.0
'@esbuild/linux-loong64': 0.24.0
'@esbuild/linux-mips64el': 0.24.0
'@esbuild/linux-ppc64': 0.24.0
'@esbuild/linux-riscv64': 0.24.0
'@esbuild/linux-s390x': 0.24.0
'@esbuild/linux-x64': 0.24.0
'@esbuild/netbsd-x64': 0.24.0
'@esbuild/openbsd-arm64': 0.24.0
'@esbuild/openbsd-x64': 0.24.0
'@esbuild/sunos-x64': 0.24.0
'@esbuild/win32-arm64': 0.24.0
'@esbuild/win32-ia32': 0.24.0
'@esbuild/win32-x64': 0.24.0
esbuild@0.24.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.24.2
@ -7929,7 +7681,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
node-html-parser@6.1.13:
node-html-parser@7.0.1:
dependencies:
css-select: 5.1.0
he: 1.2.0
@ -8859,7 +8611,7 @@ snapshots:
extsprintf: 1.4.1
optional: true
vite-plugin-inspect@0.10.6(rollup@4.29.1)(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1)):
vite-plugin-inspect@0.10.6(rollup@4.29.1)(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1)):
dependencies:
'@antfu/utils': 0.7.10
'@rollup/pluginutils': 5.1.4(rollup@4.29.1)
@ -8870,7 +8622,7 @@ snapshots:
perfect-debounce: 1.0.0
picocolors: 1.1.1
sirv: 3.0.0
vite: 6.0.5(@types/node@22.9.3)(yaml@2.6.1)
vite: 6.0.6(@types/node@22.9.3)(yaml@2.6.1)
transitivePeerDependencies:
- rollup
- supports-color
@ -8879,7 +8631,7 @@ snapshots:
dependencies:
lib-esm: 0.4.2
vite-plugin-solid@2.11.0(solid-js@1.9.3)(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1)):
vite-plugin-solid@2.11.0(solid-js@1.9.3)(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1)):
dependencies:
'@babel/core': 7.26.0
'@types/babel__core': 7.20.5
@ -8887,14 +8639,14 @@ snapshots:
merge-anything: 5.1.7
solid-js: 1.9.3
solid-refresh: 0.6.3(solid-js@1.9.3)
vite: 6.0.5(@types/node@22.9.3)(yaml@2.6.1)
vitefu: 1.0.4(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1))
vite: 6.0.6(@types/node@22.9.3)(yaml@2.6.1)
vitefu: 1.0.4(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1))
transitivePeerDependencies:
- supports-color
vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1):
vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1):
dependencies:
esbuild: 0.24.0
esbuild: 0.24.2
postcss: 8.4.49
rollup: 4.29.1
optionalDependencies:
@ -8902,9 +8654,9 @@ snapshots:
fsevents: 2.3.3
yaml: 2.6.1
vitefu@1.0.4(vite@6.0.5(@types/node@22.9.3)(yaml@2.6.1)):
vitefu@1.0.4(vite@6.0.6(@types/node@22.9.3)(yaml@2.6.1)):
optionalDependencies:
vite: 6.0.5(@types/node@22.9.3)(yaml@2.6.1)
vite: 6.0.6(@types/node@22.9.3)(yaml@2.6.1)
vudio@2.1.1(patch_hash=7iux5msqpgl3octdmwy4uspwoe): {}

View File

@ -194,10 +194,44 @@
}
},
"tray": {
"next": "अगला"
"next": "अगला",
"play-pause": "चलाएँ/रोकें",
"previous": "पिछला",
"quit": "निकास",
"restart": "ऐप पुनः प्रारंभ करें",
"show": "ऐप दिखाए",
"tooltip": {
"default": "यूट्यूब म्यूजिक",
"with-song-info": "यूट्यूब म्यूजिक: {{artist}} - {{title}}"
}
}
},
"plugins": {
"ad-speedup": {
"description": "यदि कोई विज्ञापन चलता है तो यह ऑडियो को म्यूट कर देता है और प्लेबैक गति 16x पर सेट कर देता है",
"name": "विज्ञापन की गति बढ़ाना"
},
"adblocker": {
"description": "डिफ़ॉल्ट रूप से सभी विज्ञापनों और ट्रैकिंग को ब्लॉक करें",
"menu": {
"blocker": "ब्लॉकर"
},
"name": "विज्ञापन अवरोधक"
},
"album-actions": {
"description": "प्लेलिस्ट या एल्बम के सभी गानों पर लागू करने के लिए \"अंडिसलाइक,\" \"डिसलाइक,\" \"लाइक,\" और \"अनलाइक\" बटन जोड़ता है",
"name": "एल्बम एक्शन"
},
"album-color-theme": {
"description": "एल्बम रंग पैलेट के आधार पर एक गतिशील थीम और दृश्य प्रभाव लागू करता है",
"menu": {
"color-mix-ratio": {
"submenu": {
"percent": "{{ratio}}%"
}
}
}
},
"video-toggle": {
"menu": {
"align": {

View File

@ -279,6 +279,13 @@
},
"name": "Modalità Ambiente"
},
"amuse": {
"description": "Aggiunge il supporto a YouTube Music per il widget Amuse Now Playing di 6K Labs",
"name": "Amuse",
"response": {
"query": "Il server API di Amuse è in funzione. GET /query per ottenere informazioni sui brani."
}
},
"api-server": {
"description": "Aggiunge un server API per controllare il player",
"dialog": {
@ -484,6 +491,18 @@
"button": "Scarica"
}
},
"equalizer": {
"description": "Aggiunge un equalizzatore al player",
"menu": {
"presets": {
"label": "Presets",
"list": {
"bass-booster": "Booster dei bassi"
}
}
},
"name": "Equalizzatore"
},
"exponential-volume": {
"description": "Rende esponenziale il cursore del volume, in modo da facilitare la selezione di volumi più bassi.",
"name": "Volume esponenziale"

View File

@ -279,6 +279,9 @@
},
"name": "Tryb otoczenia"
},
"amuse": {
"name": "Amuse"
},
"api-server": {
"description": "Pozwala na kontrolowanie YouTube Music poprzez podłączenie specjalnego serwera API",
"dialog": {
@ -484,6 +487,18 @@
"button": "Pobierz"
}
},
"equalizer": {
"description": "Dodaje equalizer do odtwarzacza",
"menu": {
"presets": {
"label": "Presety",
"list": {
"bass-booster": "Wzmacniacz basu"
}
}
},
"name": "Equalizer"
},
"exponential-volume": {
"description": "Sprawia, że suwak głośności jest proporcjonalna, dzięki czemu łatwiej jest wybrać niższą głośność.",
"name": "Proporcjonalna głośność"
@ -725,6 +740,9 @@
"line-effect": {
"label": "Efekty linijki",
"submenu": {
"fancy": {
"label": "Facy"
},
"focus": {
"label": "Fokus",
"tooltip": "Spraw, aby tylko obecna linijka była biała"

View File

@ -279,6 +279,13 @@
},
"name": "Modo ambiente"
},
"amuse": {
"description": "Adiciona suporte ao YouTube Music ao widget 'Reproduzindo agora' do Amuse da 6K Labs",
"name": "Amuse",
"response": {
"query": "Servidor API do Amuse em execução. GET /query para obter informações da música."
}
},
"api-server": {
"description": "Adiciona um servidor API para controlar o player",
"dialog": {

View File

@ -279,6 +279,13 @@
},
"name": "Режим Ambient"
},
"amuse": {
"description": "Добавляет поддержку виджета Amuse „сейчас играет“ от 6K Labs",
"name": "Amuse",
"response": {
"query": "Сервер Amuse API запущен. GET /query чтобы получить информацию о треке."
}
},
"api-server": {
"description": "Добавляет API сервер для контроля за плеером",
"dialog": {
@ -726,8 +733,8 @@
"synced-lyrics": {
"description": "Предоставляет синхронизированные слова для песен из таких источников, как LRClib.",
"errors": {
"fetch": "⚠️ - Возникла ошибка во время получения слов. Повторите попытку позже.",
"not-found": "⚠️ - Для этой песни не найдено слов."
"fetch": "⚠️\tПроизошла ошибка во время получения слов.\n\tПовторите попытку позже.",
"not-found": "⚠️ Для этой песни не найдено слов."
},
"menu": {
"default-text-string": {
@ -737,6 +744,10 @@
"line-effect": {
"label": "Эффект строки",
"submenu": {
"fancy": {
"label": "Красивый",
"tooltip": "Использовать большие эффекты строки, как в приложении"
},
"focus": {
"label": "Фокусировка",
"tooltip": "Делает только текущую строку белой"

View File

@ -279,6 +279,13 @@
},
"name": "微光效果"
},
"amuse": {
"description": "加入支援 6K Labs 的 Amuse OBS 外掛以取得 Youtube Music 現正播放資訊",
"name": "Amuse",
"response": {
"query": "Amuse API 伺服器正在運行中,使用 /query 以取得歌曲資訊。"
}
},
"api-server": {
"description": "新增伺服器以使用 API 控制播放器",
"dialog": {
@ -726,7 +733,7 @@
"synced-lyrics": {
"description": "使用 LRClib 等管道提供歌詞同步顯示。",
"errors": {
"fetch": "⚠️擷取歌詞時發生錯誤請稍後再試。",
"fetch": "⚠️\t擷取歌詞時發生錯誤\n請稍後再試。",
"not-found": "⚠️未找到該首歌曲的歌詞。"
},
"menu": {
@ -737,6 +744,10 @@
"line-effect": {
"label": "歌詞顯示效果",
"submenu": {
"fancy": {
"label": "絢麗",
"tooltip": "使用較為接近原生樣式並且放大當前該行歌詞"
},
"focus": {
"label": "高亮",
"tooltip": "高亮當前的歌詞"

View File

@ -505,10 +505,11 @@ app.once('browser-window-created', (_event, win) => {
// User agents are from https://developers.whatismybrowser.com/useragents/explore/
const originalUserAgent = win.webContents.userAgent;
const userAgents = {
mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12.1; rv:95.0) Gecko/20100101 Firefox/95.0',
mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.152 Safari/537.36',
windows:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
linux: 'Mozilla/5.0 (Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.152 Safari/537.36',
linux:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.152 Safari/537.36',
};
const updatedUserAgent = is.macOS()
@ -912,10 +913,12 @@ function removeContentSecurityPolicy(
delete details.responseHeaders['content-security-policy'];
delete details.responseHeaders['Content-Security-Policy'];
// Only allow cross-origin requests from music.youtube.com
delete details.responseHeaders['access-control-allow-origin'];
delete details.responseHeaders['Access-Control-Allow-Origin'];
details.responseHeaders['access-control-allow-origin'] = ['https://music.youtube.com'];
if (
!details.responseHeaders['access-control-allow-origin'] &&
!details.responseHeaders['Access-Control-Allow-Origin']
) {
details.responseHeaders['access-control-allow-origin'] = ['https://music.youtube.com'];
}
}
callback({ cancel: false, responseHeaders: details.responseHeaders });

View File

@ -24,9 +24,10 @@ export const backend = createBackend<BackendType, APIServerConfig>({
this.songInfo = songInfo;
});
ctx.ipc.on('ytmd:player-api-loaded', () =>
ctx.ipc.send('ytmd:setup-time-changed-listener'),
);
ctx.ipc.on('ytmd:player-api-loaded', () => {
ctx.ipc.send('ytmd:setup-time-changed-listener');
ctx.ipc.send('ytmd:setup-repeat-changed-listener');
});
ctx.ipc.on(
'ytmd:repeat-changed',
@ -59,6 +60,12 @@ export const backend = createBackend<BackendType, APIServerConfig>({
this.app.use('*', cors());
// for web remote control
this.app.use('*', async (ctx, next) => {
ctx.header('Access-Control-Request-Private-Network', 'true');
await next();
});
// middlewares
this.app.use('/api/*', async (ctx, next) => {
if (config.authStrategy !== AuthStrategy.NONE) {

View File

@ -5,15 +5,19 @@ import { ipcMain } from 'electron';
import getSongControls from '@/providers/song-controls';
import {
AuthHeadersSchema,
type ResponseSongInfo,
SongInfoSchema,
SeekSchema,
GoForwardScheme,
AddSongToQueueSchema,
GoBackSchema,
SwitchRepeatSchema,
SetVolumeSchema,
GoForwardScheme,
MoveSongInQueueSchema,
QueueParamsSchema,
SearchSchema,
SeekSchema,
SetFullscreenSchema,
SetQueueIndexSchema,
SetVolumeSchema,
SongInfoSchema,
SwitchRepeatSchema,
type ResponseSongInfo,
} from '../scheme';
import type { RepeatMode } from '@/types/datahost-get-state';
@ -22,6 +26,7 @@ import type { BackendContext } from '@/types/contexts';
import type { APIServerConfig } from '../../config';
import type { HonoApp } from '../types';
import type { QueueResponse } from '@/types/youtube-music-desktop-internal';
import type { Context } from 'hono';
const API_VERSION = 'v1';
@ -110,7 +115,6 @@ const routes = {
summary: 'seek',
description: 'Seek to a specific time in the current song',
request: {
headers: AuthHeadersSchema,
body: {
description: 'seconds to seek to',
content: {
@ -132,7 +136,6 @@ const routes = {
summary: 'go back',
description: 'Move the current song back by a number of seconds',
request: {
headers: AuthHeadersSchema,
body: {
description: 'seconds to go back',
content: {
@ -155,7 +158,6 @@ const routes = {
summary: 'go forward',
description: 'Move the current song forward by a number of seconds',
request: {
headers: AuthHeadersSchema,
body: {
description: 'seconds to go forward',
content: {
@ -207,7 +209,6 @@ const routes = {
summary: 'switch repeat',
description: 'Switch the repeat mode',
request: {
headers: AuthHeadersSchema,
body: {
description: 'number of times to click the repeat button',
content: {
@ -229,7 +230,6 @@ const routes = {
summary: 'set volume',
description: 'Set the volume of the player',
request: {
headers: AuthHeadersSchema,
body: {
description: 'volume to set',
content: {
@ -251,7 +251,6 @@ const routes = {
summary: 'set fullscreen',
description: 'Set the fullscreen state of the player',
request: {
headers: AuthHeadersSchema,
body: {
description: 'fullscreen state',
content: {
@ -297,7 +296,8 @@ const routes = {
},
},
}),
queueInfo: createRoute({
oldQueueInfo: createRoute({
deprecated: true,
method: 'get',
path: `/api/${API_VERSION}/queue-info`,
summary: 'get current queue info',
@ -316,7 +316,8 @@ const routes = {
},
},
}),
songInfo: createRoute({
oldSongInfo: createRoute({
deprecated: true,
method: 'get',
path: `/api/${API_VERSION}/song-info`,
summary: 'get current song info',
@ -335,6 +336,159 @@ const routes = {
},
},
}),
songInfo: createRoute({
method: 'get',
path: `/api/${API_VERSION}/song`,
summary: 'get current song info',
description: 'Get the current song info',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: SongInfoSchema,
},
},
},
204: {
description: 'No song info',
},
},
}),
queueInfo: createRoute({
method: 'get',
path: `/api/${API_VERSION}/queue`,
summary: 'get current queue info',
description: 'Get the current queue info',
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({}),
},
},
},
204: {
description: 'No queue info',
},
},
}),
addSongToQueue: createRoute({
method: 'post',
path: `/api/${API_VERSION}/queue`,
summary: 'add song to queue',
description: 'Add a song to the queue',
request: {
body: {
description: 'video id of the song to add',
content: {
'application/json': {
schema: AddSongToQueueSchema,
},
},
},
},
responses: {
204: {
description: 'Success',
},
},
}),
moveSongInQueue: createRoute({
method: 'patch',
path: `/api/${API_VERSION}/queue/{index}`,
summary: 'move song in queue',
description: 'Move a song in the queue',
request: {
params: QueueParamsSchema,
body: {
description: 'index to move the song to',
content: {
'application/json': {
schema: MoveSongInQueueSchema,
},
},
},
},
responses: {
204: {
description: 'Success',
},
},
}),
removeSongFromQueue: createRoute({
method: 'delete',
path: `/api/${API_VERSION}/queue/{index}`,
summary: 'remove song from queue',
description: 'Remove a song from the queue',
request: {
params: QueueParamsSchema,
},
responses: {
204: {
description: 'Success',
},
},
}),
setQueueIndex: createRoute({
method: 'patch',
path: `/api/${API_VERSION}/queue`,
summary: 'set queue index',
description: 'Set the current index of the queue',
request: {
body: {
description: 'index to move the song to',
content: {
'application/json': {
schema: SetQueueIndexSchema,
},
},
},
},
responses: {
204: {
description: 'Success',
},
},
}),
clearQueue: createRoute({
method: 'delete',
path: `/api/${API_VERSION}/queue`,
summary: 'clear queue',
description: 'Clear the queue',
responses: {
204: {
description: 'Success',
},
},
}),
search: createRoute({
method: 'post',
path: `/api/${API_VERSION}/search`,
summary: 'search for a song',
description: 'search for a song',
request: {
body: {
description: 'search query',
content: {
'application/json': {
schema: SearchSchema,
},
},
},
},
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: z.object({}),
},
},
},
},
}),
};
export const register = (
@ -464,7 +618,26 @@ export const register = (
ctx.status(200);
return ctx.json({ state: fullscreen });
});
app.openapi(routes.queueInfo, async (ctx) => {
const songInfo = (ctx: Context) => {
const info = songInfoGetter();
if (!info) {
ctx.status(204);
return ctx.body(null);
}
const body = { ...info };
delete body.image;
ctx.status(200);
return ctx.json(body satisfies ResponseSongInfo);
};
app.openapi(routes.oldSongInfo, songInfo);
app.openapi(routes.songInfo, songInfo);
// Queue
const queueInfo = async (ctx: Context) => {
const queueResponsePromise = new Promise<QueueResponse>((resolve) => {
ipcMain.once('ytmd:get-queue-response', (_, queue: QueueResponse) => {
return resolve(queue);
@ -482,19 +655,50 @@ export const register = (
ctx.status(200);
return ctx.json(info);
};
app.openapi(routes.oldQueueInfo, queueInfo);
app.openapi(routes.queueInfo, queueInfo);
app.openapi(routes.addSongToQueue, (ctx) => {
const { videoId } = ctx.req.valid('json');
controller.addSongToQueue(videoId);
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.songInfo, (ctx) => {
const info = songInfoGetter();
app.openapi(routes.moveSongInQueue, (ctx) => {
const index = Number(ctx.req.param('index'));
const { toIndex } = ctx.req.valid('json');
controller.moveSongInQueue(index, toIndex);
if (!info) {
ctx.status(204);
return ctx.body(null);
}
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.removeSongFromQueue, (ctx) => {
const index = Number(ctx.req.param('index'));
controller.removeSongFromQueue(index);
const body = { ...info };
delete body.image;
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.setQueueIndex, (ctx) => {
const { index } = ctx.req.valid('json');
controller.setQueueIndex(index);
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.clearQueue, (ctx) => {
controller.clearQueue();
ctx.status(204);
return ctx.body(null);
});
app.openapi(routes.search, async (ctx) => {
const { query } = ctx.req.valid('json');
const response = await controller.search(query);
ctx.status(200);
return ctx.json(body satisfies ResponseSongInfo);
return ctx.json(response as object);
});
};

View File

@ -1,11 +1,5 @@
import { z } from '@hono/zod-openapi';
export const AuthHeadersSchema = z.object({
authorization: z.string().openapi({
example: 'Bearer token',
}),
});
export type JWTPayload = z.infer<typeof JWTPayloadSchema>;
export const JWTPayloadSchema = z.object({
id: z.string(),

View File

@ -6,3 +6,5 @@ export * from './go-forward';
export * from './switch-repeat';
export * from './set-volume';
export * from './set-fullscreen';
export * from './queue';
export * from './search';

View File

@ -0,0 +1,15 @@
import { z } from '@hono/zod-openapi';
export const QueueParamsSchema = z.object({
index: z.coerce.number().int().nonnegative(),
});
export const AddSongToQueueSchema = z.object({
videoId: z.string(),
});
export const MoveSongInQueueSchema = z.object({
toIndex: z.number(),
});
export const SetQueueIndexSchema = z.object({
index: z.number().int().nonnegative(),
});

View File

@ -0,0 +1,5 @@
import { z } from '@hono/zod-openapi';
export const SearchSchema = z.object({
query: z.string(),
});

View File

@ -63,6 +63,11 @@ let yt: Innertube;
let win: BrowserWindow;
let playingUrl: string;
const isYouTubePremium = () =>
win.webContents.executeJavaScript(
'!document.querySelector(\'#endpoint[href="/music_premium"]\')',
) as Promise<boolean>;
const sendError = (error: Error, source?: string) => {
win.setProgressBar(-1); // Close progress bar
setBadge(0); // Close badge
@ -313,7 +318,7 @@ async function downloadSongUnsafe(
}
const downloadOptions: FormatOptions = {
type: 'audio', // Audio, video or video+audio
type: (await isYouTubePremium()) ? 'audio' : 'video+audio', // Audio, video or video+audio
quality: 'best', // Best, bestefficiency, 144p, 240p, 480p, 720p and so on.
format: 'any', // Media container format
};
@ -585,20 +590,17 @@ export async function downloadPlaylist(givenUrl?: string | URL) {
return;
}
if (
!playlist ||
!playlist.items ||
playlist.items.length === 0 ||
!playlist.header ||
!('title' in playlist.header)
) {
if (!playlist || !playlist.items || playlist.items.length === 0) {
sendError(
new Error(t('plugins.downloader.backend.feedback.playlist-is-empty')),
);
return;
}
const normalPlaylistTitle = playlist.header?.title?.text;
const normalPlaylistTitle =
playlist.header && 'title' in playlist.header
? playlist.header?.title?.text
: undefined;
const playlistTitle =
normalPlaylistTitle ??
playlist.page.contents_memo

View File

@ -612,7 +612,9 @@ export default createPlugin<
const accountData = renderer.data as RawAccountData;
this.me = {
handleId: accountData.channelHandle.runs[0].text,
handleId:
accountData.channelHandle.runs[0].text ??
accountData.accountName.runs[0].text,
name: accountData.accountName.runs[0].text,
thumbnail: accountData.accountPhoto.thumbnails[0].url,
};

View File

@ -6,7 +6,7 @@ import { t } from '@/i18n';
import type { ConnectionEventUnion } from '@/plugins/music-together/connection';
import type { Profile, VideoData } from '../types';
import type { QueueItem } from '@/types/datahost-get-state';
import type { QueueElement } from '@/types/queue';
import type { QueueElement, Store } from '@/types/queue';
const getHeaderPayload = (() => {
let payload: {
@ -266,7 +266,8 @@ export class Queue {
}
if (this.originalDispatch)
this.queue.queue.store.store.dispatch = this.originalDispatch;
this.queue.queue.store.store.dispatch = this
.originalDispatch as Store['dispatch'];
}
injection() {
@ -295,7 +296,11 @@ export class Queue {
videoId: it!.videoId,
ownerId: this.owner!.id,
}) satisfies VideoData,
event.payload!.items!,
(
event.payload! as {
items: QueueItem[];
}
).items,
);
const index = this._videoList.length + videoList.length - 1;
@ -334,7 +339,11 @@ export class Queue {
videoId: it!.videoId,
ownerId: this.owner!.id,
}) satisfies VideoData,
event.payload!.items!,
(
event.payload! as {
items: QueueItem[];
}
).items,
),
},
});
@ -407,7 +416,13 @@ export class Queue {
},
},
};
this.originalDispatch?.call(fakeContext, event);
this.originalDispatch?.call(
fakeContext,
event as {
type: string;
payload?: { items?: QueueItem[] | undefined } | undefined;
},
);
};
}

View File

@ -1,4 +1,5 @@
import type { LyricProvider, LyricResult, SearchSongInfo } from '../types';
import type { YouTubeMusicAppElement } from '@/types/youtube-music-app-element';
const headers = {
'Accept': 'application/json',
@ -102,19 +103,17 @@ export class YTMusic implements LyricProvider {
.padStart(2, '0')}.${remaining.toString().padStart(2, '0')}`;
}
private ENDPOINT = 'https://youtubei.googleapis.com/youtubei/v1/';
// RATE LIMITED (2 req per sec)
private PROXIED_ENDPOINT = 'https://ytmbrowseproxy.zvz.be/';
private fetchNext(videoId: string) {
return fetch(this.ENDPOINT + 'next?prettyPrint=false', {
headers,
method: 'POST',
body: JSON.stringify({
videoId,
context: { client },
}),
}).then((res) => res.json()) as Promise<NextData>;
const app = document.querySelector<YouTubeMusicAppElement>('ytmusic-app');
if (!app) return null;
return app.networkManager.fetch('/next?prettyPrint=false', {
videoId,
}) as Promise<NextData>;
}
private fetchBrowse(browseId: string) {

View File

@ -190,7 +190,7 @@
position: sticky;
top: var(--top, 0);
z-index: 100;
background-color: var(--ytmusic-background);
backdrop-filter: blur(5px);
transition: top 325ms ease-in-out;
}

View File

@ -1,5 +1,5 @@
// This is used for to control the songs
import { BrowserWindow } from 'electron';
import { BrowserWindow, ipcMain } from 'electron';
// see protocol-handler.ts
type ArgsType<T> = T | string[] | undefined;
@ -24,6 +24,16 @@ const parseBooleanFromArgsType = (args: ArgsType<boolean>) => {
}
};
const parseStringFromArgsType = (args: ArgsType<string>) => {
if (typeof args === 'string') {
return args;
} else if (Array.isArray(args)) {
return args[0];
} else {
return null;
}
};
export default (win: BrowserWindow) => {
return {
// Playback
@ -80,11 +90,49 @@ export default (win: BrowserWindow) => {
win.webContents.send('ytmd:get-queue');
},
muteUnmute: () => win.webContents.send('ytmd:toggle-mute'),
search: () => {
openSearchBox: () => {
win.webContents.sendInputEvent({
type: 'keyDown',
keyCode: '/',
});
},
// Queue
addSongToQueue: (videoId: string) => {
const videoIdValue = parseStringFromArgsType(videoId);
if (videoIdValue === null) return;
win.webContents.send('ytmd:add-to-queue', videoIdValue);
},
moveSongInQueue: (
fromIndex: ArgsType<number>,
toIndex: ArgsType<number>,
) => {
const fromIndexValue = parseNumberFromArgsType(fromIndex);
const toIndexValue = parseNumberFromArgsType(toIndex);
if (fromIndexValue === null || toIndexValue === null) return;
win.webContents.send('ytmd:move-in-queue', fromIndexValue, toIndexValue);
},
removeSongFromQueue: (index: ArgsType<number>) => {
const indexValue = parseNumberFromArgsType(index);
if (indexValue === null) return;
win.webContents.send('ytmd:remove-from-queue', indexValue);
},
setQueueIndex: (index: ArgsType<number>) => {
const indexValue = parseNumberFromArgsType(index);
if (indexValue === null) return;
win.webContents.send('ytmd:set-queue-index', indexValue);
},
clearQueue: () => win.webContents.send('ytmd:clear-queue'),
search: (query: string) =>
new Promise((resolve) => {
ipcMain.once('ytmd:search-results', (_, result) => {
resolve(result as string);
});
win.webContents.send('ytmd:search', query);
}),
};
};

View File

@ -22,6 +22,8 @@ import type { PluginConfig } from '@/types/plugins';
import type { YoutubePlayer } from '@/types/youtube-player';
import type { QueueElement } from '@/types/queue';
import type { QueueResponse } from '@/types/youtube-music-desktop-internal';
import type { YouTubeMusicAppElement } from '@/types/youtube-music-app-element';
import type { SearchBoxElement } from '@/types/search-box-element';
let api: (Element & YoutubePlayer) | null = null;
let isPluginLoaded = false;
@ -41,10 +43,6 @@ async function listenForApiLoad() {
}
}
interface YouTubeMusicAppElement extends HTMLElement {
navigate(page: string): void;
}
async function onApiLoaded() {
// Workaround for #2459
document
@ -159,6 +157,99 @@ async function onApiLoaded() {
} satisfies QueueResponse);
});
window.ipcRenderer.on('ytmd:add-to-queue', (_, videoId: string) => {
const queue = document.querySelector<QueueElement>('#queue');
const app = document.querySelector<YouTubeMusicAppElement>('ytmusic-app');
if (!app) return;
const store = queue?.queue.store.store;
if (!store) return;
app.networkManager
.fetch('/music/get_queue', {
queueContextParams: store.getState().queue.queueContextParams,
queueInsertPosition: 'INSERT_AT_END',
videoIds: [videoId],
})
.then((result) => {
if (
result &&
typeof result === 'object' &&
'queueDatas' in result &&
Array.isArray(result.queueDatas)
) {
queue?.dispatch({
type: 'ADD_ITEMS',
payload: {
nextQueueItemId: store.getState().queue.nextQueueItemId,
index: store.getState().queue.items.length ?? 0,
items: result.queueDatas
.map((it) =>
typeof it === 'object' && it && 'content' in it
? it.content
: null,
)
.filter(Boolean),
shuffleEnabled: false,
shouldAssignIds: true,
},
});
}
});
});
window.ipcRenderer.on(
'ytmd:move-in-queue',
(_, fromIndex: number, toIndex: number) => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.dispatch({
type: 'MOVE_ITEM',
payload: {
fromIndex,
toIndex,
},
});
},
);
window.ipcRenderer.on('ytmd:remove-from-queue', (_, index: number) => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.dispatch({
type: 'REMOVE_ITEM',
payload: index,
});
});
window.ipcRenderer.on('ytmd:set-queue-index', (_, index: number) => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.dispatch({
type: 'SET_INDEX',
payload: index,
});
});
window.ipcRenderer.on('ytmd:clear-queue', () => {
const queue = document.querySelector<QueueElement>('#queue');
queue?.queue.store.store.dispatch({
type: 'SET_PLAYER_PAGE_INFO',
payload: { open: false },
});
queue?.dispatch({
type: 'CLEAR',
});
});
window.ipcRenderer.on('ytmd:search', async (_, query: string) => {
const app = document.querySelector<YouTubeMusicAppElement>('ytmusic-app');
const searchBox =
document.querySelector<SearchBoxElement>('ytmusic-search-box');
if (!app || !searchBox) return;
const result = await app.networkManager.fetch('/search', {
query,
suggestStats: searchBox.getSearchboxStats(),
});
window.ipcRenderer.send('ytmd:search-results', result);
});
const video = document.querySelector('video')!;
const audioContext = new AudioContext();
const audioSource = audioContext.createMediaElementSource(video);

View File

@ -2,13 +2,8 @@ import type { YoutubePlayer } from '@/types/youtube-player';
import type { GetState, QueueItem } from '@/types/datahost-get-state';
type StoreState = GetState;
type Store = {
dispatch: (obj: {
type: string;
payload?: {
items?: QueueItem[];
};
}) => void;
export type Store = {
dispatch: (obj: { type: string; payload?: unknown }) => void;
getState: () => StoreState;
replaceReducer: (param1: unknown) => unknown;

View File

@ -0,0 +1,3 @@
export interface SearchBoxElement extends HTMLElement {
getSearchboxStats(): unknown;
}

View File

@ -0,0 +1,6 @@
export interface YouTubeMusicAppElement extends HTMLElement {
navigate(page: string): void;
networkManager: {
fetch: (url: string, data: unknown) => Promise<unknown>;
};
}