@ohmykreee/dsh-remote-control 0.0.0-stage → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/AGENTS.md +556 -0
  2. package/INSTALLATION.md +310 -0
  3. package/LICENSE +21 -0
  4. package/README.md +195 -2
  5. package/client.js +3 -0
  6. package/components.json +20 -0
  7. package/cordis.patch.yml +3 -0
  8. package/docs/README.en.md +196 -0
  9. package/docs/screenshots/en/overview.png +0 -0
  10. package/docs/screenshots/en/question.png +0 -0
  11. package/docs/screenshots/zh/overview.png +0 -0
  12. package/docs/screenshots/zh/question.png +0 -0
  13. package/icon.svg +24 -0
  14. package/index.js +2 -0
  15. package/lib/account.js +1 -0
  16. package/lib/app-mapping.js +1 -0
  17. package/lib/i18n.js +1 -0
  18. package/lib/media-enrich.js +1 -0
  19. package/lib/notifications.js +1 -0
  20. package/lib/performance.js +1 -0
  21. package/lib/platform/index.js +1 -0
  22. package/lib/probe-adapter.js +1 -0
  23. package/lib/probe-daemon.js +1 -0
  24. package/lib/probe.js +1 -0
  25. package/lib/qr.js +1 -0
  26. package/lib/runtime.js +1 -0
  27. package/lib/service.js +1 -0
  28. package/lib/sessions.js +1 -0
  29. package/lib/settings.js +1 -0
  30. package/lib/types.js +1 -0
  31. package/lib/util.js +1 -0
  32. package/lib/weather.js +1 -0
  33. package/lib/ws.js +1 -0
  34. package/locale/en.json +6 -0
  35. package/locale/zh.json +6 -0
  36. package/package.json +118 -4
  37. package/scripts/browser-shot.mjs +217 -0
  38. package/scripts/build-dsh.mjs +228 -0
  39. package/scripts/build-rust.mjs +319 -0
  40. package/scripts/build-web.mjs +17 -0
  41. package/scripts/check-npm-package.mjs +322 -0
  42. package/scripts/mock-preview.py +574 -0
  43. package/scripts/screenshot-compose.py +89 -0
  44. package/scripts/screenshot-device.sh +75 -0
  45. package/src/dsh/client/api.ts +68 -0
  46. package/src/dsh/client/i18n.ts +573 -0
  47. package/src/dsh/client/icons.ts +56 -0
  48. package/src/dsh/client/index.ts +95 -0
  49. package/src/dsh/client/logic.ts +310 -0
  50. package/src/dsh/client/panel.ts +483 -0
  51. package/src/dsh/client/sections/apps.ts +40 -0
  52. package/src/dsh/client/sections/balance.ts +38 -0
  53. package/src/dsh/client/sections/connection.ts +97 -0
  54. package/src/dsh/client/sections/notifications.ts +43 -0
  55. package/src/dsh/client/sections/pomodoro.ts +184 -0
  56. package/src/dsh/client/sections/preferences.ts +138 -0
  57. package/src/dsh/client/sections/system.ts +120 -0
  58. package/src/dsh/client/sections/weather.ts +276 -0
  59. package/src/dsh/client/styles.ts +1119 -0
  60. package/src/dsh/client/types.ts +366 -0
  61. package/src/dsh/client/ui.ts +142 -0
  62. package/src/dsh/index.ts +108 -0
  63. package/src/dsh/lib/account.ts +520 -0
  64. package/src/dsh/lib/app-mapping.ts +366 -0
  65. package/src/dsh/lib/i18n.ts +82 -0
  66. package/src/dsh/lib/media-enrich.ts +202 -0
  67. package/src/dsh/lib/notifications.ts +322 -0
  68. package/src/dsh/lib/performance.ts +279 -0
  69. package/src/dsh/lib/platform/index.ts +43 -0
  70. package/src/dsh/lib/probe-adapter.ts +275 -0
  71. package/src/dsh/lib/probe-daemon.ts +359 -0
  72. package/src/dsh/lib/probe.ts +395 -0
  73. package/src/dsh/lib/qr.ts +42 -0
  74. package/src/dsh/lib/runtime.ts +126 -0
  75. package/src/dsh/lib/service.ts +1645 -0
  76. package/src/dsh/lib/sessions.ts +907 -0
  77. package/src/dsh/lib/settings.ts +468 -0
  78. package/src/dsh/lib/types.ts +231 -0
  79. package/src/dsh/lib/util.ts +115 -0
  80. package/src/dsh/lib/weather.ts +532 -0
  81. package/src/dsh/lib/ws.ts +328 -0
  82. package/src/dsh/types/qrcode-generator.d.ts +16 -0
  83. package/src/rust/Cargo.lock +1431 -0
  84. package/src/rust/Cargo.toml +68 -0
  85. package/src/rust/src/cli.rs +233 -0
  86. package/src/rust/src/daemon.rs +140 -0
  87. package/src/rust/src/location/linux.rs +118 -0
  88. package/src/rust/src/location/macos.rs +26 -0
  89. package/src/rust/src/location/mod.rs +170 -0
  90. package/src/rust/src/location/windows.rs +205 -0
  91. package/src/rust/src/main.rs +42 -0
  92. package/src/rust/src/media/catalog.rs +454 -0
  93. package/src/rust/src/media/linux.rs +350 -0
  94. package/src/rust/src/media/macos/accessibility.rs +59 -0
  95. package/src/rust/src/media/macos.rs +366 -0
  96. package/src/rust/src/media/mod.rs +298 -0
  97. package/src/rust/src/media/vendor/apple_music.rs +90 -0
  98. package/src/rust/src/media/vendor/common.rs +186 -0
  99. package/src/rust/src/media/vendor/kugou.rs +52 -0
  100. package/src/rust/src/media/vendor/kuwo.rs +49 -0
  101. package/src/rust/src/media/vendor/mod.rs +75 -0
  102. package/src/rust/src/media/vendor/netease.rs +1715 -0
  103. package/src/rust/src/media/vendor/qishui.rs +36 -0
  104. package/src/rust/src/media/vendor/qqmusic.rs +33 -0
  105. package/src/rust/src/media/vendor/spotify.rs +46 -0
  106. package/src/rust/src/media/vendor/wesing.rs +39 -0
  107. package/src/rust/src/media/vendor/youtube_music.rs +49 -0
  108. package/src/rust/src/media/windows/accessibility.rs +85 -0
  109. package/src/rust/src/media/windows/audio.rs +116 -0
  110. package/src/rust/src/media/windows/private.rs +291 -0
  111. package/src/rust/src/media/windows.rs +775 -0
  112. package/src/rust/src/notifications/linux.rs +276 -0
  113. package/src/rust/src/notifications/macos.rs +382 -0
  114. package/src/rust/src/notifications/mod.rs +244 -0
  115. package/src/rust/src/notifications/windows.rs +565 -0
  116. package/src/rust/src/perf/common.rs +109 -0
  117. package/src/rust/src/perf/linux.rs +282 -0
  118. package/src/rust/src/perf/macos.rs +28 -0
  119. package/src/rust/src/perf/mod.rs +157 -0
  120. package/src/rust/src/perf/windows.rs +981 -0
  121. package/src/rust/src/util.rs +360 -0
  122. package/src/rust/src/volume/linux.rs +195 -0
  123. package/src/rust/src/volume/macos.rs +54 -0
  124. package/src/rust/src/volume/mod.rs +116 -0
  125. package/src/rust/src/volume/windows.rs +75 -0
  126. package/src/rust/tests/cli.rs +79 -0
  127. package/src/rust/tests/daemon.rs +107 -0
  128. package/src/svelte/App.svelte +385 -0
  129. package/src/svelte/index.html +18 -0
  130. package/src/svelte/lib/billing.svelte.ts +228 -0
  131. package/src/svelte/lib/components/BalanceCard.svelte +173 -0
  132. package/src/svelte/lib/components/ClockCard.svelte +183 -0
  133. package/src/svelte/lib/components/CountdownCard.svelte +261 -0
  134. package/src/svelte/lib/components/CpuMemoryCard.svelte +238 -0
  135. package/src/svelte/lib/components/EmptyState.svelte +34 -0
  136. package/src/svelte/lib/components/GpuCard.svelte +245 -0
  137. package/src/svelte/lib/components/HomePage.svelte +34 -0
  138. package/src/svelte/lib/components/MusicControls.svelte +256 -0
  139. package/src/svelte/lib/components/MusicCover.svelte +88 -0
  140. package/src/svelte/lib/components/MusicPage.svelte +116 -0
  141. package/src/svelte/lib/components/NotificationsCard.svelte +109 -0
  142. package/src/svelte/lib/components/Panel.svelte +22 -0
  143. package/src/svelte/lib/components/PerformancePage.svelte +55 -0
  144. package/src/svelte/lib/components/PomodoroCard.svelte +250 -0
  145. package/src/svelte/lib/components/QuestionSheet.svelte +354 -0
  146. package/src/svelte/lib/components/RadialGauge.svelte +99 -0
  147. package/src/svelte/lib/components/Rail.svelte +217 -0
  148. package/src/svelte/lib/components/SectionHeader.svelte +40 -0
  149. package/src/svelte/lib/components/SeekBar.svelte +421 -0
  150. package/src/svelte/lib/components/SessionList.svelte +364 -0
  151. package/src/svelte/lib/components/SessionStats.svelte +98 -0
  152. package/src/svelte/lib/components/SessionsPage.svelte +61 -0
  153. package/src/svelte/lib/components/SpendCard.svelte +105 -0
  154. package/src/svelte/lib/components/ThermalCard.svelte +249 -0
  155. package/src/svelte/lib/components/Toast.svelte +32 -0
  156. package/src/svelte/lib/components/WeatherIcon.svelte +40 -0
  157. package/src/svelte/lib/components/WeatherPanel.svelte +420 -0
  158. package/src/svelte/lib/components/ui/alert/alert-action.svelte +20 -0
  159. package/src/svelte/lib/components/ui/alert/alert-description.svelte +23 -0
  160. package/src/svelte/lib/components/ui/alert/alert-title.svelte +23 -0
  161. package/src/svelte/lib/components/ui/alert/alert.svelte +43 -0
  162. package/src/svelte/lib/components/ui/alert/index.ts +17 -0
  163. package/src/svelte/lib/components/ui/avatar/avatar-badge.svelte +26 -0
  164. package/src/svelte/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
  165. package/src/svelte/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
  166. package/src/svelte/lib/components/ui/avatar/avatar-group.svelte +23 -0
  167. package/src/svelte/lib/components/ui/avatar/avatar-image.svelte +17 -0
  168. package/src/svelte/lib/components/ui/avatar/avatar.svelte +26 -0
  169. package/src/svelte/lib/components/ui/avatar/index.ts +22 -0
  170. package/src/svelte/lib/components/ui/badge/badge.svelte +49 -0
  171. package/src/svelte/lib/components/ui/badge/index.ts +2 -0
  172. package/src/svelte/lib/components/ui/button/button.svelte +82 -0
  173. package/src/svelte/lib/components/ui/button/index.ts +17 -0
  174. package/src/svelte/lib/components/ui/card/card-action.svelte +23 -0
  175. package/src/svelte/lib/components/ui/card/card-content.svelte +20 -0
  176. package/src/svelte/lib/components/ui/card/card-description.svelte +20 -0
  177. package/src/svelte/lib/components/ui/card/card-footer.svelte +20 -0
  178. package/src/svelte/lib/components/ui/card/card-header.svelte +23 -0
  179. package/src/svelte/lib/components/ui/card/card-title.svelte +20 -0
  180. package/src/svelte/lib/components/ui/card/card.svelte +22 -0
  181. package/src/svelte/lib/components/ui/card/index.ts +25 -0
  182. package/src/svelte/lib/components/ui/dialog/dialog-close.svelte +11 -0
  183. package/src/svelte/lib/components/ui/dialog/dialog-content.svelte +48 -0
  184. package/src/svelte/lib/components/ui/dialog/dialog-description.svelte +17 -0
  185. package/src/svelte/lib/components/ui/dialog/dialog-footer.svelte +32 -0
  186. package/src/svelte/lib/components/ui/dialog/dialog-header.svelte +20 -0
  187. package/src/svelte/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
  188. package/src/svelte/lib/components/ui/dialog/dialog-portal.svelte +7 -0
  189. package/src/svelte/lib/components/ui/dialog/dialog-title.svelte +17 -0
  190. package/src/svelte/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
  191. package/src/svelte/lib/components/ui/dialog/dialog.svelte +7 -0
  192. package/src/svelte/lib/components/ui/dialog/index.ts +34 -0
  193. package/src/svelte/lib/components/ui/empty/empty-content.svelte +23 -0
  194. package/src/svelte/lib/components/ui/empty/empty-description.svelte +23 -0
  195. package/src/svelte/lib/components/ui/empty/empty-header.svelte +20 -0
  196. package/src/svelte/lib/components/ui/empty/empty-media.svelte +41 -0
  197. package/src/svelte/lib/components/ui/empty/empty-title.svelte +20 -0
  198. package/src/svelte/lib/components/ui/empty/empty.svelte +23 -0
  199. package/src/svelte/lib/components/ui/empty/index.ts +22 -0
  200. package/src/svelte/lib/components/ui/input/index.ts +7 -0
  201. package/src/svelte/lib/components/ui/input/input.svelte +48 -0
  202. package/src/svelte/lib/components/ui/progress/index.ts +7 -0
  203. package/src/svelte/lib/components/ui/progress/progress.svelte +27 -0
  204. package/src/svelte/lib/components/ui/scroll-area/index.ts +10 -0
  205. package/src/svelte/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte +30 -0
  206. package/src/svelte/lib/components/ui/scroll-area/scroll-area.svelte +43 -0
  207. package/src/svelte/lib/components/ui/separator/index.ts +7 -0
  208. package/src/svelte/lib/components/ui/separator/separator.svelte +21 -0
  209. package/src/svelte/lib/components/ui/sheet/index.ts +34 -0
  210. package/src/svelte/lib/components/ui/sheet/sheet-close.svelte +7 -0
  211. package/src/svelte/lib/components/ui/sheet/sheet-content.svelte +55 -0
  212. package/src/svelte/lib/components/ui/sheet/sheet-description.svelte +17 -0
  213. package/src/svelte/lib/components/ui/sheet/sheet-footer.svelte +20 -0
  214. package/src/svelte/lib/components/ui/sheet/sheet-header.svelte +20 -0
  215. package/src/svelte/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
  216. package/src/svelte/lib/components/ui/sheet/sheet-portal.svelte +7 -0
  217. package/src/svelte/lib/components/ui/sheet/sheet-title.svelte +17 -0
  218. package/src/svelte/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
  219. package/src/svelte/lib/components/ui/sheet/sheet.svelte +7 -0
  220. package/src/svelte/lib/components/ui/skeleton/index.ts +7 -0
  221. package/src/svelte/lib/components/ui/skeleton/skeleton.svelte +17 -0
  222. package/src/svelte/lib/components/ui/slider/index.ts +7 -0
  223. package/src/svelte/lib/components/ui/slider/slider.svelte +52 -0
  224. package/src/svelte/lib/components/ui/tooltip/index.ts +19 -0
  225. package/src/svelte/lib/components/ui/tooltip/tooltip-content.svelte +51 -0
  226. package/src/svelte/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
  227. package/src/svelte/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
  228. package/src/svelte/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  229. package/src/svelte/lib/components/ui/tooltip/tooltip.svelte +7 -0
  230. package/src/svelte/lib/countdown.svelte.ts +252 -0
  231. package/src/svelte/lib/format.ts +199 -0
  232. package/src/svelte/lib/i18n.svelte.ts +583 -0
  233. package/src/svelte/lib/media.ts +32 -0
  234. package/src/svelte/lib/pages.ts +18 -0
  235. package/src/svelte/lib/pomodoro.svelte.ts +223 -0
  236. package/src/svelte/lib/session-tree.ts +80 -0
  237. package/src/svelte/lib/state.svelte.ts +359 -0
  238. package/src/svelte/lib/timer-alarm.svelte.ts +319 -0
  239. package/src/svelte/lib/types.ts +306 -0
  240. package/src/svelte/lib/utils.ts +8 -0
  241. package/src/svelte/lib/weather.ts +84 -0
  242. package/src/svelte/lib/ws.ts +104 -0
  243. package/src/svelte/main.ts +13 -0
  244. package/src/svelte/styles/global.css +315 -0
  245. package/src/svelte/vite-env.d.ts +12 -0
  246. package/static/fonts/LICENSE.txt +287 -0
  247. package/static/icon.svg +24 -0
  248. package/static/manifest.webmanifest +18 -0
  249. package/svelte.config.js +5 -0
  250. package/tsconfig.dsh-client.json +18 -0
  251. package/tsconfig.dsh.json +21 -0
  252. package/tsconfig.json +20 -0
  253. package/vite.config.js +31 -0
  254. package/web/assets/index-DLdSfnJ3.css +1 -0
  255. package/web/assets/index-YNE8X0Uy.js +6 -0
  256. package/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  257. package/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  258. package/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  259. package/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  260. package/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  261. package/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  262. package/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  263. package/web/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2 +0 -0
  264. package/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  265. package/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  266. package/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  267. package/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  268. package/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  269. package/web/assets/noto-sans-sc-100-wght-normal-DluiXYzi.woff2 +0 -0
  270. package/web/assets/noto-sans-sc-101-wght-normal-CPkZHnw0.woff2 +0 -0
  271. package/web/assets/noto-sans-sc-102-wght-normal-gzIulUH6.woff2 +0 -0
  272. package/web/assets/noto-sans-sc-103-wght-normal-CJfSBxFo.woff2 +0 -0
  273. package/web/assets/noto-sans-sc-104-wght-normal-B3QMglgE.woff2 +0 -0
  274. package/web/assets/noto-sans-sc-105-wght-normal-xXh_OMfk.woff2 +0 -0
  275. package/web/assets/noto-sans-sc-106-wght-normal-B9n3jMjP.woff2 +0 -0
  276. package/web/assets/noto-sans-sc-107-wght-normal-D7fvVprN.woff2 +0 -0
  277. package/web/assets/noto-sans-sc-108-wght-normal-J0_LhxVo.woff2 +0 -0
  278. package/web/assets/noto-sans-sc-109-wght-normal-Dsp-De_0.woff2 +0 -0
  279. package/web/assets/noto-sans-sc-110-wght-normal-DE_6NUrb.woff2 +0 -0
  280. package/web/assets/noto-sans-sc-111-wght-normal-C4S-TlTn.woff2 +0 -0
  281. package/web/assets/noto-sans-sc-112-wght-normal-BcFuP3w6.woff2 +0 -0
  282. package/web/assets/noto-sans-sc-113-wght-normal-Dk932kmj.woff2 +0 -0
  283. package/web/assets/noto-sans-sc-114-wght-normal-CO_5Xo-i.woff2 +0 -0
  284. package/web/assets/noto-sans-sc-115-wght-normal-BWy07H14.woff2 +0 -0
  285. package/web/assets/noto-sans-sc-116-wght-normal-B4HVAfHo.woff2 +0 -0
  286. package/web/assets/noto-sans-sc-117-wght-normal-siZzFqgK.woff2 +0 -0
  287. package/web/assets/noto-sans-sc-118-wght-normal-xhBB7f6V.woff2 +0 -0
  288. package/web/assets/noto-sans-sc-119-wght-normal-Ck_g2FGz.woff2 +0 -0
  289. package/web/assets/noto-sans-sc-21-wght-normal-D0x9-yBt.woff2 +0 -0
  290. package/web/assets/noto-sans-sc-22-wght-normal-BBUVTShg.woff2 +0 -0
  291. package/web/assets/noto-sans-sc-23-wght-normal-Du1oYWkE.woff2 +0 -0
  292. package/web/assets/noto-sans-sc-24-wght-normal-CmWjJ0gt.woff2 +0 -0
  293. package/web/assets/noto-sans-sc-25-wght-normal-BFHiqilg.woff2 +0 -0
  294. package/web/assets/noto-sans-sc-26-wght-normal-C0sF2Z2f.woff2 +0 -0
  295. package/web/assets/noto-sans-sc-27-wght-normal-COFdrquJ.woff2 +0 -0
  296. package/web/assets/noto-sans-sc-28-wght-normal-DMgdNu1o.woff2 +0 -0
  297. package/web/assets/noto-sans-sc-29-wght-normal-Cmq8em30.woff2 +0 -0
  298. package/web/assets/noto-sans-sc-30-wght-normal-CZWqZ3yU.woff2 +0 -0
  299. package/web/assets/noto-sans-sc-31-wght-normal-Wfi_lutW.woff2 +0 -0
  300. package/web/assets/noto-sans-sc-32-wght-normal-BRJQ4PY5.woff2 +0 -0
  301. package/web/assets/noto-sans-sc-33-wght-normal-CUVQXNAJ.woff2 +0 -0
  302. package/web/assets/noto-sans-sc-34-wght-normal-Bspba0wC.woff2 +0 -0
  303. package/web/assets/noto-sans-sc-35-wght-normal-K8fX_YPG.woff2 +0 -0
  304. package/web/assets/noto-sans-sc-36-wght-normal-CkITyjoT.woff2 +0 -0
  305. package/web/assets/noto-sans-sc-37-wght-normal-WCZ2zfEe.woff2 +0 -0
  306. package/web/assets/noto-sans-sc-38-wght-normal-DeM7CUce.woff2 +0 -0
  307. package/web/assets/noto-sans-sc-39-wght-normal-BMZNLWQU.woff2 +0 -0
  308. package/web/assets/noto-sans-sc-4-wght-normal-AR5bdDjz.woff2 +0 -0
  309. package/web/assets/noto-sans-sc-40-wght-normal-BQ5xpS5j.woff2 +0 -0
  310. package/web/assets/noto-sans-sc-41-wght-normal-Hthm7FFi.woff2 +0 -0
  311. package/web/assets/noto-sans-sc-42-wght-normal-eaS2gKds.woff2 +0 -0
  312. package/web/assets/noto-sans-sc-43-wght-normal-o-T53XXV.woff2 +0 -0
  313. package/web/assets/noto-sans-sc-44-wght-normal-BJH5J4Ez.woff2 +0 -0
  314. package/web/assets/noto-sans-sc-45-wght-normal-BKuxSCOJ.woff2 +0 -0
  315. package/web/assets/noto-sans-sc-46-wght-normal-BR_5Mu6e.woff2 +0 -0
  316. package/web/assets/noto-sans-sc-47-wght-normal-CyY7gKNX.woff2 +0 -0
  317. package/web/assets/noto-sans-sc-48-wght-normal-BNMfTR6k.woff2 +0 -0
  318. package/web/assets/noto-sans-sc-49-wght-normal-DX-0y7wg.woff2 +0 -0
  319. package/web/assets/noto-sans-sc-5-wght-normal-CfWugllB.woff2 +0 -0
  320. package/web/assets/noto-sans-sc-50-wght-normal-DAOGW-Dr.woff2 +0 -0
  321. package/web/assets/noto-sans-sc-51-wght-normal-BlFLOjcP.woff2 +0 -0
  322. package/web/assets/noto-sans-sc-52-wght-normal-OZjrybnw.woff2 +0 -0
  323. package/web/assets/noto-sans-sc-53-wght-normal-CwyWNT6x.woff2 +0 -0
  324. package/web/assets/noto-sans-sc-54-wght-normal-DM0nRe18.woff2 +0 -0
  325. package/web/assets/noto-sans-sc-55-wght-normal-DBK2wUOR.woff2 +0 -0
  326. package/web/assets/noto-sans-sc-56-wght-normal-C-VKK1LS.woff2 +0 -0
  327. package/web/assets/noto-sans-sc-57-wght-normal-Cdbfh5kq.woff2 +0 -0
  328. package/web/assets/noto-sans-sc-58-wght-normal-CflMKARZ.woff2 +0 -0
  329. package/web/assets/noto-sans-sc-59-wght-normal-BS4Kwzxx.woff2 +0 -0
  330. package/web/assets/noto-sans-sc-6-wght-normal-ChnZIQfQ.woff2 +0 -0
  331. package/web/assets/noto-sans-sc-60-wght-normal-B7QOYsXJ.woff2 +0 -0
  332. package/web/assets/noto-sans-sc-61-wght-normal-C8JjQS7w.woff2 +0 -0
  333. package/web/assets/noto-sans-sc-62-wght-normal-bS4wq1Gg.woff2 +0 -0
  334. package/web/assets/noto-sans-sc-63-wght-normal-BJr8P-OB.woff2 +0 -0
  335. package/web/assets/noto-sans-sc-64-wght-normal-DVB11t7j.woff2 +0 -0
  336. package/web/assets/noto-sans-sc-65-wght-normal-DMPCXBvK.woff2 +0 -0
  337. package/web/assets/noto-sans-sc-66-wght-normal-BobaG10V.woff2 +0 -0
  338. package/web/assets/noto-sans-sc-67-wght-normal-Dzk7WhCR.woff2 +0 -0
  339. package/web/assets/noto-sans-sc-68-wght-normal-DGbCD9xu.woff2 +0 -0
  340. package/web/assets/noto-sans-sc-69-wght-normal-Ctrv1EqA.woff2 +0 -0
  341. package/web/assets/noto-sans-sc-70-wght-normal-RgTO941D.woff2 +0 -0
  342. package/web/assets/noto-sans-sc-71-wght-normal-D9sG-CWv.woff2 +0 -0
  343. package/web/assets/noto-sans-sc-72-wght-normal-C4oWy3ul.woff2 +0 -0
  344. package/web/assets/noto-sans-sc-73-wght-normal-B1ZoOTgM.woff2 +0 -0
  345. package/web/assets/noto-sans-sc-74-wght-normal-F7dH_rZJ.woff2 +0 -0
  346. package/web/assets/noto-sans-sc-75-wght-normal-Cp2wp4si.woff2 +0 -0
  347. package/web/assets/noto-sans-sc-76-wght-normal-B494D7xS.woff2 +0 -0
  348. package/web/assets/noto-sans-sc-77-wght-normal-B3MVY9Ax.woff2 +0 -0
  349. package/web/assets/noto-sans-sc-78-wght-normal-5jX7iNMC.woff2 +0 -0
  350. package/web/assets/noto-sans-sc-79-wght-normal-BIoZy8fr.woff2 +0 -0
  351. package/web/assets/noto-sans-sc-80-wght-normal-Bje1Fxy1.woff2 +0 -0
  352. package/web/assets/noto-sans-sc-81-wght-normal-B2NN7Qi3.woff2 +0 -0
  353. package/web/assets/noto-sans-sc-82-wght-normal-CUggNZi4.woff2 +0 -0
  354. package/web/assets/noto-sans-sc-83-wght-normal-99rD8YO-.woff2 +0 -0
  355. package/web/assets/noto-sans-sc-84-wght-normal-zMBmUocm.woff2 +0 -0
  356. package/web/assets/noto-sans-sc-85-wght-normal-CjI3qdxG.woff2 +0 -0
  357. package/web/assets/noto-sans-sc-86-wght-normal-BHY8TVEH.woff2 +0 -0
  358. package/web/assets/noto-sans-sc-87-wght-normal-DioZSHnU.woff2 +0 -0
  359. package/web/assets/noto-sans-sc-88-wght-normal-BXYxwf2t.woff2 +0 -0
  360. package/web/assets/noto-sans-sc-89-wght-normal-ClMFrFaL.woff2 +0 -0
  361. package/web/assets/noto-sans-sc-90-wght-normal-BnOlPreP.woff2 +0 -0
  362. package/web/assets/noto-sans-sc-91-wght-normal-DTr9RQcS.woff2 +0 -0
  363. package/web/assets/noto-sans-sc-97-wght-normal-9u6Aiav-.woff2 +0 -0
  364. package/web/assets/noto-sans-sc-98-wght-normal-BR-H2C0V.woff2 +0 -0
  365. package/web/assets/noto-sans-sc-99-wght-normal-BI6XPoxp.woff2 +0 -0
  366. package/web/assets/noto-sans-sc-cyrillic-wght-normal-BGmsQlw-.woff2 +0 -0
  367. package/web/assets/noto-sans-sc-latin-ext-wght-normal-DuJTpTg_.woff2 +0 -0
  368. package/web/assets/noto-sans-sc-latin-wght-normal-DDW8mKAR.woff2 +0 -0
  369. package/web/assets/noto-sans-sc-vietnamese-wght-normal-Bi3ATJ76.woff2 +0 -0
  370. package/web/fonts/LICENSE.txt +287 -0
  371. package/web/icon.svg +24 -0
  372. package/web/index.html +19 -0
  373. package/web/manifest.webmanifest +18 -0
@@ -0,0 +1,454 @@
1
+ //! Cross-platform media application catalog.
2
+ //!
3
+ //! The probe talks to one system media interface per platform (SMTC on Windows,
4
+ //! MPRIS on Linux, MediaRemote/`nowplaying-cli` on macOS). A few desktop
5
+ //! players do not expose a complete system session, so the probe also knows how
6
+ //! to recognise their process/window identity and, as a last resort, read their
7
+ //! accessibility tree.
8
+ //!
9
+ //! This module owns the parts that are identical everywhere:
10
+ //!
11
+ //! * the list of supported applications and the ids/process names that map to
12
+ //! them (adapted from <https://github.com/Widdit/now-playing-service>),
13
+ //! * per-application window-title parsing (the reference project uses window
14
+ //! titles for NetEase/QQ/KuGou/KuWo/WeSing/Spotify/Cider),
15
+ //! * `MM:SS | MM:SS` progress parsing used by UI Automation/AT-SPI/AX readers,
16
+ //! * the tier/seek policy: transport seeking is only exposed by a complete
17
+ //! system session, never by accessibility, and only by a private interface
18
+ //! when that interface is explicitly real-time.
19
+ //!
20
+ //! No network API is used anywhere in this crate: the private tier reads local
21
+ //! client state (SQLite databases, local title files) only.
22
+
23
+ // Some fields/helpers are only used by one platform's adapter (for example the
24
+ // window-title parsers are Windows/macOS-only, while `seek_allowed` is used
25
+ // everywhere). Keep the catalog compiled on every target without per-platform
26
+ // dead-code noise.
27
+ #![allow(dead_code)]
28
+
29
+ /// A supported desktop player (or a browser acting as one).
30
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
31
+ pub enum App {
32
+ Netease,
33
+ QQMusic,
34
+ Kugou,
35
+ Kuwo,
36
+ Qishui,
37
+ WeSing,
38
+ Spotify,
39
+ AppleMusic,
40
+ YouTubeMusic,
41
+ Browser,
42
+ }
43
+
44
+ /// How much trust to place in an application's system media session.
45
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46
+ pub enum SmtcPolicy {
47
+ /// Accept the system session whenever it exposes metadata, transport and a
48
+ /// seekable timeline.
49
+ Trusted,
50
+ /// Older builds of some clients advertise a seekable timeline while never
51
+ /// reporting a real position. Only accept the system session when it also
52
+ /// reports a non-zero position.
53
+ VerifyPosition,
54
+ /// The system session is useful for metadata/transport status but does not
55
+ /// report playback progress (current NetEase builds do this). Never treat
56
+ /// it as a complete timeline; fill progress from the private/accessibility
57
+ /// tiers instead.
58
+ MetadataOnly,
59
+ }
60
+
61
+ /// The private interface a client exposes, if any.
62
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
63
+ pub enum PrivateKind {
64
+ /// No useful private interface.
65
+ None,
66
+ /// Read the current track from the client's top-level window title.
67
+ WindowTitle,
68
+ /// Cider keeps the current Apple Music title in `Plugins/title.txt`.
69
+ CiderTitleFile,
70
+ }
71
+
72
+ /// Which layer produced a media payload.
73
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
74
+ #[allow(dead_code)]
75
+ pub enum Tier {
76
+ System,
77
+ Private,
78
+ Accessibility,
79
+ }
80
+
81
+ impl Tier {
82
+ pub fn as_str(self) -> &'static str {
83
+ match self {
84
+ Tier::System => "system",
85
+ Tier::Private => "private",
86
+ Tier::Accessibility => "accessibility",
87
+ }
88
+ }
89
+ }
90
+
91
+ /// One application description.
92
+ #[derive(Debug, Clone, Copy)]
93
+ pub struct Profile {
94
+ pub app: App,
95
+ /// Stable fallback id when no executable name is available.
96
+ pub app_id: &'static str,
97
+ /// Human-readable name, used only in diagnostics.
98
+ pub name: &'static str,
99
+ /// Lower-case executable stems (without `.exe`/`.app`).
100
+ pub process_names: &'static [&'static str],
101
+ /// Lower-case substrings of an SMTC AUMID / MPRIS bus name / bundle id.
102
+ pub id_keywords: &'static [&'static str],
103
+ pub smtc_policy: SmtcPolicy,
104
+ /// Whether a progress read through the accessibility API is worthwhile.
105
+ pub accessibility: bool,
106
+ pub private: PrivateKind,
107
+ }
108
+
109
+ /// All known profiles, in match priority order.
110
+ pub const PROFILES: &[Profile] = &[
111
+ Profile {
112
+ app: App::Netease,
113
+ app_id: "cloudmusic.exe",
114
+ name: "网易云音乐",
115
+ process_names: &["cloudmusic", "netease-cloud-music", "netease cloud music"],
116
+ id_keywords: &["cloudmusic", "netease", "163music", "网易云"],
117
+ // Current NetEase builds expose SMTC metadata and transport controls
118
+ // but not playback progress. Use SMTC as the metadata source, then fill
119
+ // the position/duration from the window/UI Automation fallback.
120
+ smtc_policy: SmtcPolicy::MetadataOnly,
121
+ accessibility: true,
122
+ private: PrivateKind::WindowTitle,
123
+ },
124
+ Profile {
125
+ app: App::QQMusic,
126
+ app_id: "QQMusic.exe",
127
+ name: "QQ音乐",
128
+ process_names: &["qqmusic", "qq music"],
129
+ id_keywords: &["qqmusic", "qq music", "qq音乐"],
130
+ smtc_policy: SmtcPolicy::VerifyPosition,
131
+ accessibility: false,
132
+ private: PrivateKind::WindowTitle,
133
+ },
134
+ Profile {
135
+ app: App::Kugou,
136
+ app_id: "KuGou.exe",
137
+ name: "酷狗音乐",
138
+ process_names: &["kugou", "kgmusic", "ku gou"],
139
+ id_keywords: &["kugou", "kgmusic", "酷狗"],
140
+ smtc_policy: SmtcPolicy::VerifyPosition,
141
+ accessibility: false,
142
+ private: PrivateKind::WindowTitle,
143
+ },
144
+ Profile {
145
+ app: App::Kuwo,
146
+ app_id: "KwMusic.exe",
147
+ name: "酷我音乐",
148
+ process_names: &["kwmusic", "kwservice", "kuwo"],
149
+ id_keywords: &["kwmusic", "kuwo", "酷我"],
150
+ smtc_policy: SmtcPolicy::VerifyPosition,
151
+ accessibility: false,
152
+ private: PrivateKind::WindowTitle,
153
+ },
154
+ Profile {
155
+ app: App::Qishui,
156
+ app_id: "SodaMusic.exe",
157
+ name: "汽水音乐",
158
+ process_names: &["sodamusic", "soda music"],
159
+ id_keywords: &["sodamusic", "soda music", "汽水"],
160
+ smtc_policy: SmtcPolicy::VerifyPosition,
161
+ accessibility: false,
162
+ private: PrivateKind::None,
163
+ },
164
+ Profile {
165
+ app: App::WeSing,
166
+ app_id: "WeSing.exe",
167
+ name: "全民K歌",
168
+ process_names: &["wesing"],
169
+ id_keywords: &["wesing", "全民k歌"],
170
+ smtc_policy: SmtcPolicy::VerifyPosition,
171
+ accessibility: true,
172
+ private: PrivateKind::WindowTitle,
173
+ },
174
+ Profile {
175
+ app: App::Spotify,
176
+ app_id: "Spotify.exe",
177
+ name: "Spotify",
178
+ process_names: &["spotify"],
179
+ id_keywords: &["spotify"],
180
+ smtc_policy: SmtcPolicy::Trusted,
181
+ accessibility: false,
182
+ private: PrivateKind::WindowTitle,
183
+ },
184
+ Profile {
185
+ app: App::AppleMusic,
186
+ app_id: "AppleMusic.exe",
187
+ name: "Apple Music",
188
+ process_names: &["applemusic", "apple music", "cider"],
189
+ id_keywords: &["applemusic", "apple music", "com.apple.music", "cider"],
190
+ smtc_policy: SmtcPolicy::Trusted,
191
+ accessibility: false,
192
+ private: PrivateKind::CiderTitleFile,
193
+ },
194
+ Profile {
195
+ app: App::YouTubeMusic,
196
+ app_id: "YouTube Music.exe",
197
+ name: "YouTube Music",
198
+ process_names: &["youtube-music", "youtubemusic", "youtube music", "ytmusic"],
199
+ id_keywords: &["youtube-music", "youtubemusic", "youtube music", "ytmusic"],
200
+ smtc_policy: SmtcPolicy::Trusted,
201
+ accessibility: false,
202
+ private: PrivateKind::WindowTitle,
203
+ },
204
+ Profile {
205
+ app: App::Browser,
206
+ app_id: "browser",
207
+ name: "浏览器",
208
+ process_names: &[
209
+ "chrome",
210
+ "msedge",
211
+ "firefox",
212
+ "brave",
213
+ "opera",
214
+ "vivaldi",
215
+ "arc",
216
+ "qqbrowser",
217
+ "sogou",
218
+ "360se",
219
+ "360chrome",
220
+ ],
221
+ id_keywords: &[
222
+ "chrome",
223
+ "msedge",
224
+ "firefox",
225
+ "brave",
226
+ "opera",
227
+ "vivaldi",
228
+ "arc",
229
+ "qqbrowser",
230
+ "sogou",
231
+ "360se",
232
+ "360chrome",
233
+ "electron.app",
234
+ ],
235
+ smtc_policy: SmtcPolicy::Trusted,
236
+ accessibility: false,
237
+ private: PrivateKind::None,
238
+ },
239
+ ];
240
+
241
+ /// Find a profile from a system media id (AUMID, MPRIS name or bundle id).
242
+ pub fn profile_for_id(id: &str) -> Option<&'static Profile> {
243
+ let id = id.to_lowercase();
244
+ if id.trim().is_empty() {
245
+ return None;
246
+ }
247
+ PROFILES.iter().find(|profile| {
248
+ profile
249
+ .id_keywords
250
+ .iter()
251
+ .any(|keyword| id.contains(keyword))
252
+ })
253
+ }
254
+
255
+ /// Find a profile from an executable/process name (path allowed).
256
+ pub fn profile_for_process(process: &str) -> Option<&'static Profile> {
257
+ let lower = process.to_lowercase();
258
+ let name = lower
259
+ .rsplit(['\\', '/'])
260
+ .next()
261
+ .unwrap_or(&lower)
262
+ .trim()
263
+ .trim_end_matches(".exe")
264
+ .trim_end_matches(".app");
265
+ if name.is_empty() {
266
+ return None;
267
+ }
268
+ PROFILES.iter().find(|profile| {
269
+ profile
270
+ .process_names
271
+ .iter()
272
+ .any(|candidate| name == *candidate || name.contains(candidate))
273
+ })
274
+ }
275
+
276
+ #[allow(dead_code)]
277
+ pub fn profile_for_app(app: App) -> Option<&'static Profile> {
278
+ PROFILES.iter().find(|profile| profile.app == app)
279
+ }
280
+
281
+ impl Profile {
282
+ /// Parse this client's window title through its vendor adapter.
283
+ pub fn parse_title(&self, raw: &str) -> Option<ParsedTitle> {
284
+ crate::media::vendor::parse_title(self.app, raw)
285
+ }
286
+ }
287
+
288
+ use super::Media;
289
+
290
+ /// A complete system session is the only source allowed to expose seek.
291
+ ///
292
+ /// This is shared by the vendor dispatchers so each vendor decides whether its
293
+ /// system-API path is usable, while the generic fallback uses the same rule.
294
+ pub fn system_complete(profile: Option<&Profile>, media: &Media) -> bool {
295
+ if media.title.trim().is_empty() {
296
+ return false;
297
+ }
298
+ if media.playback_status == "Closed" {
299
+ return false;
300
+ }
301
+ if media.end_time <= media.start_time || media.end_time <= 0.0 {
302
+ return false;
303
+ }
304
+ if !media.can_seek {
305
+ return false;
306
+ }
307
+ match profile
308
+ .map(|profile| profile.smtc_policy)
309
+ .unwrap_or(SmtcPolicy::Trusted)
310
+ {
311
+ SmtcPolicy::Trusted => true,
312
+ // Older builds (notably some QQ/KuGou releases) advertise a seekable
313
+ // timeline but keep the position at zero. Require an actual position
314
+ // report for those adapters.
315
+ SmtcPolicy::VerifyPosition => media.position > 0.01,
316
+ // NetEase exposes SMTC metadata/controls but not playback progress.
317
+ // It must never be treated as a complete timeline (and never grants
318
+ // seek); the accessibility/local tier supplies the displayed progress.
319
+ SmtcPolicy::MetadataOnly => false,
320
+ }
321
+ }
322
+
323
+ /// A title/artist pair parsed from a client window title.
324
+ #[derive(Debug, Clone, PartialEq, Eq)]
325
+ pub struct ParsedTitle {
326
+ pub title: String,
327
+ pub artist: String,
328
+ }
329
+
330
+ /// Parse `MM:SS`, `H:MM:SS`, `MM:SS | MM:SS` and `MM:SS/MM:SS`.
331
+ pub fn parse_progress_text(text: &str) -> Option<(f64, f64)> {
332
+ let cleaned: String = text
333
+ .chars()
334
+ .filter(|character| {
335
+ !character.is_whitespace()
336
+ && *character != '\u{00a0}'
337
+ && *character != '\u{3000}'
338
+ && *character != '\u{feff}'
339
+ })
340
+ .collect();
341
+ if cleaned.is_empty() {
342
+ return None;
343
+ }
344
+ let (left, right) = cleaned
345
+ .split_once('|')
346
+ .or_else(|| cleaned.split_once('|'))
347
+ .or_else(|| cleaned.split_once('/'))?;
348
+ let current = parse_time(left)?;
349
+ let total = parse_time(right)?;
350
+ if total <= 0.0 || current < 0.0 || current > total + 2.0 {
351
+ return None;
352
+ }
353
+ Some((current, total))
354
+ }
355
+
356
+ /// The seek rule shared by every platform.
357
+ ///
358
+ /// * A complete system session may expose seek.
359
+ /// * Accessibility readers are display-only.
360
+ /// * A private interface may expose seek only when it is explicitly fast enough
361
+ /// to survive the follow-up read (`private_realtime`).
362
+ pub fn seek_allowed(tier: Tier, system_complete: bool, private_realtime: bool) -> bool {
363
+ match tier {
364
+ Tier::System => system_complete,
365
+ Tier::Private => private_realtime,
366
+ Tier::Accessibility => false,
367
+ }
368
+ }
369
+
370
+ fn parse_time(value: &str) -> Option<f64> {
371
+ let value = value.replace(':', ":");
372
+ let parts: Vec<&str> = value.split(':').collect();
373
+ let (hours, minutes, seconds) = match parts.as_slice() {
374
+ [minutes, seconds] => (
375
+ 0.0,
376
+ minutes.parse::<f64>().ok()?,
377
+ seconds.parse::<f64>().ok()?,
378
+ ),
379
+ [hours, minutes, seconds] => (
380
+ hours.parse::<f64>().ok()?,
381
+ minutes.parse::<f64>().ok()?,
382
+ seconds.parse::<f64>().ok()?,
383
+ ),
384
+ _ => return None,
385
+ };
386
+ if minutes < 0.0 || seconds < 0.0 || seconds >= 60.0 {
387
+ return None;
388
+ }
389
+ if parts.len() == 3 && minutes >= 60.0 {
390
+ return None;
391
+ }
392
+ Some(hours * 3600.0 + minutes * 60.0 + seconds)
393
+ }
394
+
395
+ #[cfg(test)]
396
+ mod tests {
397
+ use super::*;
398
+
399
+ #[test]
400
+ fn detects_apps_from_ids_and_processes() {
401
+ assert_eq!(profile_for_id("cloudmusic.exe").unwrap().app, App::Netease);
402
+ assert_eq!(
403
+ profile_for_id("cloudmusic.exe").unwrap().smtc_policy,
404
+ SmtcPolicy::MetadataOnly
405
+ );
406
+ assert_eq!(
407
+ profile_for_id("SpotifyAB.SpotifyMusic_zpdnekdrzrea0!Spotify")
408
+ .unwrap()
409
+ .app,
410
+ App::Spotify
411
+ );
412
+ assert_eq!(
413
+ profile_for_id("com.apple.Music").unwrap().app,
414
+ App::AppleMusic
415
+ );
416
+ assert_eq!(
417
+ profile_for_process(r"D:\Program Files\KuGou\KuGou.exe")
418
+ .unwrap()
419
+ .app,
420
+ App::Kugou
421
+ );
422
+ assert_eq!(profile_for_process("KwService.exe").unwrap().app, App::Kuwo);
423
+ assert_eq!(
424
+ profile_for_process("youtube-music-desktop-app.exe")
425
+ .unwrap()
426
+ .app,
427
+ App::YouTubeMusic
428
+ );
429
+ assert_eq!(profile_for_id("MSEdge").unwrap().app, App::Browser);
430
+ assert_eq!(profile_for_id("MSEdge").unwrap().private, PrivateKind::None);
431
+ }
432
+
433
+ #[test]
434
+ fn parses_progress_text() {
435
+ assert_eq!(parse_progress_text("00:08 | 04:16"), Some((8.0, 256.0)));
436
+ assert_eq!(parse_progress_text("00:08 / 04:16"), Some((8.0, 256.0)));
437
+ assert_eq!(
438
+ parse_progress_text("1:02:03 | 2:00:00"),
439
+ Some((3723.0, 7200.0))
440
+ );
441
+ assert_eq!(parse_progress_text("00:08 | 04:16"), Some((8.0, 256.0)));
442
+ assert_eq!(parse_progress_text("not a progress"), None);
443
+ assert_eq!(parse_progress_text("05:00 | 04:00"), None);
444
+ }
445
+
446
+ #[test]
447
+ fn accessibility_never_exposes_seek() {
448
+ assert!(seek_allowed(Tier::System, true, false));
449
+ assert!(!seek_allowed(Tier::System, false, false));
450
+ assert!(!seek_allowed(Tier::Accessibility, true, false));
451
+ assert!(!seek_allowed(Tier::Private, false, false));
452
+ assert!(seek_allowed(Tier::Private, false, true));
453
+ }
454
+ }