├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── extensions.json ├── LICENSE ├── README.md ├── app-icon.png ├── app-icon.png.bak ├── auto-imports.d.ts ├── components.d.ts ├── eslint.config.js ├── index.html ├── package.json ├── postcss.config.js ├── public ├── icon.ico └── wuji.svg ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── capabilities │ ├── default.json │ └── desktop.json ├── gen │ └── android │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── wuji_app │ │ │ │ └── app │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_foreground.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values-night │ │ │ └── themes.xml │ │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ └── file_paths.xml │ │ ├── build.gradle.kts │ │ ├── buildSrc │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── com │ │ │ └── wuji_app │ │ │ └── app │ │ │ └── kotlin │ │ │ ├── BuildTask.kt │ │ │ └── RustPlugin.kt │ │ ├── gradle.properties │ │ ├── gradle │ │ ├── libs.versions.toml │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ └── settings.gradle ├── icons │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── 32x32.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ └── ios │ │ ├── AppIcon-20x20@1x.png │ │ ├── AppIcon-20x20@2x-1.png │ │ ├── AppIcon-20x20@2x.png │ │ ├── AppIcon-20x20@3x.png │ │ ├── AppIcon-29x29@1x.png │ │ ├── AppIcon-29x29@2x-1.png │ │ ├── AppIcon-29x29@2x.png │ │ ├── AppIcon-29x29@3x.png │ │ ├── AppIcon-40x40@1x.png │ │ ├── AppIcon-40x40@2x-1.png │ │ ├── AppIcon-40x40@2x.png │ │ ├── AppIcon-40x40@3x.png │ │ ├── AppIcon-512@2x.png │ │ ├── AppIcon-60x60@2x.png │ │ ├── AppIcon-60x60@3x.png │ │ ├── AppIcon-76x76@1x.png │ │ ├── AppIcon-76x76@2x.png │ │ └── AppIcon-83.5x83.5@2x.png ├── permissions │ ├── fetch-plugin │ │ └── default.toml │ └── proxy-plugin │ │ └── default.toml ├── src │ ├── fetch_plugin │ │ ├── commands.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ └── scope.rs │ ├── lib.rs │ ├── main.rs │ └── proxy_plugin │ │ ├── ad_remove.rs │ │ ├── commands.rs │ │ └── mod.rs └── tauri.conf.json ├── src ├── App.vue ├── MobileApp.vue ├── WinApp.vue ├── apis │ ├── hot │ │ ├── apiHot.ts │ │ └── index.ts │ └── ip.ts ├── assets │ ├── cd.svg │ ├── coverall.png │ ├── font │ │ └── alipuhui.otf │ ├── heart-fill.svg │ ├── icon.ico │ └── wuji.svg ├── components │ ├── BookShelfButton.vue │ ├── ComicShelfButton.vue │ ├── HorizonList.vue │ ├── LoadImage.vue │ ├── NavBar.vue │ ├── PlatformSwitch.vue │ ├── PositionBackTop.vue │ ├── ResponsiveGrid.vue │ ├── ResponsiveGrid2.vue │ ├── SongShelfSideBar.vue │ ├── View.vue │ ├── actionSheets │ │ ├── MoreOptions.vue │ │ ├── PlayingPlaylist.vue │ │ └── SongSelectShelf.vue │ ├── buttons │ │ ├── MobileBookTTSButton.vue │ │ ├── SongToFavoriteButton.vue │ │ └── WinBookTTSButton.vue │ ├── card │ │ ├── bookCards │ │ │ ├── MobileBookCard.vue │ │ │ ├── MobileShelfBookCard.vue │ │ │ ├── WinBookCard.vue │ │ │ └── WinShelfBookCard.vue │ │ ├── comicCards │ │ │ ├── MobileComicCard.vue │ │ │ ├── MobileShelfComicCard.vue │ │ │ ├── WinComicCard.vue │ │ │ └── WinShelfComicCard.vue │ │ ├── photoCards │ │ │ ├── MobilePhotoCard.vue │ │ │ ├── MobileShelfPhotoCard.vue │ │ │ ├── WinPhotoCard.vue │ │ │ └── WinShelfPhotoCard.vue │ │ ├── songCards │ │ │ ├── MobilePlaylistCard.vue │ │ │ ├── MobileShelfSongCard.vue │ │ │ ├── MobileSongCard.vue │ │ │ ├── WinPlaylistCard.vue │ │ │ ├── WinShelfSongCard.vue │ │ │ └── WinSongCard.vue │ │ └── videoCards │ │ │ ├── MobileShelfVideoCard.vue │ │ │ ├── MobileVideoCard.vue │ │ │ ├── WinShelfVideoCard.vue │ │ │ └── WinVideoCard.vue │ ├── codeEditor │ │ ├── Guide.vue │ │ ├── IEditor.vue │ │ ├── completions.ts │ │ ├── guides │ │ │ ├── common.md │ │ │ ├── index.ts │ │ │ ├── photoDetail.md │ │ │ ├── photoList.md │ │ │ ├── photoSearchList.md │ │ │ ├── songList.md │ │ │ ├── songLyric.md │ │ │ ├── songPlayUrl.md │ │ │ ├── songPlaylist.md │ │ │ ├── songPlaylistDetail.md │ │ │ ├── songSearchList.md │ │ │ └── songSearchPlaylist.md │ │ ├── templates.ts │ │ └── templates │ │ │ ├── photoTemplate.txt │ │ │ └── songTemplate.txt │ ├── dialogs │ │ ├── About.vue │ │ ├── CreateSourceConfim.vue │ │ ├── SwitchBookSource.vue │ │ └── SwitchComicSource.vue │ ├── media │ │ ├── error.ts │ │ ├── mobile │ │ │ ├── MobileFullScreenButton.vue │ │ │ ├── MobileQuickSeekButton.vue │ │ │ └── MobileVideoPlayer.vue │ │ └── win │ │ │ ├── BrowserFullScreenButton.vue │ │ │ ├── FullScreenButton.vue │ │ │ ├── PlayNextButton.vue │ │ │ ├── PlayPauseButton.vue │ │ │ ├── PlaybackRateButton.vue │ │ │ ├── ProgressBar.vue │ │ │ ├── QuickSeekButton.vue │ │ │ ├── TimeShow.vue │ │ │ └── VideoPlayer.vue │ ├── mobile │ │ ├── DraggableSongBar.vue │ │ ├── LeftPopup.vue │ │ ├── MobileHeader.vue │ │ ├── MobileSongBar.vue │ │ ├── Search.vue │ │ └── Search2.vue │ ├── pagination │ │ ├── FullPagination.vue │ │ └── SimplePagination.vue │ ├── photos │ │ ├── SongCardPhoto.vue │ │ └── SongPlaylistPhoto.vue │ ├── tabs │ │ ├── MobileBooksTab.vue │ │ ├── MobileComicsTab.vue │ │ └── MobileVideosTab.vue │ └── windows │ │ ├── BooksTab.vue │ │ ├── ComicsTab.vue │ │ ├── TaiChiAnimate.vue │ │ ├── VideosTab.vue │ │ ├── VolumeControl.vue │ │ ├── WinSearch.vue │ │ ├── WinSongBar.vue │ │ └── dialogs │ │ ├── AddBookShelf.vue │ │ ├── AddComicShelf.vue │ │ ├── AddPhotoShelf.vue │ │ ├── AddSongShelf.vue │ │ ├── AddVideoShelf.vue │ │ ├── ImportPlaylist.vue │ │ ├── ImportSubscribe.vue │ │ ├── ManageSubscribe.vue │ │ ├── RemoveBookShelf.vue │ │ ├── RemoveComicShelf.vue │ │ ├── RemoveSongShelf.vue │ │ ├── RemoveVideoShelf.vue │ │ ├── Setting.vue │ │ └── removePhotoShelf.vue ├── extensions │ ├── baseExtension.ts │ ├── book │ │ ├── index.ts │ │ ├── test.d.ts │ │ └── test.js │ ├── comic │ │ ├── index.ts │ │ ├── test.d.ts │ │ └── test.js │ ├── index.ts │ ├── photo │ │ ├── index.ts │ │ ├── test.d.ts │ │ └── test.js │ ├── song │ │ ├── index.ts │ │ ├── test.d.ts │ │ └── test.js │ └── video │ │ ├── index.ts │ │ ├── test.d.ts │ │ └── test.js ├── global.d.ts ├── main.ts ├── router │ └── index.ts ├── store │ ├── bookChaptersStore.ts │ ├── bookShelfStore.ts │ ├── bookStore.ts │ ├── comicShelfStore.ts │ ├── comicStore.ts │ ├── displayStore.ts │ ├── index.ts │ ├── photoShelfStore.ts │ ├── serverStore.ts │ ├── songCacheStore.ts │ ├── songShelfStore.ts │ ├── songStore.ts │ ├── store.ts │ ├── subscribeSourceStore.ts │ ├── ttsStore.ts │ ├── utils.ts │ └── videoShelfStore.ts ├── styles │ └── index.css ├── types │ ├── book.ts │ ├── index.ts │ └── song.ts ├── utils │ ├── cancelableFunction.ts │ ├── device.ts │ ├── directives │ │ ├── horizontalScroll.ts │ │ ├── hoverDelay.ts │ │ └── rememberScroll.ts │ ├── edge-tts │ │ ├── constants.ts │ │ ├── drm.ts │ │ ├── edge-tts-client.ts │ │ └── index.ts │ ├── extensionUtils.ts │ ├── fetch.ts │ ├── heartbeat.ts │ ├── index.ts │ ├── lruCache.ts │ ├── lyric.ts │ ├── neteaseMusic │ │ ├── crypto.ts │ │ ├── deviceid.txt │ │ ├── index.ts │ │ └── request.ts │ ├── permissions.ts │ ├── proxyUrl.ts │ ├── reader │ │ ├── reader-layout.d.ts │ │ ├── reader-layout.js │ │ └── types.ts │ ├── songCover.ts │ ├── tray.ts │ ├── trycatch.ts │ └── update.ts ├── views │ ├── auth │ │ ├── Login.vue │ │ └── User.vue │ ├── book │ │ ├── BookDetail.vue │ │ ├── BookRead.vue │ │ ├── BookShelf.vue │ │ └── index.vue │ ├── comic │ │ ├── ComicDetail.vue │ │ ├── ComicRead.vue │ │ ├── ComicShelf.vue │ │ └── index.vue │ ├── home │ │ └── index.vue │ ├── mobileView │ │ ├── auth │ │ │ ├── Login.vue │ │ │ └── User.vue │ │ ├── book │ │ │ ├── BookDetail.vue │ │ │ ├── BookRead.vue │ │ │ ├── BookShelf.vue │ │ │ └── index.vue │ │ ├── comic │ │ │ ├── ComicDetail.vue │ │ │ ├── ComicRead.vue │ │ │ ├── ComicShelf.vue │ │ │ └── index.vue │ │ ├── photo │ │ │ ├── PhotoDetail.vue │ │ │ ├── PhotoShelf.vue │ │ │ └── index.vue │ │ ├── song │ │ │ ├── PlayView.vue │ │ │ ├── PlaylistDetail.vue │ │ │ ├── SongShelf.vue │ │ │ └── index.vue │ │ ├── source │ │ │ ├── createSource │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ └── video │ │ │ ├── VideoDetail.vue │ │ │ ├── VideoShelf.vue │ │ │ └── index.vue │ ├── photo │ │ ├── PhotoDetail.vue │ │ ├── PhotoShelf.vue │ │ └── index.vue │ ├── song │ │ ├── PlayView.vue │ │ ├── PlaylistDetail.vue │ │ ├── SongShelf.vue │ │ └── index.vue │ ├── source │ │ ├── CreateSource.vue │ │ └── index.vue │ ├── video │ │ ├── VideoDetail.vue │ │ ├── VideoShelf.vue │ │ └── index.vue │ └── windowsView │ │ ├── auth │ │ ├── Login.vue │ │ └── User.vue │ │ ├── book │ │ ├── BookDetail.vue │ │ ├── BookRead.vue │ │ ├── BookShelf.vue │ │ └── index.vue │ │ ├── comic │ │ ├── ComicDetail.vue │ │ ├── ComicRead.vue │ │ ├── ComicShelf.vue │ │ └── index.vue │ │ ├── home │ │ └── index.vue │ │ ├── photo │ │ ├── PhotoDetail.vue │ │ ├── PhotoShelf.vue │ │ └── index.vue │ │ ├── song │ │ ├── PlayView.vue │ │ ├── PlaylistDetail.vue │ │ ├── SongShelf.vue │ │ └── index.vue │ │ ├── source │ │ ├── SourceManage.vue │ │ ├── createSource │ │ │ ├── index.vue │ │ │ └── views │ │ │ │ ├── PhotoDetail.vue │ │ │ │ ├── PhotoList.vue │ │ │ │ ├── PhotoSearchList.vue │ │ │ │ ├── SongList.vue │ │ │ │ ├── SongLyric.vue │ │ │ │ ├── SongPlayUrl.vue │ │ │ │ ├── SongPlaylist.vue │ │ │ │ ├── SongPlaylistDetail.vue │ │ │ │ ├── SongSearchList.vue │ │ │ │ └── SongSearchPlaylist.vue │ │ └── index.vue │ │ └── video │ │ ├── VideoDetail.vue │ │ ├── VideoShelf.vue │ │ └── index.vue └── vite-env.d.ts ├── tailwind.config.js ├── tauri-plugin-commands ├── .gitignore ├── Cargo.toml ├── README.md ├── android │ ├── .gitignore │ ├── build.gradle.kts │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── local.properties │ ├── proguard-rules.pro │ ├── settings.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ ├── Commands.kt │ │ │ └── CommandsPlugin.kt │ │ └── test │ │ └── java │ │ └── ExampleUnitTest.kt ├── build.rs ├── guest-js │ └── index.ts ├── package.json ├── permissions │ └── default.toml ├── rollup.config.js ├── src │ ├── commands.rs │ ├── desktop.rs │ ├── error.rs │ ├── lib.rs │ ├── mobile.rs │ └── models.rs └── tsconfig.json ├── tauri-plugin-fs ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE.spdx ├── LICENSE_APACHE-2.0 ├── LICENSE_MIT ├── README.md ├── SECURITY.md ├── android │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ ├── settings.gradle │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── FsPlugin.kt │ │ └── test │ │ └── java │ │ └── ExampleUnitTest.kt ├── api-iife.js ├── banner.png ├── build.rs ├── guest-js │ └── index.ts ├── package.json ├── permissions │ ├── app.toml │ ├── create-app-specific-dirs.toml │ ├── default.toml │ ├── deny-default.toml │ ├── deny-webview-data.toml │ ├── read-all.toml │ ├── read-app-specific-dirs-recursive.toml │ ├── read-dirs.toml │ ├── read-files.toml │ ├── read-meta.toml │ ├── scope.toml │ ├── write-all.toml │ └── write-files.toml ├── rollup.config.js ├── src │ ├── commands.rs │ ├── config.rs │ ├── desktop.rs │ ├── error.rs │ ├── file_path.rs │ ├── lib.rs │ ├── mobile.rs │ ├── models.rs │ ├── scope.rs │ └── watcher.rs └── tsconfig.json ├── tauri-plugin-mediasession ├── .gitignore ├── .gradle │ ├── 8.5 │ │ ├── checksums │ │ │ └── checksums.lock │ │ ├── dependencies-accessors │ │ │ ├── dependencies-accessors.lock │ │ │ └── gc.properties │ │ ├── fileChanges │ │ │ └── last-build.bin │ │ ├── fileHashes │ │ │ └── fileHashes.lock │ │ └── gc.properties │ ├── buildOutputCleanup │ │ ├── buildOutputCleanup.lock │ │ ├── cache.properties │ │ └── outputFiles.bin │ ├── config.properties │ └── vcs-1 │ │ └── gc.properties ├── Cargo.toml ├── README.md ├── android │ ├── .gitignore │ ├── Users │ │ └── 14438 │ │ │ └── Documents │ │ │ └── GitHub │ │ │ └── wuji-tauri │ │ │ └── tauri-plugin-mediasession │ │ │ └── android │ │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── MusicControlService.kt │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ ├── libs.versions.toml │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── local.properties │ ├── proguard-rules.pro │ ├── settings.gradle │ ├── src │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ ├── MediaSessionCallback.kt │ │ │ │ ├── MediaSessionPlugin.kt │ │ │ │ └── MediaSessionService.kt │ │ │ └── res │ │ │ │ └── drawable │ │ │ │ ├── ic_baseline_forward_30_24.xml │ │ │ │ ├── ic_baseline_pause_24.xml │ │ │ │ ├── ic_baseline_play_arrow_24.xml │ │ │ │ ├── ic_baseline_replay_30_24.xml │ │ │ │ ├── ic_baseline_skip_next_24.xml │ │ │ │ ├── ic_baseline_skip_previous_24.xml │ │ │ │ ├── ic_baseline_stop_24.xml │ │ │ │ ├── ic_baseline_volume_up_24.xml │ │ │ │ └── ic_stop.xml │ │ └── test │ │ │ └── java │ │ │ └── ExampleUnitTest.kt │ └── tauri-plugin-mediasession │ │ └── android │ │ └── src │ │ └── main │ │ └── java │ │ └── MusicControlService.kt ├── build.rs ├── guest-js │ ├── index.ts │ └── types.ts ├── package.json ├── permissions │ └── default.toml ├── rollup.config.js ├── src │ ├── commands.rs │ ├── desktop.rs │ ├── error.rs │ ├── lib.rs │ ├── mobile.rs │ └── models.rs └── tsconfig.json ├── test.html ├── test.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | *lock.yaml 27 | upload-keystore.jks 28 | .tauri 29 | test.html -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src-tauri/ 2 | node_modules/ 3 | dist/ 4 | public/ 5 | .git/ 6 | .vscode/ 7 | tauri-plugin-commands/ 8 | tauri-plugin-fs/ 9 | tauri-plugin-mediasession/ 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "printWidth": 80, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "lf", 11 | "htmlWhitespaceSensitivity": "ignore", 12 | "vueIndentScriptAndStyle": false, 13 | "overrides": [ 14 | { 15 | "files": "*.md", 16 | "options": { 17 | "printWidth": 100 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Vue.volar", 4 | "tauri-apps.tauri-vscode", 5 | "rust-lang.rust-analyzer" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/app-icon.png -------------------------------------------------------------------------------- /app-icon.png.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/app-icon.png.bak -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | // biome-ignore lint: disable 7 | export {} 8 | declare global { 9 | const showToast: typeof import('vant/es')['showToast'] 10 | } 11 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config'; 2 | 3 | export default antfu({ 4 | vue: true, 5 | typescript: true, 6 | stylistic: { 7 | semi: true, // 分号(与 Prettier 的 `semi: true` 一致) 8 | quotes: ['error', 'single'], // 单引号(与 Prettier 的 `singleQuote: true` 一致) 9 | indent: 2, // 缩进(与 Prettier 的 `tabWidth: 2` 一致) 10 | }, 11 | rules: { 12 | 'no-console': 'off', 13 | 'vue/script-indent': 'off', 14 | // 禁用与 Prettier 冲突的规则(@antfu/eslint-config 已内置 eslint-config-prettier) 15 | // 如果仍有冲突,可手动禁用: 16 | // 'operator-linebreak': 'off', // 关闭运算符换行检查(由 Prettier 接管) 17 | }, 18 | ignores: [ 19 | '**/node_modules/**', 20 | '**/dist/**', 21 | '**/build/**', 22 | '**/src-tauri/**', 23 | '**/tauri-plugin-commands/**', 24 | '**/tauri-plugin-fs/**', 25 | '**/tauri-plugin-mediasession/**', 26 | ], 27 | }); 28 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/public/icon.ico -------------------------------------------------------------------------------- /public/wuji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::try_build( 3 | tauri_build::Attributes::new() 4 | .plugin( 5 | "fetch-plugin", 6 | tauri_build::InlinedPlugin::new().commands(&[ 7 | "fetch", 8 | "fetch_cancel", 9 | "fetch_send", 10 | "fetch_read_body", 11 | ]), 12 | ) 13 | .plugin( 14 | "proxy-plugin", 15 | tauri_build::InlinedPlugin::new().commands(&["get_m3u8_url", "get_proxy_url"]), 16 | ), 17 | ) 18 | .expect("failed to run tauri-build"); 19 | } 20 | -------------------------------------------------------------------------------- /src-tauri/capabilities/desktop.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "desktop-capability", 3 | "platforms": [ 4 | "macOS", 5 | "windows", 6 | "linux" 7 | ], 8 | "windows": [ 9 | "main" 10 | ], 11 | "permissions": [ 12 | "updater:default" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src-tauri/gen/android/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false -------------------------------------------------------------------------------- /src-tauri/gen/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | key.properties 17 | 18 | /.tauri 19 | /tauri.settings.gradle 20 | keystore.properties -------------------------------------------------------------------------------- /src-tauri/gen/android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /src/main/java/com/wuji_app/app/generated 2 | /src/main/jniLibs/**/*.so 3 | /src/main/assets/tauri.conf.json 4 | /tauri.build.gradle.kts 5 | /proguard-tauri.pro 6 | /tauri.properties -------------------------------------------------------------------------------- /src-tauri/gen/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/java/com/wuji_app/app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.wuji_app.app 2 | 3 | import android.annotation.SuppressLint 4 | import android.os.Bundle 5 | import android.view.KeyEvent 6 | import android.webkit.WebView 7 | 8 | 9 | class MainActivity : TauriActivity() { 10 | private var wv: WebView? = null; 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | } 15 | 16 | @SuppressLint("SetJavaScriptEnabled") 17 | override fun onWebViewCreate(webView: WebView) { 18 | wv = webView 19 | wv!!.settings.setSupportMultipleWindows(true) 20 | wv!!.settings.javaScriptEnabled = true 21 | } 22 | 23 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { 24 | if (keyCode == KeyEvent.KEYCODE_BACK && event?.repeatCount == 0) { 25 | if (wv == null) { 26 | return super.onKeyDown(keyCode, event) 27 | } 28 | 29 | wv?.evaluateJavascript( 30 | """ 31 | try { 32 | window.androidBackCallback() ? "true" : "false" 33 | } catch (_) { 34 | "false" 35 | } 36 | """.trimIndent() 37 | ) { _ -> } 38 | return false 39 | } 40 | return super.onKeyDown(keyCode, event) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 无极 3 | 无极 4 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /src-tauri/gen/android/app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src-tauri/gen/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath("com.android.tools.build:gradle:8.5.1") 8 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | mavenCentral() 16 | maven("https://jitpack.io") 17 | 18 | } 19 | } 20 | 21 | tasks.register("clean").configure { 22 | delete("build") 23 | } -------------------------------------------------------------------------------- /src-tauri/gen/android/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | gradlePlugin { 6 | plugins { 7 | create("pluginsForCoolKids") { 8 | id = "rust" 9 | implementationClass = "RustPlugin" 10 | } 11 | } 12 | } 13 | 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | compileOnly(gradleApi()) 21 | implementation("com.android.tools.build:gradle:8.5.1") 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src-tauri/gen/android/buildSrc/src/main/java/com/wuji_app/app/kotlin/BuildTask.kt: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import org.apache.tools.ant.taskdefs.condition.Os 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.GradleException 5 | import org.gradle.api.logging.LogLevel 6 | import org.gradle.api.tasks.Input 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | open class BuildTask : DefaultTask() { 10 | @Input 11 | var rootDirRel: String? = null 12 | @Input 13 | var target: String? = null 14 | @Input 15 | var release: Boolean? = null 16 | 17 | @TaskAction 18 | fun assemble() { 19 | val executable = """pnpm"""; 20 | try { 21 | runTauriCli(executable) 22 | } catch (e: Exception) { 23 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 24 | runTauriCli("$executable.cmd") 25 | } else { 26 | throw e; 27 | } 28 | } 29 | } 30 | 31 | fun runTauriCli(executable: String) { 32 | val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") 33 | val target = target ?: throw GradleException("target cannot be null") 34 | val release = release ?: throw GradleException("release cannot be null") 35 | val args = listOf("tauri", "android", "android-studio-script"); 36 | 37 | project.exec { 38 | workingDir(File(project.projectDir, rootDirRel)) 39 | executable(executable) 40 | args(args) 41 | if (project.logger.isEnabled(LogLevel.DEBUG)) { 42 | args("-vv") 43 | } else if (project.logger.isEnabled(LogLevel.INFO)) { 44 | args("-v") 45 | } 46 | if (release) { 47 | args("--release") 48 | } 49 | args(listOf("--target", target)) 50 | }.assertNormalExitValue() 51 | } 52 | } -------------------------------------------------------------------------------- /src-tauri/gen/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue May 10 19:22:52 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /src-tauri/gen/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | // 需要将plugin需求的module添加到此 3 | apply from: 'tauri.settings.gradle' 4 | -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-20x20@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-20x20@2x-1.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-20x20@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-20x20@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-29x29@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-29x29@2x-1.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-29x29@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-29x29@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-40x40@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-40x40@2x-1.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-40x40@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-40x40@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-512@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-60x60@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-60x60@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-76x76@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-76x76@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png -------------------------------------------------------------------------------- /src-tauri/permissions/fetch-plugin/default.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | description = "Default permissions for the plugin" 3 | permissions = [ 4 | "allow-fetch", 5 | "allow-fetch-send", 6 | "allow-fetch-cancel", 7 | "allow-fetch-read-body" 8 | ] 9 | 10 | [allow] 11 | http = [ 'http://*' ] 12 | https = [ 'https://*' ] 13 | -------------------------------------------------------------------------------- /src-tauri/permissions/proxy-plugin/default.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | description = "Default permissions for the plugin" 3 | permissions = ["allow-get-m3u8-url", "allow-get-proxy-url"] 4 | 5 | [allow] 6 | http = ['http://*'] 7 | https = ['https://*'] 8 | -------------------------------------------------------------------------------- /src-tauri/src/fetch_plugin/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | use serde::{Serialize, Serializer}; 6 | use url::Url; 7 | 8 | #[derive(Debug, thiserror::Error)] 9 | pub enum Error { 10 | #[error(transparent)] 11 | Json(#[from] serde_json::Error), 12 | #[error(transparent)] 13 | Io(#[from] std::io::Error), 14 | #[error(transparent)] 15 | Network(#[from] reqwest::Error), 16 | #[error(transparent)] 17 | Http(#[from] http::Error), 18 | #[error(transparent)] 19 | HttpInvalidHeaderName(#[from] http::header::InvalidHeaderName), 20 | #[error(transparent)] 21 | HttpInvalidHeaderValue(#[from] http::header::InvalidHeaderValue), 22 | /// URL not allowed by the scope. 23 | #[error("url not allowed on the configured scope: {0}")] 24 | UrlNotAllowed(Url), 25 | #[error(transparent)] 26 | UrlParseError(#[from] url::ParseError), 27 | /// HTTP method error. 28 | #[error(transparent)] 29 | HttpMethod(#[from] http::method::InvalidMethod), 30 | #[error("scheme {0} not supported")] 31 | SchemeNotSupport(String), 32 | #[error("Request canceled")] 33 | RequestCanceled, 34 | #[error(transparent)] 35 | FsError(#[from] tauri_plugin_fs2::Error), 36 | #[error("failed to process data url")] 37 | DataUrlError, 38 | #[error("failed to decode data url into bytes")] 39 | DataUrlDecodeError, 40 | #[error(transparent)] 41 | Tauri(#[from] tauri::Error), 42 | #[error(transparent)] 43 | Utf8(#[from] std::string::FromUtf8Error), 44 | } 45 | 46 | impl Serialize for Error { 47 | fn serialize(&self, serializer: S) -> std::result::Result 48 | where 49 | S: Serializer, 50 | { 51 | serializer.serialize_str(self.to_string().as_ref()) 52 | } 53 | } 54 | 55 | pub type Result = std::result::Result; 56 | -------------------------------------------------------------------------------- /src-tauri/src/fetch_plugin/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | //! Access the HTTP client written in Rust. 6 | 7 | pub use reqwest; 8 | use tauri::{ 9 | plugin::{Builder, TauriPlugin}, 10 | Manager, Runtime, 11 | }; 12 | 13 | pub use error::{Error, Result}; 14 | 15 | mod commands; 16 | mod error; 17 | mod scope; 18 | 19 | pub(crate) struct Http { 20 | cookies_jar: std::sync::Arc, 21 | } 22 | 23 | pub fn init() -> TauriPlugin { 24 | // 定义在同一文件夹中的叫做 inlined plugin,同导入的plugin用法不一样 25 | // 1. lib.rs中 26 | // .setup(move |app| { 27 | // let handle = app.handle(); 28 | // handle.plugin(fetch_plugin::init())?; 29 | // Ok(()) 30 | // }) 31 | // 2. build.rs中 32 | // tauri_build::try_build(tauri_build::Attributes::new().plugin( 33 | // "fetch-plugin", 34 | // tauri_build::InlinedPlugin::new().commands(&[ 35 | // "fetch", 36 | // "fetch_cancel", 37 | // "fetch_send", 38 | // "fetch_read_body", 39 | // ]), 40 | // )) 41 | // 3. 定义permissions并在capabilities中添加权限 42 | 43 | Builder::::new("fetch-plugin") 44 | .setup(|app, _| { 45 | let state = Http { 46 | cookies_jar: std::sync::Arc::new(reqwest::cookie::Jar::default()), 47 | }; 48 | 49 | app.manage(state); 50 | 51 | Ok(()) 52 | }) 53 | .invoke_handler(tauri::generate_handler![ 54 | commands::fetch, 55 | commands::fetch_cancel, 56 | commands::fetch_send, 57 | commands::fetch_read_body 58 | ]) 59 | .build() 60 | } 61 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | wuji_app_lib::run() 6 | } 7 | -------------------------------------------------------------------------------- /src-tauri/src/proxy_plugin/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | //! Access the HTTP client written in Rust. 6 | 7 | use tauri::{ 8 | plugin::{Builder, TauriPlugin}, 9 | Manager, Runtime, 10 | }; 11 | mod ad_remove; 12 | mod commands; 13 | 14 | pub fn init() -> TauriPlugin { 15 | Builder::::new("proxy-plugin") 16 | .setup(|app, _| { 17 | commands::start_proxy_server().map_err(|e| e.to_string())?; 18 | Ok(()) 19 | }) 20 | .invoke_handler(tauri::generate_handler![ 21 | commands::get_m3u8_url, 22 | commands::get_proxy_url 23 | ]) 24 | .build() 25 | } 26 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2.5.0", 3 | "productName": "无极", 4 | "mainBinaryName":"无极", 5 | "version": "0.1.10", 6 | "identifier": "com.wuji-app.app", 7 | "build": { 8 | "beforeDevCommand": "pnpm dev", 9 | "devUrl": "http://localhost:1420", 10 | "beforeBuildCommand": "pnpm build", 11 | "frontendDist": "../dist" 12 | }, 13 | "app": { 14 | "windows": [ 15 | { 16 | "title": "无极", 17 | "width": 1100, 18 | "height": 700, 19 | "windowClassname": "wuji", 20 | "backgroundThrottling": "throttle" 21 | } 22 | ], 23 | "security": { 24 | "csp": null, 25 | "capabilities": [] 26 | } 27 | }, 28 | "bundle": { 29 | "active": true, 30 | "targets": "all", 31 | "windows": { 32 | "wix": { 33 | "language": "zh-CN" 34 | }, 35 | "nsis": { 36 | "languages": ["SimpChinese"], 37 | "displayLanguageSelector": false 38 | }, 39 | "webviewInstallMode": { 40 | "silent": true, 41 | "type": "embedBootstrapper" 42 | } 43 | }, 44 | "icon": [ 45 | "icons/32x32.png", 46 | "icons/128x128.png", 47 | "icons/128x128@2x.png", 48 | "icons/icon.icns", 49 | "icons/icon.ico" 50 | ], 51 | "createUpdaterArtifacts": true 52 | }, 53 | "plugins": { 54 | "updater": { 55 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEE4NDNCRDczRjBFOTMzMQpSV1F4a3c0LzF6dUVDaDRpTU9qeTNIay84eUtJRW44L3JGdlNvcm0xNFZyYWlJTWpMelpaZmJTMQo=", 56 | "endpoints": [ 57 | "https://wuji.s3.bitiful.net/wuji%2Fupdater_win.json" 58 | ], 59 | "windows": { 60 | "installMode": "passive" 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/apis/hot/index.ts: -------------------------------------------------------------------------------- 1 | // 热搜榜 2 | export * as apiHot from './apiHot.ts'; 3 | -------------------------------------------------------------------------------- /src/apis/ip.ts: -------------------------------------------------------------------------------- 1 | export async function getLocation() { 2 | const url = 'https://www.ip.cn/api/index?ip=&type=0'; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/cd.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coverall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src/assets/coverall.png -------------------------------------------------------------------------------- /src/assets/font/alipuhui.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src/assets/font/alipuhui.otf -------------------------------------------------------------------------------- /src/assets/heart-fill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src/assets/icon.ico -------------------------------------------------------------------------------- /src/assets/wuji.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/HorizonList.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/PlatformSwitch.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/components/ResponsiveGrid.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/components/ResponsiveGrid2.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/components/View.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | 28 | 45 | -------------------------------------------------------------------------------- /src/components/actionSheets/MoreOptions.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/actionSheets/SongSelectShelf.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/components/buttons/SongToFavoriteButton.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/components/card/bookCards/MobileBookCard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/card/bookCards/WinBookCard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/components/card/comicCards/MobileComicCard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/components/card/photoCards/MobilePhotoCard.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 43 | 44 | 49 | -------------------------------------------------------------------------------- /src/components/card/photoCards/WinPhotoCard.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 44 | 45 | 50 | -------------------------------------------------------------------------------- /src/components/card/videoCards/MobileVideoCard.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 40 | 41 | 46 | -------------------------------------------------------------------------------- /src/components/card/videoCards/WinVideoCard.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 45 | 46 | 51 | -------------------------------------------------------------------------------- /src/components/codeEditor/guides/index.ts: -------------------------------------------------------------------------------- 1 | import commonMD from './common.md?raw'; 2 | import photoListMD from './photoList.md?raw'; 3 | import photoSearchListMD from './photoSearchList.md?raw'; 4 | import photoDetailMD from './photoDetail.md?raw'; 5 | import songListMD from './songList.md?raw'; 6 | import searchSongListMD from './songSearchList.md?raw'; 7 | import playlistMD from './songPlaylist.md?raw'; 8 | import searchPlaylistMD from './songSearchPlaylist.md?raw'; 9 | import playlistDetailMD from './songPlaylistDetail.md?raw'; 10 | import playUrlMD from './songPlayUrl.md?raw'; 11 | import lyricMD from './songLyric.md?raw'; 12 | 13 | export const guideCommonMD = commonMD; 14 | 15 | const photoConstructorMD = `构造函数必须填写baseUrl`; 16 | const songConstructorMD = `构造函数必须填写baseUrl`; 17 | 18 | export const guideExamplesMD = { 19 | photo: { 20 | constructor: photoConstructorMD, 21 | list: photoListMD, 22 | searchList: photoSearchListMD, 23 | detail: photoDetailMD, 24 | }, 25 | song: { 26 | constructor: songConstructorMD, 27 | songList: songListMD, 28 | searchSongList: searchSongListMD, 29 | playlist: playlistMD, 30 | searchPlaylist: searchPlaylistMD, 31 | playlistDetail: playlistDetailMD, 32 | playUrl: playUrlMD, 33 | lyric: lyricMD, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/codeEditor/guides/songLyric.md: -------------------------------------------------------------------------------- 1 | ## 无需实现, 软件自动匹配 2 | 3 | ## 函数定义 4 | 5 | ```typescript 6 | /** 7 | * 抽象方法:获取歌曲歌词 8 | * @param item 歌曲信息对象 9 | * @returns 返回Promise对象:歌词内容 10 | */ 11 | abstract getLyric(item: SongInfo): Promise; 12 | ``` 13 | 14 | ## 从DOM获取元素 15 | 16 | ```javascript 17 | 18 | ``` 19 | 20 | ## 从api获取元素 21 | 22 | ```javascript 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /src/components/codeEditor/templates/photoTemplate.txt: -------------------------------------------------------------------------------- 1 | class CustomPhotoExtension extends PhotoExtension { 2 | id = 'testPhoto'; 3 | name = '测试'; 4 | version = ''; 5 | author = ''; 6 | 7 | constructor() {} 8 | 9 | async getRecommendList(pageNo) {} 10 | 11 | async search(keyword, pageNo) {} 12 | 13 | async getPhotoDetail(item, pageNo) {} 14 | 15 | } 16 | 17 | return CustomPhotoExtension; 18 | -------------------------------------------------------------------------------- /src/components/codeEditor/templates/songTemplate.txt: -------------------------------------------------------------------------------- 1 | class CustomSongExtension extends SongExtension { 2 | id = 'testSong'; 3 | name = '测试'; 4 | version = ''; 5 | author = ''; 6 | 7 | constructor() {} 8 | 9 | async getRecommendPlaylists(pageNo) {} 10 | 11 | async getRecommendSongs(pageNo) {} 12 | 13 | async searchPlaylists(keyword, pageNo) {} 14 | 15 | async searchSongs(keyword, pageNo) {} 16 | 17 | async getPlaylistDetail(item, pageNo) {} 18 | 19 | async getSongUrl(item, size) {} 20 | 21 | async getLyric(item) {} 22 | } 23 | 24 | return CustomSongExtension; 25 | -------------------------------------------------------------------------------- /src/components/dialogs/About.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/components/dialogs/SwitchComicSource.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/media/mobile/MobileFullScreenButton.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 53 | -------------------------------------------------------------------------------- /src/components/media/mobile/MobileQuickSeekButton.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/components/media/win/BrowserFullScreenButton.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/components/media/win/FullScreenButton.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 53 | -------------------------------------------------------------------------------- /src/components/media/win/PlayNextButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/components/media/win/PlayPauseButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 29 | 30 | 54 | -------------------------------------------------------------------------------- /src/components/media/win/QuickSeekButton.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/media/win/TimeShow.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 42 | -------------------------------------------------------------------------------- /src/components/mobile/MobileHeader.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/mobile/Search.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 57 | 58 | 70 | -------------------------------------------------------------------------------- /src/components/photos/SongCardPhoto.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/components/photos/SongPlaylistPhoto.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/AddBookShelf.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/AddComicShelf.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/AddPhotoShelf.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/AddSongShelf.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/AddVideoShelf.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/ImportSubscribe.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/RemoveBookShelf.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/RemoveComicShelf.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/RemoveSongShelf.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/RemoveVideoShelf.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/components/windows/dialogs/removePhotoShelf.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/extensions/book/test.d.ts: -------------------------------------------------------------------------------- 1 | import { BookExtension } from '.'; 2 | 3 | declare class TestBookExtension extends BookExtension { 4 | id: string; 5 | name: string; 6 | version: string; 7 | baseUrl: string; 8 | pageSize: number; 9 | 10 | constructor(); 11 | } 12 | 13 | export default TestBookExtension; 14 | -------------------------------------------------------------------------------- /src/extensions/comic/test.d.ts: -------------------------------------------------------------------------------- 1 | import { ComicExtension } from '.'; 2 | 3 | declare class TestComicExtension extends ComicExtension { 4 | id: string; 5 | name: string; 6 | version: string; 7 | baseUrl: string; 8 | pageSize: number; 9 | 10 | constructor(); 11 | } 12 | 13 | export default TestComicExtension; 14 | -------------------------------------------------------------------------------- /src/extensions/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/src/extensions/index.ts -------------------------------------------------------------------------------- /src/extensions/photo/test.d.ts: -------------------------------------------------------------------------------- 1 | import { PhotoExtension } from '.'; 2 | 3 | declare class TestPhotoExtension extends PhotoExtension { 4 | id: string; 5 | name: string; 6 | version: string; 7 | baseUrl: string; 8 | pageSize: number; 9 | 10 | constructor(); 11 | } 12 | 13 | export default TestPhotoExtension; 14 | -------------------------------------------------------------------------------- /src/extensions/song/test.d.ts: -------------------------------------------------------------------------------- 1 | import { SongExtension } from '.'; 2 | 3 | declare class TestSongExtension extends SongExtension { 4 | id: string; 5 | name: string; 6 | version: string; 7 | baseUrl: string; 8 | pageSize: number; 9 | 10 | constructor(); 11 | } 12 | 13 | export default TestSongExtension; 14 | -------------------------------------------------------------------------------- /src/extensions/video/test.d.ts: -------------------------------------------------------------------------------- 1 | import { VideoExtension } from '.'; 2 | 3 | declare class TestVideoExtension extends VideoExtension { 4 | id: string; 5 | name: string; 6 | version: string; 7 | baseUrl: string; 8 | pageSize: number; 9 | 10 | constructor(); 11 | } 12 | 13 | export default TestVideoExtension; 14 | -------------------------------------------------------------------------------- /src/extensions/video/test.js: -------------------------------------------------------------------------------- 1 | import { VideoExtension } from './index'; 2 | 3 | class TestVideoExtension extends VideoExtension { 4 | id = 'testVideo'; 5 | name = 'testVideo'; 6 | version = '0.0.1'; 7 | baseUrl = 'https://www.hhlqilongzhu.cn/api/duanju_hema.php'; 8 | async getRecommendVideos(pageNo, type) { 9 | return await this.search('系统', pageNo); 10 | } 11 | 12 | async search(keyword, pageNo) { 13 | const url = `${this.baseUrl}?name=${keyword}&page=${pageNo}`; 14 | const response = await this.fetch(url); 15 | const json = await response.json(); 16 | const list = []; 17 | json.data.forEach((element) => { 18 | list.push({ 19 | id: element.book_id, 20 | title: element.title, 21 | intro: element.intro, 22 | cover: element.cover, 23 | cast: element.author, 24 | tags: element.type?.map((v) => v.name).join(','), 25 | }); 26 | }); 27 | return { 28 | list, 29 | page: pageNo, 30 | totalPage: json.total_page, 31 | }; 32 | } 33 | 34 | async getVideoDetail(item, pageNo) { 35 | const url = `${this.baseUrl}?book_id=${item.id}`; 36 | const response = await this.fetch(url); 37 | const json = await response.json(); 38 | const resources = [ 39 | { 40 | id: item.id, 41 | title: '播放列表', 42 | episodes: [], 43 | }, 44 | ]; 45 | json.data.forEach((e) => { 46 | resources[0].episodes.push({ 47 | id: e.video_id, 48 | title: e.title, 49 | url: e.url_mp4, 50 | }); 51 | }); 52 | 53 | item.resources = resources; 54 | return item; 55 | } 56 | 57 | async getPlayUrl(item, resource, episode) { 58 | const response = await this.fetch(episode.url); 59 | return { 60 | url: await this.getProxyUrl(response.url, { referer: response.url }), 61 | }; 62 | } 63 | } 64 | 65 | export default TestVideoExtension; 66 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { HlsVideoElement } from 'hls-video-element'; 2 | import type { MediaThemeElement } from 'media-chrome/media-theme-element'; 3 | 4 | export {}; 5 | declare global { 6 | interface Window { 7 | androidBackCallback?: () => void; 8 | sendGreeting?: () => void; 9 | } 10 | interface HTMLElementTagNameMap { 11 | 'hls-video': HlsVideoElement; 12 | 'media-chrome': MediaThemeElement; 13 | } 14 | } 15 | 16 | // Type definitions for vue-virtual-scroller 17 | // Project: https://github.com/Akryum/vue-virtual-scroller/ 18 | declare module 'vue-virtual-scroller' { 19 | import type { Component, ComponentOptions, PluginObject } from 'vue'; 20 | import type Vue from 'vue'; 21 | 22 | interface PluginOptions { 23 | installComponents?: boolean; 24 | componentsPrefix?: string; 25 | } 26 | 27 | const plugin: PluginObject & { version: string }; 28 | 29 | export const RecycleScroller: Component; 30 | export const DynamicScroller: Component; 31 | export const DynamicScrollerItem: Component; 32 | 33 | export function IdState(options?: { 34 | idProp?: (vm: any) => any; 35 | }): ComponentOptions | typeof Vue; 36 | 37 | export default plugin; 38 | } 39 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { GesturePlugin } from '@vueuse/gesture'; 2 | import { createPinia } from 'pinia'; 3 | import { Lazyload } from 'vant'; 4 | import { Toast, Dialog, Notify, ImagePreview, Sticky } from 'vant'; 5 | 6 | import { createApp } from 'vue'; 7 | import { createVuetify } from 'vuetify'; 8 | import * as components from 'vuetify/components'; 9 | 10 | import * as directives from 'vuetify/directives'; 11 | import { aliases, mdi } from 'vuetify/iconsets/mdi'; 12 | 13 | import App from './App.vue'; 14 | import { router } from './router'; 15 | 16 | import HorizontalScrollDirective from './utils/directives/horizontalScroll'; 17 | import RememberScrollDirective from './utils/directives/rememberScroll'; 18 | import HoverDelay from './utils/directives/hoverDelay'; 19 | 20 | import 'vuetify/styles'; 21 | import 'vant/es/toast/style'; 22 | import 'vant/es/dialog/style'; 23 | import 'vant/es/notify/style'; 24 | import 'vant/es/sticky/style'; 25 | import 'vant/es/image-preview/style'; 26 | import '@vant/touch-emulator'; 27 | import '@/styles/index.css'; 28 | import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; 29 | import 'animate.css'; 30 | 31 | createApp(App) 32 | .use(createPinia()) 33 | .use( 34 | createVuetify({ 35 | components, 36 | directives, 37 | icons: { 38 | defaultSet: 'mdi', 39 | aliases, 40 | sets: { 41 | mdi, 42 | }, 43 | }, 44 | }), 45 | ) 46 | .use(Lazyload) 47 | .use(Toast) 48 | .use(Dialog) 49 | .use(Notify) 50 | .use(Sticky) 51 | .use(ImagePreview) 52 | .use(GesturePlugin) 53 | .use(router) 54 | .directive('remember-scroll', RememberScrollDirective) 55 | .directive('horizontal-scroll', HorizontalScrollDirective) 56 | .directive('hover-delay', HoverDelay) 57 | .mount('#app'); 58 | -------------------------------------------------------------------------------- /src/store/comicStore.ts: -------------------------------------------------------------------------------- 1 | import type { ComicChapter, ComicItem } from '@/extensions/comic'; 2 | import { defineStore } from 'pinia'; 3 | import { ref } from 'vue'; 4 | 5 | export const useComicStore = defineStore('comic', () => { 6 | const readingComic = ref(); 7 | const readingChapter = ref(); 8 | return { 9 | readingComic, 10 | readingChapter, 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bookChaptersStore'; 2 | export * from './bookShelfStore'; 3 | export * from './bookStore'; 4 | export * from './comicShelfStore'; 5 | export * from './comicStore'; 6 | export * from './displayStore'; 7 | export * from './photoShelfStore'; 8 | export * from './serverStore'; 9 | export * from './songCacheStore'; 10 | export * from './songShelfStore'; 11 | export * from './songStore'; 12 | export * from './store'; 13 | export * from './subscribeSourceStore'; 14 | export * from './ttsStore'; 15 | export * from './utils'; 16 | export * from './videoShelfStore'; 17 | -------------------------------------------------------------------------------- /src/types/book.ts: -------------------------------------------------------------------------------- 1 | export interface ReadTheme { 2 | name: string; 3 | color: string; 4 | bgColor: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { BooksList } from '@/extensions/book'; 2 | import type { ComicsList } from '@/extensions/comic'; 3 | import type { PhotoList } from '@/extensions/photo'; 4 | import type { PlaylistList, SongList } from '@/extensions/song'; 5 | import type { VideosList } from '@/extensions/video'; 6 | 7 | export interface Source { 8 | item: SubscribeItem; 9 | } 10 | 11 | export enum SourceType { 12 | Photo = 'photo', 13 | Song = 'song', 14 | Video = 'video', 15 | Book = 'book', 16 | Resource = 'resource', 17 | Comic = 'comic', 18 | } 19 | 20 | export interface PhotoSource extends Source { 21 | list?: PhotoList; 22 | } 23 | export interface SongSource extends Source { 24 | songList?: SongList; 25 | playlist?: PlaylistList; 26 | } 27 | export interface BookSource extends Source { 28 | list?: BooksList; 29 | } 30 | export interface ComicSource extends Source { 31 | list?: ComicsList; 32 | } 33 | export interface VideoSource extends Source { 34 | list?: VideosList; 35 | } 36 | 37 | export interface SubscribeItem { 38 | id: string; 39 | name: string; 40 | type: SourceType; 41 | url: string; 42 | disable?: boolean; 43 | code?: string; 44 | } 45 | 46 | export interface SubscribeDetail { 47 | id: string; 48 | name: string; 49 | version: number; // 订阅源的版本 50 | requireVersion?: number; // 软件所需的最低版本 51 | urls: SubscribeItem[]; 52 | } 53 | export interface SubscribeSource { 54 | // 订阅源 55 | url: string; 56 | disable: boolean; 57 | detail: SubscribeDetail; 58 | } 59 | -------------------------------------------------------------------------------- /src/types/song.ts: -------------------------------------------------------------------------------- 1 | export enum SongPlayMode { 2 | single = 'single', // 单曲循环 3 | list = 'list', // 列表循环 4 | random = 'random', // 随机播放 5 | } 6 | export enum SongShelfType { 7 | create = 'create', // 自建歌单 8 | like = 'like', // 我喜欢的音乐 9 | playlist = 'playlist', // 收藏的歌单 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/cancelableFunction.ts: -------------------------------------------------------------------------------- 1 | type CancellableFunction = (...args: Args) => Promise; 2 | 3 | function createCancellableFunction( 4 | fn: (signal: AbortSignal, ...args: Args) => Promise, 5 | ): CancellableFunction { 6 | let abortController: AbortController | null = null; 7 | 8 | return async (...args: Args) => { 9 | // 如果之前有调用在进行,中断它 10 | const temp = abortController; 11 | temp?.abort(); 12 | 13 | // 创建新的 AbortController 14 | abortController = new AbortController(); 15 | const signal = abortController.signal; 16 | 17 | try { 18 | // 将原函数包装为一个支持中断的 Promise 19 | const result = await new Promise((resolve, reject) => { 20 | // 执行原函数 21 | const originalPromise = fn(signal, ...args); 22 | 23 | // 监听中断信号 24 | signal.addEventListener('abort', () => { 25 | reject(new DOMException('操作中断', 'AbortError')); 26 | }); 27 | // 等待原函数完成 28 | originalPromise.then(resolve).catch(reject); 29 | }); 30 | 31 | return result; 32 | } 33 | catch (error) { 34 | if (error instanceof Error && error.name === 'AbortError') { 35 | // 处理中断错误 36 | console.log('操作被中断'); 37 | } 38 | else { 39 | // 处理其他错误 40 | console.error('操作失败:', error); 41 | } 42 | throw error; 43 | } 44 | finally { 45 | // abortController = null; 46 | } 47 | }; 48 | } 49 | 50 | export { createCancellableFunction }; 51 | -------------------------------------------------------------------------------- /src/utils/device.ts: -------------------------------------------------------------------------------- 1 | import { get_device_id } from 'tauri-plugin-commands-api'; 2 | export async function getDeviceId() { 3 | const deviceInfo = await get_device_id(); 4 | return deviceInfo; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/directives/horizontalScroll.ts: -------------------------------------------------------------------------------- 1 | import type { Directive, DirectiveBinding } from 'vue'; 2 | import { useDisplayStore } from '@/store'; 3 | import { onUnmounted } from 'vue'; 4 | 5 | export default >{ 6 | mounted(el: HTMLElement, binding: DirectiveBinding) { 7 | let timer: NodeJS.Timeout; 8 | el.style.scrollBehavior = 'smooth'; 9 | const handleWheel = (event: WheelEvent) => { 10 | event.preventDefault(); 11 | clearTimeout(timer); 12 | timer = setTimeout(() => { 13 | const delta = event.deltaY; 14 | const currentScrollLeft = el.scrollLeft; 15 | const newScrollLeft = currentScrollLeft + delta * 5; // 调整滚动速度 16 | el.scroll({ 17 | left: newScrollLeft, 18 | }); 19 | }, 0); 20 | }; 21 | const displaystore = useDisplayStore(); 22 | if (displaystore.isAndroid) 23 | return; 24 | el.addEventListener('wheel', handleWheel); 25 | // 移除事件监听器 26 | onUnmounted(() => { 27 | el.removeEventListener('wheel', handleWheel); 28 | }, binding.instance?.$); 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/utils/directives/hoverDelay.ts: -------------------------------------------------------------------------------- 1 | import { Directive, DirectiveBinding } from 'vue'; 2 | 3 | const handlerMap = new WeakMap< 4 | HTMLElement, 5 | { 6 | mouseEnterHandler: () => void; 7 | mouseLeaveHandler: () => void; 8 | } 9 | >(); 10 | 11 | export const hoverDelay: Directive = { 12 | mounted(el: HTMLElement, binding: DirectiveBinding) { 13 | let timer: NodeJS.Timeout | null = null; 14 | 15 | const mouseEnterHandler = () => { 16 | if (timer) { 17 | clearTimeout(timer); 18 | timer = null; 19 | } 20 | binding.value(true); 21 | }; 22 | 23 | const mouseLeaveHandler = () => { 24 | timer = setTimeout(() => { 25 | binding.value(false); 26 | }, 2000); 27 | }; 28 | 29 | el.addEventListener('mouseenter', mouseEnterHandler); 30 | el.addEventListener('mouseleave', mouseLeaveHandler); 31 | 32 | handlerMap.set(el, { mouseEnterHandler, mouseLeaveHandler }); 33 | }, 34 | unmounted(el: HTMLElement) { 35 | const handlers = handlerMap.get(el); 36 | if (handlers) { 37 | el.removeEventListener('mouseenter', handlers.mouseEnterHandler); 38 | el.removeEventListener('mouseleave', handlers.mouseLeaveHandler); 39 | handlerMap.delete(el); 40 | } 41 | }, 42 | }; 43 | 44 | export default hoverDelay; 45 | -------------------------------------------------------------------------------- /src/utils/directives/rememberScroll.ts: -------------------------------------------------------------------------------- 1 | import type { Directive } from 'vue'; 2 | import { useScroll } from '@vueuse/core'; 3 | import { onActivated, onDeactivated } from 'vue'; 4 | 5 | export const vRememberScroll: Directive = { 6 | mounted(el, binding) { 7 | // 1. 确定目标元素 8 | const targetEl = binding.value 9 | ? (el.querySelector(binding.value) as HTMLElement) 10 | : el; 11 | 12 | if (!targetEl) { 13 | console.warn(`v-remember-scroll: 未找到目标元素 "${binding.value}"`); 14 | return; 15 | } 16 | 17 | // 2. 存储滚动位置 18 | let scrollPosition = { top: 0, left: 0 }; 19 | const { x, y } = useScroll(targetEl); 20 | 21 | // 3. 激活时恢复位置 22 | onActivated(() => { 23 | targetEl.scrollTo({ 24 | ...scrollPosition, 25 | behavior: 'instant' as ScrollBehavior, 26 | }); 27 | }, binding.instance?.$); 28 | 29 | // 4. 停用时保存位置 30 | onDeactivated(() => { 31 | scrollPosition = { left: x.value, top: y.value }; 32 | }, binding.instance?.$); 33 | }, 34 | }; 35 | 36 | export default vRememberScroll; 37 | -------------------------------------------------------------------------------- /src/utils/edge-tts/constants.ts: -------------------------------------------------------------------------------- 1 | export enum OUTPUT_FORMAT { 2 | AUDIO_24KHZ_48KBITRATE_MONO_MP3 = 'audio-24khz-48kbitrate-mono-mp3', 3 | AUDIO_24KHZ_96KBITRATE_MONO_MP3 = 'audio-24khz-96kbitrate-mono-mp3', 4 | WEBM_24KHZ_16BIT_MONO_OPUS = 'webm-24khz-16bit-mono-opus', 5 | } 6 | 7 | export enum PITCH { 8 | X_LOW = 'x-low', 9 | LOW = 'low', 10 | MEDIUM = 'medium', 11 | HIGH = 'high', 12 | X_HIGH = 'x-high', 13 | DEFAULT = 'default', 14 | } 15 | 16 | export enum RATE { 17 | X_SLOW = 'x-slow', 18 | SLOW = 'slow', 19 | MEDIUM = 'medium', 20 | FAST = 'fast', 21 | X_FAST = 'x-fast', 22 | DEFAULT = 'default', 23 | } 24 | 25 | export enum VOLUME { 26 | SILENT = 'silent', 27 | X_SOFT = 'x-soft', 28 | SOFT = 'soft', 29 | MEDIUM = 'medium', 30 | LOUD = 'loud', 31 | X_LOUD = 'x-LOUD', 32 | DEFAULT = 'default', 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/edge-tts/drm.ts: -------------------------------------------------------------------------------- 1 | import { createHash } from 'node:crypto'; 2 | 3 | export const CHROMIUM_FULL_VERSION = '130.0.2849.68'; 4 | export const TRUSTED_CLIENT_TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4'; 5 | const WINDOWS_FILE_TIME_EPOCH = 11644473600n; 6 | 7 | export function generateSecMsGecToken() { 8 | const ticks 9 | = BigInt(Math.floor(Date.now() / 1000 + Number(WINDOWS_FILE_TIME_EPOCH))) 10 | * 10000000n; 11 | const roundedTicks = ticks - (ticks % 3000000000n); 12 | 13 | const strToHash = `${roundedTicks}${TRUSTED_CLIENT_TOKEN}`; 14 | 15 | const hash = createHash('sha256'); 16 | hash.update(strToHash, 'ascii'); 17 | 18 | return hash.digest('hex').toUpperCase(); 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/edge-tts/index.ts: -------------------------------------------------------------------------------- 1 | // Re-exporting enums from constants.ts 2 | export { OUTPUT_FORMAT, PITCH, RATE, VOLUME } from './constants'; 3 | // Re-exporting the main classes 4 | export { EdgeTTSClient } from './edge-tts-client'; 5 | 6 | export type { ProsodyOptions } from './edge-tts-client'; 7 | 8 | // Re-exporting types 9 | export type { Voice } from './edge-tts-client'; 10 | -------------------------------------------------------------------------------- /src/utils/heartbeat.ts: -------------------------------------------------------------------------------- 1 | import { clearInterval, setInterval } from 'worker-timers'; 2 | 3 | let ws: WebSocket; 4 | let heartbeatInterval: number; 5 | 6 | export function connectWebSocket() { 7 | ws = new WebSocket('ws://localhost:52381'); 8 | 9 | ws.onopen = () => { 10 | console.log('WebSocket connected'); 11 | startHeartbeat(); 12 | }; 13 | 14 | ws.onmessage = (event) => { 15 | console.log('Received:', event.data); 16 | }; 17 | 18 | ws.onclose = () => { 19 | console.log('WebSocket disconnected'); 20 | clearInterval(heartbeatInterval); 21 | // setTimeout(connectWebSocket, 3000); // 3秒后重连 22 | }; 23 | 24 | ws.onerror = (error) => { 25 | console.error('WebSocket error:', error); 26 | }; 27 | } 28 | 29 | function startHeartbeat() { 30 | heartbeatInterval = setInterval(() => { 31 | const heartbeat = { 32 | type: 'heartbeat', 33 | timestamp: new Date().toISOString(), 34 | }; 35 | ws.send(JSON.stringify(heartbeat)); 36 | console.log('Sent heartbeat'); 37 | }, 5000); // 每5秒发送一次心跳 38 | } 39 | 40 | export default connectWebSocket; 41 | -------------------------------------------------------------------------------- /src/utils/lruCache.ts: -------------------------------------------------------------------------------- 1 | class SimpleLRUCache { 2 | private cache: Map; 3 | private maxSize: number; 4 | 5 | constructor(maxSize: number) { 6 | this.cache = new Map(); 7 | this.maxSize = maxSize; 8 | } 9 | 10 | has(key: K): boolean { 11 | return this.cache.has(key); 12 | } 13 | 14 | get(key: K): V | undefined { 15 | if (!this.cache.has(key)) return undefined; 16 | const value = this.cache.get(key)!; 17 | // LRU 逻辑:访问后重新插入,保持最近使用 18 | this.cache.delete(key); 19 | this.cache.set(key, value); 20 | return value; 21 | } 22 | 23 | set(key: K, value: V): void { 24 | if (this.cache.size >= this.maxSize) { 25 | // 删除最久未使用的(Map 的第一个键) 26 | const oldestKey = this.cache.keys().next().value; 27 | if (oldestKey) { 28 | this.cache.delete(oldestKey); 29 | } 30 | } 31 | this.cache.set(key, value); 32 | } 33 | delete(key: K): void { 34 | this.cache.delete(key); 35 | } 36 | clear(): void { 37 | this.cache.clear(); 38 | } 39 | } 40 | 41 | export { SimpleLRUCache }; 42 | export default SimpleLRUCache; 43 | -------------------------------------------------------------------------------- /src/utils/permissions.ts: -------------------------------------------------------------------------------- 1 | import type { PermissionState } from '@tauri-apps/api/core'; 2 | import { invoke } from '@tauri-apps/api/core'; 3 | 4 | export interface MediaSessionPermissions { 5 | foregroundService: PermissionState; 6 | foregroundServiceMediaPlayback: PermissionState; 7 | wakeLock: PermissionState; 8 | } 9 | 10 | export enum MediaSessionPermissionType { 11 | ForegroundService = 'foregroundService', 12 | ForegroundServiceMediaPlayback = 'foregroundServiceMediaPlayback', 13 | WakeLock = 'wakeLock', 14 | } 15 | 16 | export async function handlePermissionRequest(type: MediaSessionPermissionType) { 17 | const permission = await invoke( 18 | 'plugin:mediasession|check_permissions', 19 | ); 20 | 21 | const state = permission[type]; 22 | 23 | if (state === 'prompt-with-rationale') { 24 | // 显示解释信息,告诉用户为什么需要这个权限 25 | // 例如弹出对话框或提示用户权限的重要性 26 | } 27 | 28 | if (state.startsWith('prompt')) { 29 | await invoke('plugin:mediasession|request_permissions', { 30 | permissions: [type], 31 | }); 32 | // return checkPermission(type); // 请求后重新检查 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/proxyUrl.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from '@tauri-apps/api/core'; 2 | 3 | export async function getProxyUrl( 4 | url: string, 5 | headers?: Record, 6 | ): Promise { 7 | return await invoke('plugin:proxy-plugin|get_proxy_url', { 8 | url: url, 9 | headers: Array.from(Object.entries(headers || {})), 10 | }); 11 | } 12 | 13 | export async function getM3u8ProxyUrl( 14 | url: string, 15 | headers?: Record, 16 | ): Promise { 17 | return await invoke('plugin:proxy-plugin|get_m3u8_url', { 18 | originalUrl: url, 19 | headers: headers || {}, 20 | }); 21 | } 22 | 23 | export async function getFileProxyUrl( 24 | filename: string, 25 | baseDir: Record, 26 | ): Promise { 27 | return await invoke('plugin:proxy-plugin|get_file_url', { 28 | filename, 29 | baseDir, 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/reader/reader-layout.d.ts: -------------------------------------------------------------------------------- 1 | import type { ReaderOptions, ReaderResult } from './types'; 2 | 3 | declare module '@/utils/reader/reader-layout' { 4 | function Reader(content: string, option: ReaderOptions): ReaderResult; 5 | 6 | export default Reader; 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/reader/types.ts: -------------------------------------------------------------------------------- 1 | export interface ReaderOptions { 2 | platform?: 3 | | 'browser' 4 | | 'quickApp' 5 | | 'wxMini' 6 | | 'alipayMini' 7 | | 'alitbMini' 8 | | 'swan'; 9 | id?: string; 10 | splitCode?: string; 11 | fast?: boolean; 12 | type?: 'page' | 'line'; 13 | width: number; 14 | height: number; 15 | fontFamily?: string; 16 | fontSize: number; 17 | lineHeight?: number; 18 | pGap?: number; 19 | title?: string; 20 | titleSize?: number; 21 | titleHeight?: number; 22 | titleWeight?: string; 23 | titleGap?: number; 24 | } 25 | export interface LineData { 26 | isTitle: boolean; 27 | center: boolean; 28 | pFirst: boolean; 29 | pLast: boolean; 30 | pIndex: number; 31 | lineIndex: number; 32 | textIndex: number; 33 | text: string; 34 | } 35 | export type ReaderResult = LineData[][]; 36 | -------------------------------------------------------------------------------- /src/utils/songCover.ts: -------------------------------------------------------------------------------- 1 | import type { SongInfo } from '@/extensions/song'; 2 | import { joinSongArtists } from '.'; 3 | import { 4 | search as neteaseSearch, 5 | songDetail as neteaseSongDetail, 6 | } from './neteaseMusic'; 7 | 8 | export async function getSongCover(song: SongInfo): Promise { 9 | if (!song.name) 10 | return song; 11 | const songName = song.name; 12 | const singerName = joinSongArtists(song.artists); 13 | 14 | const res = await neteaseSearch(songName); 15 | const t = await res.text(); 16 | const songs: { id: number; name: string; artists: string }[] = JSON.parse( 17 | t, 18 | ).result.songs.map((song: any) => { 19 | return { 20 | id: song.id, 21 | name: song.name, 22 | artists: song.artists.map((artist: any) => artist.name).join(','), 23 | }; 24 | }); 25 | if (!songs) 26 | return song; 27 | const sSong 28 | = songs.find( 29 | song => song.name === songName && song.artists.includes(singerName), 30 | ) || songs.find(song => song.name === songName); 31 | 32 | if (!sSong) 33 | return song; 34 | 35 | const detail = await neteaseSongDetail([String(sSong.id)]); 36 | const json = await detail.json(); 37 | if (json.code === 200 && json.songs.length > 0) { 38 | if (json.songs[0].al.picUrl) { 39 | song.picUrl = json.songs[0].al.picUrl; 40 | } 41 | } 42 | return song; 43 | } 44 | -------------------------------------------------------------------------------- /src/utils/tray.ts: -------------------------------------------------------------------------------- 1 | import type { TrayIconEvent, TrayIconOptions } from '@tauri-apps/api/tray'; 2 | import ico from '@/assets/icon.ico'; 3 | import { Image } from '@tauri-apps/api/image'; 4 | import { Menu } from '@tauri-apps/api/menu'; 5 | import { TrayIcon } from '@tauri-apps/api/tray'; 6 | import { Window } from '@tauri-apps/api/window'; 7 | 8 | export default async function buildTray() { 9 | const options: TrayIconOptions = { 10 | tooltip: '无极', 11 | icon: await Image.fromBytes(await (await fetch(ico)).arrayBuffer()), 12 | menu: await Menu.new({ 13 | items: [ 14 | { 15 | id: 'quit', 16 | text: '退出', 17 | action: async () => { 18 | const windows = await Window.getAll(); 19 | windows.forEach((window) => { 20 | window.close(); 21 | }); 22 | }, 23 | }, 24 | ], 25 | }), 26 | action: async (event: TrayIconEvent) => { 27 | if (event.type === 'DoubleClick') { 28 | const windows = await Window.getAll(); 29 | const window 30 | = windows.find(window => window.label === 'main') || windows[0]; 31 | 32 | window?.show(); 33 | window?.setFocus(); 34 | } 35 | }, 36 | }; 37 | 38 | const tray = await TrayIcon.new(options); 39 | return tray; 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/trycatch.ts: -------------------------------------------------------------------------------- 1 | export interface TryCatchOptions { 2 | default?: T; 3 | onCatch?: (e: any) => T | Promise; 4 | } 5 | 6 | export function tryCatch( 7 | fn: (...args: any[]) => Promise, 8 | options?: TryCatchOptions, 9 | ): (...args: any[]) => Promise { 10 | return async (...args: any[]): Promise => { 11 | try { 12 | return await fn(...args); 13 | } 14 | catch (e) { 15 | if (options?.onCatch) { 16 | try { 17 | return await options.onCatch(e); 18 | } 19 | catch (catchError) { 20 | console.error('Error in onCatch handler:', catchError); 21 | } 22 | } 23 | // Ensure the returned value is of type T or null 24 | if (options?.default !== undefined) { 25 | return options.default; 26 | } 27 | return null; 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/views/auth/User.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/views/home/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/mobileView/source/createSource/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/views/photo/PhotoShelf.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/views/source/CreateSource.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/views/source/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/video/VideoShelf.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/views/windowsView/source/SourceManage.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/views/windowsView/source/createSource/views/SongLyric.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue'; 5 | 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | import tailwindScrollBar from 'tailwind-scrollbar'; 3 | import scrollbarHide from 'tailwind-scrollbar-hide'; 4 | 5 | export default { 6 | darkMode: 'selector', 7 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 8 | theme: { 9 | extend: { 10 | keyframes: { 11 | shake: { 12 | '0%': { transform: 'translateX(0)' }, 13 | '25%': { transform: 'translateX(-10px)' }, 14 | '75%': { transform: 'translateX(10px)' }, 15 | }, 16 | }, 17 | animation: { 18 | shake: 'shake 0.2s linear 2s infinite', 19 | 'spin-slow': 'spin 9s linear infinite', 20 | }, 21 | }, 22 | screens: { 23 | xs: '360px', 24 | sm: '640px', 25 | md: '700px', 26 | lg: '865px', 27 | xl: '1000px', 28 | '2xl': '1280px', 29 | '3xl': '1560px', 30 | '4xl': '1920px', 31 | '5xl': '2560px', 32 | '6xl': '3840px', 33 | }, 34 | }, 35 | plugins: [tailwindScrollBar, scrollbarHide], 36 | safelist: [ 37 | // 预生成1-12列的所有可能组合 38 | ...Array.from({ length: 12 }, (_, i) => `grid-cols-${i + 1}`), 39 | 40 | // 预生成响应式变体 41 | ...[ 42 | 'xs', 43 | 'sm', 44 | 'md', 45 | 'lg', 46 | 'xl', 47 | '2xl', 48 | '3xl', 49 | '4xl', 50 | '5xl', 51 | '6xl', 52 | ].flatMap((prefix) => 53 | Array.from({ length: 12 }, (_, i) => `${prefix}:grid-cols-${i + 1}`), 54 | ), 55 | ...Array.from({ length: 12 }, (_, i) => `p-${i + 1}`), 56 | ...Array.from({ length: 12 }, (_, i) => `gap-${i + 1}`), 57 | ], 58 | }; 59 | -------------------------------------------------------------------------------- /tauri-plugin-commands/.gitignore: -------------------------------------------------------------------------------- 1 | /.vs 2 | .DS_Store 3 | .Thumbs.db 4 | *.sublime* 5 | .idea/ 6 | debug.log 7 | package-lock.json 8 | .vscode/settings.json 9 | yarn.lock 10 | 11 | /.tauri 12 | /target 13 | Cargo.lock 14 | node_modules/ 15 | /examples 16 | 17 | 18 | dist-js 19 | dist 20 | permissions/autogenerated/ 21 | permissions/schemas/ 22 | -------------------------------------------------------------------------------- /tauri-plugin-commands/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tauri-plugin-commands" 3 | version = "0.1.0" 4 | authors = ["You"] 5 | description = "" 6 | edition = "2021" 7 | rust-version = "1.77.2" 8 | exclude = ["/examples", "/webview-dist", "/webview-src", "/node_modules"] 9 | links = "tauri-plugin-commands" 10 | 11 | [dependencies] 12 | tauri = { version = "2.1.0" } 13 | serde = "1.0" 14 | thiserror = "2" 15 | 16 | [target.'cfg(windows)'.dependencies] 17 | winreg = { version = "0.55.0" } 18 | 19 | [build-dependencies] 20 | tauri-plugin = { version = "2.0.2", features = ["build"] } 21 | -------------------------------------------------------------------------------- /tauri-plugin-commands/README.md: -------------------------------------------------------------------------------- 1 | # Tauri Plugin commands 2 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.tauri 3 | /.idea 4 | /.gradle 5 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | android { 7 | namespace = "tauri.plugin.commands" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | minSdk = 24 12 | 13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles("consumer-rules.pro") 15 | } 16 | 17 | buildTypes { 18 | release { 19 | isMinifyEnabled = false 20 | proguardFiles( 21 | getDefaultProguardFile("proguard-android-optimize.txt"), 22 | "proguard-rules.pro" 23 | ) 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility = JavaVersion.VERSION_1_8 28 | targetCompatibility = JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = "1.8" 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation("androidx.core:core-ktx:1.9.0") 37 | implementation("androidx.appcompat:appcompat:1.6.0") 38 | implementation("com.google.android.material:material:1.7.0") 39 | testImplementation("junit:junit:4.13.2") 40 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 41 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 42 | implementation(project(":tauri-android")) 43 | } 44 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-commands/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tauri-plugin-commands/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Wed Feb 05 18:28:33 CST 2025 8 | sdk.dir=C\:\\Users\\14438\\AppData\\Local\\Android\\Sdk 9 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /tauri-plugin-commands/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | google() 6 | } 7 | resolutionStrategy { 8 | eachPlugin { 9 | switch (requested.id.id) { 10 | case "com.android.library": 11 | useVersion("8.0.2") 12 | break 13 | case "org.jetbrains.kotlin.android": 14 | useVersion("1.8.20") 15 | break 16 | } 17 | } 18 | } 19 | } 20 | 21 | dependencyResolutionManagement { 22 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 23 | repositories { 24 | mavenCentral() 25 | google() 26 | 27 | } 28 | } 29 | 30 | include ':tauri-android' 31 | project(':tauri-android').projectDir = new File('./.tauri/tauri-api') 32 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/src/androidTest/java/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package tauri.plugin.commands 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("tauri.plugin.commands", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-commands/android/src/test/java/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package tauri.plugin.commands 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tauri-plugin-commands/build.rs: -------------------------------------------------------------------------------- 1 | const COMMANDS: &[&str] = &[ 2 | "exit_app", 3 | "return_to_home", 4 | "set_status_bar", 5 | "hide_status_bar", 6 | "get_system_font_scale", 7 | "get_screen_orientation", 8 | "set_screen_orientation", 9 | "get_brightness", 10 | "get_system_brightness", 11 | "set_brightness", 12 | "get_volume", 13 | "set_volume", 14 | "get_device_id", 15 | "register_listener", 16 | "remove_listener", 17 | "check_permissions", 18 | "request_permissions", 19 | ]; 20 | 21 | fn main() { 22 | tauri_plugin::Builder::new(COMMANDS) 23 | .android_path("android") 24 | .ios_path("ios") 25 | .build(); 26 | } 27 | -------------------------------------------------------------------------------- /tauri-plugin-commands/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tauri-plugin-commands-api", 3 | "version": "0.1.0", 4 | "author": "You", 5 | "description": "", 6 | "type": "module", 7 | "types": "./dist-js/index.d.ts", 8 | "main": "./dist-js/index.cjs", 9 | "module": "./dist-js/index.js", 10 | "exports": { 11 | "types": "./dist-js/index.d.ts", 12 | "import": "./dist-js/index.js", 13 | "require": "./dist-js/index.cjs" 14 | }, 15 | "files": [ 16 | "dist-js", 17 | "README.md" 18 | ], 19 | "scripts": { 20 | "build": "rollup -c", 21 | "prepublishOnly": "pnpm build", 22 | "pretest": "pnpm build" 23 | }, 24 | "dependencies": { 25 | "@tauri-apps/api": ">=2.0.0-beta.6" 26 | }, 27 | "devDependencies": { 28 | "@rollup/plugin-typescript": "^11.1.6", 29 | "rollup": "^4.9.6", 30 | "typescript": "^5.3.3", 31 | "tslib": "^2.6.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tauri-plugin-commands/permissions/default.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | description = "Default permissions for the plugin" 3 | permissions = [ 4 | "allow-exit-app", 5 | "allow-return-to-home", 6 | "allow-set-status-bar", 7 | "allow-hide-status-bar", 8 | "allow-get-system-font-scale", 9 | "allow-get-screen-orientation", 10 | "allow-set-screen-orientation", 11 | "allow-get-brightness", 12 | "allow-get-system-brightness", 13 | "allow-set-brightness", 14 | "allow-get-volume", 15 | "allow-set-volume", 16 | "allow-get-device-id", 17 | "allow-register-listener", 18 | "allow-remove-listener", 19 | "allow-check-permissions", 20 | "allow-request-permissions", 21 | ] 22 | -------------------------------------------------------------------------------- /tauri-plugin-commands/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs' 2 | import { join } from 'path' 3 | import { cwd } from 'process' 4 | import typescript from '@rollup/plugin-typescript' 5 | 6 | const pkg = JSON.parse(readFileSync(join(cwd(), 'package.json'), 'utf8')) 7 | 8 | export default { 9 | input: 'guest-js/index.ts', 10 | output: [ 11 | { 12 | file: pkg.exports.import, 13 | format: 'esm' 14 | }, 15 | { 16 | file: pkg.exports.require, 17 | format: 'cjs' 18 | } 19 | ], 20 | plugins: [ 21 | typescript({ 22 | declaration: true, 23 | declarationDir: `./${pkg.exports.import.split('/')[0]}` 24 | }) 25 | ], 26 | external: [ 27 | /^@tauri-apps\/api/, 28 | ...Object.keys(pkg.dependencies || {}), 29 | ...Object.keys(pkg.peerDependencies || {}) 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tauri-plugin-commands/src/error.rs: -------------------------------------------------------------------------------- 1 | use serde::{ser::Serializer, Serialize}; 2 | 3 | pub type Result = std::result::Result; 4 | 5 | #[derive(Debug, thiserror::Error)] 6 | pub enum Error { 7 | #[error(transparent)] 8 | Io(#[from] std::io::Error), 9 | #[cfg(mobile)] 10 | #[error(transparent)] 11 | PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), 12 | } 13 | 14 | impl Serialize for Error { 15 | fn serialize(&self, serializer: S) -> std::result::Result 16 | where 17 | S: Serializer, 18 | { 19 | serializer.serialize_str(self.to_string().as_ref()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tauri-plugin-commands/src/lib.rs: -------------------------------------------------------------------------------- 1 | use tauri::{ 2 | plugin::{Builder, TauriPlugin}, 3 | Manager, Runtime, 4 | }; 5 | 6 | pub use models::*; 7 | 8 | #[cfg(desktop)] 9 | mod desktop; 10 | #[cfg(mobile)] 11 | mod mobile; 12 | 13 | mod commands; 14 | mod error; 15 | mod models; 16 | 17 | pub use error::{Error, Result}; 18 | 19 | #[cfg(desktop)] 20 | use desktop::Commands; 21 | #[cfg(mobile)] 22 | use mobile::Commands; 23 | 24 | /// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the commands APIs. 25 | pub trait CommandsExt { 26 | fn commands(&self) -> &Commands; 27 | } 28 | 29 | impl> crate::CommandsExt for T { 30 | fn commands(&self) -> &Commands { 31 | self.state::>().inner() 32 | } 33 | } 34 | 35 | /// Initializes the plugin. 36 | pub fn init() -> TauriPlugin { 37 | Builder::new("commands") 38 | .invoke_handler(tauri::generate_handler![ 39 | commands::exit_app, 40 | commands::return_to_home, 41 | commands::set_status_bar, 42 | commands::hide_status_bar, 43 | commands::get_system_font_scale, 44 | commands::get_screen_orientation, 45 | commands::set_screen_orientation, 46 | commands::get_brightness, 47 | commands::get_system_brightness, 48 | commands::set_brightness, 49 | commands::get_volume, 50 | commands::set_volume, 51 | commands::get_device_id, 52 | ]) 53 | .setup(|app, api| { 54 | #[cfg(mobile)] 55 | let commands = mobile::init(app, api)?; 56 | #[cfg(desktop)] 57 | let commands = desktop::init(app, api)?; 58 | app.manage(commands); 59 | Ok(()) 60 | }) 61 | .build() 62 | } 63 | -------------------------------------------------------------------------------- /tauri-plugin-commands/src/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Deserialize, Serialize)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct SetStatusBarRequest { 6 | pub bg: String, 7 | pub text: Option 8 | } 9 | 10 | #[derive(Debug, Deserialize, Serialize)] 11 | #[serde(rename_all = "camelCase")] 12 | pub struct SetStatusBarResponse { 13 | pub res: Option, 14 | } 15 | 16 | #[derive(Debug, Deserialize, Serialize)] 17 | #[serde(rename_all = "camelCase")] 18 | pub struct EmptyRequest {} 19 | 20 | #[derive(Debug, Deserialize, Serialize)] 21 | #[serde(rename_all = "camelCase")] 22 | pub struct NumberResponse { 23 | pub value: f32, 24 | } 25 | 26 | #[derive(Debug, Deserialize, Serialize)] 27 | #[serde(rename_all = "camelCase")] 28 | pub struct StringResponse { 29 | pub value: String, 30 | } 31 | 32 | #[derive(Debug, Deserialize, Serialize)] 33 | #[serde(rename_all = "camelCase")] 34 | pub struct SetScreenOrientationRequest { 35 | pub orientation: String, 36 | } 37 | 38 | #[derive(Debug, Deserialize, Serialize)] 39 | #[serde(rename_all = "camelCase")] 40 | pub struct GetScreenOrientationResponse { 41 | pub orientation: String, 42 | } 43 | 44 | #[derive(Debug, Deserialize, Serialize)] 45 | #[serde(rename_all = "camelCase")] 46 | pub struct SetBrightnessRequest { 47 | pub brightness: f32, 48 | } 49 | 50 | #[derive(Debug, Deserialize, Serialize)] 51 | #[serde(rename_all = "camelCase")] 52 | pub struct SetVolumeRequest { 53 | pub volume: f32, 54 | } 55 | 56 | #[derive(Debug, Deserialize, Serialize)] 57 | #[serde(rename_all = "camelCase")] 58 | pub struct BoolResponse { 59 | pub res: Option, 60 | } 61 | 62 | #[derive(Debug, Deserialize, Serialize)] 63 | #[serde(rename_all = "camelCase")] 64 | pub struct HideStatusBarRequest { 65 | pub hide: Option, 66 | } -------------------------------------------------------------------------------- /tauri-plugin-commands/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "esnext", 5 | "moduleResolution": "bundler", 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noUnusedLocals": true, 9 | "noImplicitAny": true, 10 | "noEmit": true 11 | }, 12 | "include": ["guest-js/*.ts"], 13 | "exclude": ["dist-js", "node_modules"] 14 | } 15 | -------------------------------------------------------------------------------- /tauri-plugin-fs/.gitignore: -------------------------------------------------------------------------------- 1 | /.vs 2 | .DS_Store 3 | .Thumbs.db 4 | *.sublime* 5 | .idea/ 6 | debug.log 7 | package-lock.json 8 | .vscode/settings.json 9 | yarn.lock 10 | 11 | /.tauri 12 | /target 13 | Cargo.lock 14 | node_modules/ 15 | /examples 16 | 17 | 18 | dist-js 19 | dist 20 | permissions/autogenerated/ 21 | permissions/schemas/ -------------------------------------------------------------------------------- /tauri-plugin-fs/LICENSE.spdx: -------------------------------------------------------------------------------- 1 | SPDXVersion: SPDX-2.1 2 | DataLicense: CC0-1.0 3 | PackageName: tauri 4 | DataFormat: SPDXRef-1 5 | PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy 6 | PackageHomePage: https://tauri.app 7 | PackageLicenseDeclared: Apache-2.0 8 | PackageLicenseDeclared: MIT 9 | PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy 10 | PackageSummary: Tauri is a rust project that enables developers to make secure 11 | and small desktop applications using a web frontend. 12 | 13 | PackageComment: The package includes the following libraries; see 14 | Relationship information. 15 | 16 | Created: 2019-05-20T09:00:00Z 17 | PackageDownloadLocation: git://github.com/tauri-apps/tauri 18 | PackageDownloadLocation: git+https://github.com/tauri-apps/tauri.git 19 | PackageDownloadLocation: git+ssh://github.com/tauri-apps/tauri.git 20 | Creator: Person: Daniel Thompson-Yvetot -------------------------------------------------------------------------------- /tauri-plugin-fs/LICENSE_MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 - Present Tauri Apps Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tauri-plugin-fs/android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.tauri 3 | .gradle -------------------------------------------------------------------------------- /tauri-plugin-fs/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | android { 7 | namespace = "com.plugin.fs" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | minSdk = 21 12 | targetSdk = 34 13 | 14 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles("consumer-rules.pro") 16 | } 17 | 18 | buildTypes { 19 | release { 20 | isMinifyEnabled = false 21 | proguardFiles( 22 | getDefaultProguardFile("proguard-android-optimize.txt"), 23 | "proguard-rules.pro" 24 | ) 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility = JavaVersion.VERSION_1_8 29 | targetCompatibility = JavaVersion.VERSION_1_8 30 | } 31 | kotlinOptions { 32 | jvmTarget = "1.8" 33 | } 34 | } 35 | 36 | dependencies { 37 | 38 | implementation("androidx.core:core-ktx:1.9.0") 39 | implementation("androidx.appcompat:appcompat:1.6.0") 40 | implementation("com.google.android.material:material:1.7.0") 41 | testImplementation("junit:junit:4.13.2") 42 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 43 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 44 | implementation(project(":tauri-android")) 45 | } 46 | -------------------------------------------------------------------------------- /tauri-plugin-fs/android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /tauri-plugin-fs/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | google() 6 | } 7 | resolutionStrategy { 8 | eachPlugin { 9 | switch (requested.id.id) { 10 | case "com.android.library": 11 | useVersion("8.0.2") 12 | break 13 | case "org.jetbrains.kotlin.android": 14 | useVersion("1.8.20") 15 | break 16 | } 17 | } 18 | } 19 | } 20 | 21 | dependencyResolutionManagement { 22 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 23 | repositories { 24 | mavenCentral() 25 | google() 26 | 27 | } 28 | } 29 | 30 | include ':tauri-android' 31 | project(':tauri-android').projectDir = new File('./.tauri/tauri-api') 32 | -------------------------------------------------------------------------------- /tauri-plugin-fs/android/src/androidTest/java/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | package com.plugin.fs 6 | 7 | import androidx.test.platform.app.InstrumentationRegistry 8 | import androidx.test.ext.junit.runners.AndroidJUnit4 9 | 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | 13 | import org.junit.Assert.* 14 | 15 | /** 16 | * Instrumented test, which will execute on an Android device. 17 | * 18 | * See [testing documentation](http://d.android.com/tools/testing). 19 | */ 20 | @RunWith(AndroidJUnit4::class) 21 | class ExampleInstrumentedTest { 22 | @Test 23 | fun useAppContext() { 24 | // Context of the app under test. 25 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 26 | assertEquals("com.plugin.fs", appContext.packageName) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tauri-plugin-fs/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tauri-plugin-fs/android/src/test/java/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | package com.plugin.fs 6 | 7 | import org.junit.Test 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Example local unit test, which will execute on the development machine (host). 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | class ExampleUnitTest { 17 | @Test 18 | fun addition_isCorrect() { 19 | assertEquals(4, 2 + 2) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tauri-plugin-fs/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-fs/banner.png -------------------------------------------------------------------------------- /tauri-plugin-fs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tauri-plugin-fs2", 3 | "version": "2.2.0", 4 | "description": "Access the file system.", 5 | "license": "MIT OR Apache-2.0", 6 | "authors": [ 7 | "Tauri Programme within The Commons Conservancy" 8 | ], 9 | "repository": "https://github.com/tauri-apps/plugins-workspace", 10 | "type": "module", 11 | "types": "./dist-js/index.d.ts", 12 | "main": "./dist-js/index.cjs", 13 | "module": "./dist-js/index.js", 14 | "exports": { 15 | "types": "./dist-js/index.d.ts", 16 | "import": "./dist-js/index.js", 17 | "require": "./dist-js/index.cjs" 18 | }, 19 | "scripts": { 20 | "build": "rollup -c" 21 | }, 22 | "files": [ 23 | "dist-js", 24 | "README.md", 25 | "LICENSE" 26 | ], 27 | "dependencies": { 28 | "@tauri-apps/api": ">=2.0.0-beta.6" 29 | }, 30 | "devDependencies": { 31 | "@rollup/plugin-node-resolve": "^16.0.0", 32 | "@rollup/plugin-typescript": "^11.1.6", 33 | "rollup": "^4.9.6", 34 | "tslib": "^2.6.2", 35 | "typescript": "^5.3.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/create-app-specific-dirs.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "create-app-specific-dirs" 5 | description = """ 6 | This permissions allows to create the application specific directories. 7 | """ 8 | commands.allow = ["mkdir", "scope-app-index"] 9 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/default.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [default] 4 | description = """ 5 | This set of permissions describes the what kind of 6 | file system access the `fs` plugin has enabled or denied by default. 7 | 8 | #### Granted Permissions 9 | 10 | This default permission set enables read access to the 11 | application specific directories (AppConfig, AppData, AppLocalData, AppCache, 12 | AppLog) and all files and sub directories created in it. 13 | The location of these directories depends on the operating system, 14 | where the application is run. 15 | 16 | In general these directories need to be manually created 17 | by the application at runtime, before accessing files or folders 18 | in it is possible. 19 | 20 | Therefore, it is also allowed to create all of these folders via 21 | the `mkdir` command. 22 | 23 | #### Denied Permissions 24 | 25 | This default permission set prevents access to critical components 26 | of the Tauri application by default. 27 | On Windows the webview data folder access is denied. 28 | 29 | #### Included permissions within this default permission set: 30 | """ 31 | permissions = [ 32 | "create-app-specific-dirs", 33 | "read-app-specific-dirs-recursive", 34 | "deny-default", 35 | ] 36 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/deny-default.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[set]] 4 | identifier = "deny-default" 5 | description = "This denies access to dangerous Tauri relevant files and folders by default." 6 | permissions = ["deny-webview-data-linux", "deny-webview-data-windows"] 7 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/deny-webview-data.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "deny-webview-data-linux" 5 | description = """This denies read access to the 6 | `$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here. 7 | Allowing access can lead to sensitive information disclosure and should be well considered.""" 8 | 9 | [[scope.deny]] 10 | path = "$APPLOCALDATA/**" 11 | 12 | [[permission]] 13 | identifier = "deny-webview-data-windows" 14 | description = """This denies read access to the 15 | `$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here. 16 | Allowing access can lead to sensitive information disclosure and should be well considered.""" 17 | 18 | [[scope.deny]] 19 | path = "$APPLOCALDATA/EBWebView/**" 20 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/read-all.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "read-all" 5 | description = "This enables all read related commands without any pre-configured accessible paths." 6 | commands.allow = [ 7 | "read_dir", 8 | "read_file", 9 | "read", 10 | "open", 11 | "read_text_file", 12 | "read_text_file_lines", 13 | "read_text_file_lines_next", 14 | "seek", 15 | "stat", 16 | "lstat", 17 | "fstat", 18 | "exists", 19 | "watch", 20 | "unwatch", 21 | ] 22 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/read-app-specific-dirs-recursive.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "read-app-specific-dirs-recursive" 5 | description = """ 6 | This permission allows recursive read functionality on the application 7 | specific base directories. 8 | """ 9 | commands.allow = [ 10 | "read_dir", 11 | "read_file", 12 | "read_text_file", 13 | "read_text_file_lines", 14 | "read_text_file_lines_next", 15 | "exists", 16 | "scope-app-recursive", 17 | ] 18 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/read-dirs.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "read-dirs" 5 | description = "This enables directory read and file metadata related commands without any pre-configured accessible paths." 6 | commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists"] 7 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/read-files.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "read-files" 5 | description = "This enables file read related commands without any pre-configured accessible paths." 6 | commands.allow = [ 7 | "read_file", 8 | "read", 9 | "open", 10 | "read_text_file", 11 | "read_text_file_lines", 12 | "read_text_file_lines_next", 13 | "seek", 14 | "stat", 15 | "lstat", 16 | "fstat", 17 | "exists", 18 | 19 | ] 20 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/read-meta.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "read-meta" 5 | description = "This enables all index or metadata related commands without any pre-configured accessible paths." 6 | commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists", "size"] 7 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/scope.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "scope" 5 | description = "An empty permission you can use to modify the global scope." 6 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/write-all.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "write-all" 5 | description = "This enables all write related commands without any pre-configured accessible paths." 6 | commands.allow = [ 7 | "mkdir", 8 | "create", 9 | "copy_file", 10 | "remove", 11 | "rename", 12 | "truncate", 13 | "ftruncate", 14 | "write", 15 | "write_file", 16 | "write_text_file", 17 | ] 18 | -------------------------------------------------------------------------------- /tauri-plugin-fs/permissions/write-files.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [[permission]] 4 | identifier = "write-files" 5 | description = "This enables all file write related commands without any pre-configured accessible paths." 6 | commands.allow = [ 7 | "create", 8 | "copy_file", 9 | "remove", 10 | "rename", 11 | "truncate", 12 | "ftruncate", 13 | "write", 14 | "write_file", 15 | "write_text_file", 16 | ] 17 | -------------------------------------------------------------------------------- /tauri-plugin-fs/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | import { join } from 'path'; 3 | import { cwd } from 'process'; 4 | import typescript from '@rollup/plugin-typescript'; 5 | 6 | const pkg = JSON.parse(readFileSync(join(cwd(), 'package.json'), 'utf8')); 7 | 8 | export default { 9 | input: 'guest-js/index.ts', 10 | output: [ 11 | { 12 | file: pkg.exports.import, 13 | format: 'esm', 14 | }, 15 | { 16 | file: pkg.exports.require, 17 | format: 'cjs', 18 | }, 19 | ], 20 | plugins: [ 21 | typescript({ 22 | declaration: true, 23 | declarationDir: `./${pkg.exports.import.split('/')[0]}`, 24 | }), 25 | ], 26 | external: [ 27 | /^@tauri-apps\/api/, 28 | ...Object.keys(pkg.dependencies || {}), 29 | ...Object.keys(pkg.peerDependencies || {}), 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /tauri-plugin-fs/src/config.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | use serde::Deserialize; 6 | 7 | #[derive(Deserialize)] 8 | #[serde(rename_all = "camelCase", deny_unknown_fields)] 9 | pub struct Config { 10 | /// Whether or not paths that contain components that start with a `.` 11 | /// will require that `.` appears literally in the pattern; `*`, `?`, `**`, 12 | /// or `[...]` will not match. This is useful because such files are 13 | /// conventionally considered hidden on Unix systems and it might be 14 | /// desirable to skip them when listing files. 15 | /// 16 | /// Defaults to `true` on Unix systems and `false` on Windows 17 | // dotfiles are not supposed to be exposed by default on unix 18 | pub require_literal_leading_dot: Option, 19 | } 20 | -------------------------------------------------------------------------------- /tauri-plugin-fs/src/desktop.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | use std::path::PathBuf; 6 | 7 | use tauri::{AppHandle, Runtime}; 8 | 9 | use crate::{FilePath, OpenOptions}; 10 | 11 | pub struct Fs(pub(crate) AppHandle); 12 | 13 | fn path_or_err>(p: P) -> std::io::Result { 14 | match p.into() { 15 | FilePath::Path(p) => Ok(p), 16 | FilePath::Url(u) if u.scheme() == "file" => u 17 | .to_file_path() 18 | .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid file URL")), 19 | FilePath::Url(_) => Err(std::io::Error::new( 20 | std::io::ErrorKind::InvalidInput, 21 | "cannot use a URL to load files on desktop and iOS", 22 | )), 23 | } 24 | } 25 | 26 | impl Fs { 27 | pub fn open>( 28 | &self, 29 | path: P, 30 | opts: OpenOptions, 31 | ) -> std::io::Result { 32 | let path = path_or_err(path)?; 33 | std::fs::OpenOptions::from(opts).open(path) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tauri-plugin-fs/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | use std::path::PathBuf; 6 | 7 | use serde::{Serialize, Serializer}; 8 | 9 | #[derive(Debug, thiserror::Error)] 10 | #[non_exhaustive] 11 | pub enum Error { 12 | #[error(transparent)] 13 | Json(#[from] serde_json::Error), 14 | #[error(transparent)] 15 | Tauri(#[from] tauri::Error), 16 | #[error(transparent)] 17 | Io(#[from] std::io::Error), 18 | #[error("forbidden path: {0}")] 19 | PathForbidden(PathBuf), 20 | /// Invalid glob pattern. 21 | #[error("invalid glob pattern: {0}")] 22 | GlobPattern(#[from] glob::PatternError), 23 | /// Watcher error. 24 | #[cfg(feature = "watch")] 25 | #[error(transparent)] 26 | Watch(#[from] notify::Error), 27 | #[cfg(target_os = "android")] 28 | #[error(transparent)] 29 | PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), 30 | #[error("URL is not a valid path")] 31 | InvalidPathUrl, 32 | #[error("Unsafe PathBuf: {0}")] 33 | UnsafePathBuf(&'static str), 34 | #[error("scheme {0} not supported")] 35 | SchemeNotSupport(String), 36 | } 37 | 38 | impl Serialize for Error { 39 | fn serialize(&self, serializer: S) -> std::result::Result 40 | where 41 | S: Serializer, 42 | { 43 | serializer.serialize_str(self.to_string().as_ref()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tauri-plugin-fs/src/models.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Debug, Clone, Default, Deserialize, Serialize)] 8 | #[serde(rename_all = "camelCase")] 9 | pub struct GetFileDescriptorPayload { 10 | pub uri: String, 11 | pub mode: String, 12 | } 13 | 14 | #[derive(Debug, Clone, Default, Deserialize, Serialize)] 15 | #[serde(rename_all = "camelCase")] 16 | pub struct GetFileDescriptorResponse { 17 | pub fd: Option, 18 | } 19 | -------------------------------------------------------------------------------- /tauri-plugin-fs/src/scope.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Tauri Programme within The Commons Conservancy 2 | // SPDX-License-Identifier: Apache-2.0 3 | // SPDX-License-Identifier: MIT 4 | 5 | use std::path::PathBuf; 6 | 7 | use serde::Deserialize; 8 | 9 | #[derive(Debug)] 10 | pub struct Entry { 11 | pub path: Option, 12 | } 13 | 14 | #[derive(Deserialize)] 15 | #[serde(untagged)] 16 | pub(crate) enum EntryRaw { 17 | Value(PathBuf), 18 | Object { path: PathBuf }, 19 | } 20 | -------------------------------------------------------------------------------- /tauri-plugin-fs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "esnext", 5 | "moduleResolution": "bundler", 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noUnusedLocals": true, 9 | "noImplicitAny": true, 10 | "noEmit": true 11 | }, 12 | "include": ["guest-js/*.ts"], 13 | "exclude": ["dist-js", "node_modules"] 14 | } 15 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gitignore: -------------------------------------------------------------------------------- 1 | /.vs 2 | .DS_Store 3 | .Thumbs.db 4 | *.sublime* 5 | .idea/ 6 | debug.log 7 | package-lock.json 8 | .vscode/settings.json 9 | yarn.lock 10 | 11 | /.tauri 12 | /target 13 | Cargo.lock 14 | node_modules/ 15 | 16 | dist-js 17 | dist 18 | permissions/autogenerated/ 19 | permissions/schemas/ -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/8.5/checksums/checksums.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/8.5/checksums/checksums.lock -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/8.5/dependencies-accessors/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/8.5/dependencies-accessors/gc.properties -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/8.5/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/8.5/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/8.5/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/8.5/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/8.5/gc.properties -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 28 19:04:38 CST 2025 2 | gradle.version=8.5 3 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/config.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 28 19:04:33 CST 2025 2 | java.home=C\:\\Program Files\\Android\\Android Studio\\jbr 3 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /tauri-plugin-mediasession/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tauri-plugin-mediasession" 3 | version = "0.1.0" 4 | authors = ["You"] 5 | description = "" 6 | edition = "2021" 7 | rust-version = "1.77.2" 8 | exclude = ["/examples", "/webview-dist", "/webview-src", "/node_modules"] 9 | links = "tauri-plugin-mediasession" 10 | 11 | [dependencies] 12 | tauri = { version = "2.1.0" } 13 | serde = "1.0" 14 | thiserror = "2" 15 | 16 | [build-dependencies] 17 | tauri-plugin = { version = "2.0.2", features = ["build"] } 18 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/README.md: -------------------------------------------------------------------------------- 1 | # Tauri Plugin mediasession 2 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.tauri 3 | .gradle -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/Users/14438/Documents/GitHub/wuji-tauri/tauri-plugin-mediasession/android/src/main/java/MusicControlService.kt: -------------------------------------------------------------------------------- 1 | private fun getActionIntent(action: String): PendingIntent { 2 | val intent = Intent(Intent.ACTION_MEDIA_BUTTON, null, this, MediaButtonReceiver::class.java).apply { 3 | putExtra(Intent.EXTRA_KEY_EVENT, KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)) 4 | // Optional: You can also add a custom extra to distinguish actions if necessary 5 | // putExtra("custom_action", action) 6 | } 7 | return PendingIntent.getBroadcast( 8 | this, 9 | action.hashCode(), 10 | intent, 11 | PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE 12 | ) 13 | } -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | android { 7 | namespace = "tauri.plugin.mediasession" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | minSdk = 24 12 | 13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles("consumer-rules.pro") 15 | } 16 | 17 | buildTypes { 18 | release { 19 | isMinifyEnabled = false 20 | proguardFiles( 21 | getDefaultProguardFile("proguard-android-optimize.txt"), 22 | "proguard-rules.pro" 23 | ) 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility = JavaVersion.VERSION_1_8 28 | targetCompatibility = JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = "1.8" 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation("androidx.media:media:1.7.0") 37 | implementation("androidx.core:core-ktx:1.9.0") 38 | implementation("androidx.appcompat:appcompat:1.6.0") 39 | implementation("com.google.android.material:material:1.7.0") 40 | testImplementation("junit:junit:4.13.2") 41 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 42 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 43 | implementation(project(":tauri-android")) 44 | } 45 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=true -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/tauri-plugin-mediasession/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Thu Feb 27 12:32:11 CST 2025 8 | sdk.dir=C\:\\Users\\14438\\AppData\\Local\\Android\\Sdk 9 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | google() 6 | 7 | } 8 | resolutionStrategy { 9 | eachPlugin { 10 | switch (requested.id.id) { 11 | case "com.android.library": 12 | useVersion("8.0.2") 13 | break 14 | case "org.jetbrains.kotlin.android": 15 | useVersion("1.8.20") 16 | break 17 | } 18 | } 19 | } 20 | } 21 | 22 | dependencyResolutionManagement { 23 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 24 | repositories { 25 | mavenCentral() 26 | google() 27 | maven { url "https://jitpack.io" } 28 | 29 | } 30 | } 31 | 32 | 33 | include ':tauri-android' 34 | project(':tauri-android').projectDir = new File('./.tauri/tauri-api') 35 | 36 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/androidTest/java/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package tauri.plugin.mediasession 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("tauri.plugin.mediasession", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/java/MediaSessionCallback.kt: -------------------------------------------------------------------------------- 1 | package tauri.plugin.mediasession 2 | 3 | import android.support.v4.media.session.MediaSessionCompat 4 | 5 | class MediaSessionCallback(private val plugin: MediaSessionPlugin) : MediaSessionCompat.Callback() { 6 | companion object { 7 | private const val TAG = "MediaSessionCallback" 8 | } 9 | 10 | override fun onPlay() { 11 | plugin.actionCallback("play") 12 | } 13 | 14 | override fun onPause() { 15 | plugin.actionCallback("pause") 16 | } 17 | 18 | override fun onSeekTo(pos: Long) { 19 | plugin.actionCallback("seekto", pos.toString()) 20 | } 21 | 22 | override fun onRewind() { 23 | plugin.actionCallback("seekbackward") 24 | } 25 | 26 | override fun onFastForward() { 27 | plugin.actionCallback("seekforward") 28 | } 29 | 30 | override fun onSkipToPrevious() { 31 | plugin.actionCallback("previoustrack") 32 | } 33 | 34 | override fun onSkipToNext() { 35 | plugin.actionCallback("nexttrack") 36 | } 37 | 38 | override fun onStop() { 39 | plugin.actionCallback("stop") 40 | } 41 | } -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_forward_30_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_pause_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_play_arrow_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_skip_next_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_skip_previous_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_stop_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_baseline_volume_up_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/main/res/drawable/ic_stop.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/src/test/java/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package tauri.plugin.mediasession 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/android/tauri-plugin-mediasession/android/src/main/java/MusicControlService.kt: -------------------------------------------------------------------------------- 1 | private fun getPlayPauseAction(): NotificationCompat.Action { 2 | val state = mediaSession.controller.playbackState?.state 3 | ?: PlaybackStateCompat.STATE_NONE 4 | 5 | val icon = if (state == PlaybackStateCompat.STATE_PLAYING) { 6 | android.R.drawable.ic_media_pause 7 | } else { 8 | android.R.drawable.ic_media_play 9 | } 10 | 11 | return NotificationCompat.Action( 12 | icon, 13 | if (state == PlaybackStateCompat.STATE_PLAYING) "Pause" else "Play", 14 | getActionIntent(ACTION_PLAY_PAUSE) 15 | ) 16 | } -------------------------------------------------------------------------------- /tauri-plugin-mediasession/build.rs: -------------------------------------------------------------------------------- 1 | const COMMANDS: &[&str] = &[ 2 | "set_metadata", 3 | "set_playback_state", 4 | "set_position_state", 5 | "register_listener", 6 | "remove_listener", 7 | "check_permissions", 8 | "request_permissions", 9 | ]; 10 | 11 | fn main() { 12 | tauri_plugin::Builder::new(COMMANDS) 13 | .android_path("android") 14 | .ios_path("ios") 15 | .build(); 16 | } 17 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/guest-js/index.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from '@tauri-apps/api/core'; 2 | import { PlayMusicItem, PlaybackState, PositionState } from './types'; 3 | export * from './types'; 4 | 5 | export async function setMetedata(value: PlayMusicItem): Promise { 6 | return await invoke<{ value?: boolean }>('plugin:mediasession|set_metadata', { 7 | payload: value, 8 | }).then((r) => (r.value ? r.value : null)); 9 | } 10 | 11 | export async function setPlaybackState( 12 | value: PlaybackState 13 | ): Promise { 14 | return await invoke<{ value?: boolean }>( 15 | 'plugin:mediasession|set_playback_state', 16 | { 17 | payload: value, 18 | } 19 | ).then((r) => (r.value ? r.value : null)); 20 | } 21 | 22 | export async function setPositionState( 23 | value: PositionState 24 | ): Promise { 25 | return await invoke<{ value?: boolean }>( 26 | 'plugin:mediasession|set_position_state', 27 | { 28 | payload: value, 29 | } 30 | ).then((r) => (r.value ? r.value : null)); 31 | } 32 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/guest-js/types.ts: -------------------------------------------------------------------------------- 1 | export interface PlayMusicItem { 2 | title: string; 3 | artist?: string; 4 | album?: string; 5 | cover?: string; 6 | } 7 | export interface PlaybackState { 8 | state: 'playing' | 'paused' | 'stopped'; 9 | } 10 | 11 | export interface PositionState { 12 | duration?: number; 13 | position?: number; 14 | playbackRate?: number; 15 | } 16 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tauri-plugin-mediasession-api", 3 | "version": "0.1.0", 4 | "author": "You", 5 | "description": "", 6 | "type": "module", 7 | "types": "./dist-js/index.d.ts", 8 | "main": "./dist-js/index.cjs", 9 | "module": "./dist-js/index.js", 10 | "exports": { 11 | "types": "./dist-js/index.d.ts", 12 | "import": "./dist-js/index.js", 13 | "require": "./dist-js/index.cjs" 14 | }, 15 | "files": [ 16 | "dist-js", 17 | "README.md" 18 | ], 19 | "scripts": { 20 | "build": "rollup -c", 21 | "prepublishOnly": "pnpm build", 22 | "pretest": "pnpm build" 23 | }, 24 | "dependencies": { 25 | "@tauri-apps/api": ">=2.0.0-beta.6" 26 | }, 27 | "devDependencies": { 28 | "@rollup/plugin-typescript": "^11.1.6", 29 | "rollup": "^4.9.6", 30 | "typescript": "^5.3.3", 31 | "tslib": "^2.6.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/permissions/default.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | description = "Default permissions for the plugin" 3 | permissions = [ 4 | "allow-set-metadata", 5 | "allow-set-playback-state", 6 | "allow-set-position-state", 7 | "allow-register-listener", 8 | "allow-remove-listener", 9 | "allow-check-permissions", 10 | "allow-request-permissions", 11 | ] 12 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs' 2 | import { join } from 'path' 3 | import { cwd } from 'process' 4 | import typescript from '@rollup/plugin-typescript' 5 | 6 | const pkg = JSON.parse(readFileSync(join(cwd(), 'package.json'), 'utf8')) 7 | 8 | export default { 9 | input: 'guest-js/index.ts', 10 | output: [ 11 | { 12 | file: pkg.exports.import, 13 | format: 'esm' 14 | }, 15 | { 16 | file: pkg.exports.require, 17 | format: 'cjs' 18 | } 19 | ], 20 | plugins: [ 21 | typescript({ 22 | declaration: true, 23 | declarationDir: `./${pkg.exports.import.split('/')[0]}` 24 | }) 25 | ], 26 | external: [ 27 | /^@tauri-apps\/api/, 28 | ...Object.keys(pkg.dependencies || {}), 29 | ...Object.keys(pkg.peerDependencies || {}) 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/src/commands.rs: -------------------------------------------------------------------------------- 1 | use tauri::{command, AppHandle, Runtime}; 2 | 3 | use crate::models::*; 4 | use crate::MediasessionExt; 5 | use crate::Result; 6 | 7 | #[command] 8 | pub(crate) async fn set_metadata( 9 | app: AppHandle, 10 | payload: PlayMusicItemRequest, 11 | ) -> Result { 12 | app.mediasession().set_metadata(payload) 13 | } 14 | 15 | #[command] 16 | pub(crate) async fn set_playback_state( 17 | app: AppHandle, 18 | payload: PlaybackStateRequest, 19 | ) -> Result { 20 | app.mediasession().set_playback_state(payload) 21 | } 22 | 23 | #[command] 24 | pub(crate) async fn set_position_state( 25 | app: AppHandle, 26 | payload: PositionStateRequest, 27 | ) -> Result { 28 | app.mediasession().set_position_state(payload) 29 | } 30 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/src/desktop.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | use tauri::{plugin::PluginApi, AppHandle, Runtime}; 3 | 4 | use crate::models::*; 5 | 6 | pub fn init( 7 | app: &AppHandle, 8 | _api: PluginApi, 9 | ) -> crate::Result> { 10 | Ok(Mediasession(app.clone())) 11 | } 12 | 13 | /// Access to the mediasession APIs. 14 | pub struct Mediasession(AppHandle); 15 | 16 | impl Mediasession { 17 | pub fn set_metadata(&self, payload: PlayMusicItemRequest) -> crate::Result { 18 | Ok(BooleanResponse { value: Some(true) }) 19 | } 20 | pub fn set_playback_state( 21 | &self, 22 | payload: PlaybackStateRequest, 23 | ) -> crate::Result { 24 | Ok(BooleanResponse { value: Some(true) }) 25 | } 26 | pub fn set_position_state(&self, payload: PositionStateRequest) -> crate::Result { 27 | Ok(BooleanResponse { value: Some(true) }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/src/error.rs: -------------------------------------------------------------------------------- 1 | use serde::{ser::Serializer, Serialize}; 2 | 3 | pub type Result = std::result::Result; 4 | 5 | #[derive(Debug, thiserror::Error)] 6 | pub enum Error { 7 | #[error(transparent)] 8 | Io(#[from] std::io::Error), 9 | #[cfg(mobile)] 10 | #[error(transparent)] 11 | PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError), 12 | } 13 | 14 | impl Serialize for Error { 15 | fn serialize(&self, serializer: S) -> std::result::Result 16 | where 17 | S: Serializer, 18 | { 19 | serializer.serialize_str(self.to_string().as_ref()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/src/lib.rs: -------------------------------------------------------------------------------- 1 | use tauri::{ 2 | plugin::{Builder, TauriPlugin}, 3 | Manager, Runtime, 4 | }; 5 | 6 | pub use models::*; 7 | 8 | #[cfg(desktop)] 9 | mod desktop; 10 | #[cfg(mobile)] 11 | mod mobile; 12 | 13 | mod commands; 14 | mod error; 15 | mod models; 16 | 17 | pub use error::{Error, Result}; 18 | 19 | #[cfg(desktop)] 20 | use desktop::Mediasession; 21 | #[cfg(mobile)] 22 | use mobile::Mediasession; 23 | 24 | /// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the mediasession APIs. 25 | pub trait MediasessionExt { 26 | fn mediasession(&self) -> &Mediasession; 27 | } 28 | 29 | impl> crate::MediasessionExt for T { 30 | fn mediasession(&self) -> &Mediasession { 31 | self.state::>().inner() 32 | } 33 | } 34 | 35 | /// Initializes the plugin. 36 | pub fn init() -> TauriPlugin { 37 | Builder::new("mediasession") 38 | .invoke_handler(tauri::generate_handler![ 39 | commands::set_metadata, 40 | commands::set_playback_state, 41 | commands::set_position_state, 42 | ]) 43 | .setup(|app, api| { 44 | #[cfg(mobile)] 45 | let mediasession = mobile::init(app, api)?; 46 | #[cfg(desktop)] 47 | let mediasession = desktop::init(app, api)?; 48 | app.manage(mediasession); 49 | Ok(()) 50 | }) 51 | .build() 52 | } 53 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/src/mobile.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | use tauri::{ 3 | plugin::{PluginApi, PluginHandle}, 4 | AppHandle, Runtime, 5 | }; 6 | 7 | use crate::models::*; 8 | 9 | #[cfg(target_os = "ios")] 10 | tauri::ios_plugin_binding!(init_plugin_mediasession); 11 | 12 | // initializes the Kotlin or Swift plugin classes 13 | pub fn init( 14 | _app: &AppHandle, 15 | api: PluginApi, 16 | ) -> crate::Result> { 17 | #[cfg(target_os = "android")] 18 | let handle = api.register_android_plugin("tauri.plugin.mediasession", "MediaSessionPlugin")?; 19 | #[cfg(target_os = "ios")] 20 | let handle = api.register_ios_plugin(init_plugin_mediasession)?; 21 | Ok(Mediasession(handle)) 22 | } 23 | 24 | /// Access to the mediasession APIs. 25 | pub struct Mediasession(PluginHandle); 26 | 27 | impl Mediasession { 28 | pub fn set_metadata(&self, payload: PlayMusicItemRequest) -> crate::Result { 29 | self.0 30 | .run_mobile_plugin("setMetadata", payload) 31 | .map_err(Into::into) 32 | } 33 | pub fn set_playback_state( 34 | &self, 35 | payload: PlaybackStateRequest, 36 | ) -> crate::Result { 37 | self.0 38 | .run_mobile_plugin("setPlaybackState", payload) 39 | .map_err(Into::into) 40 | } 41 | pub fn set_position_state( 42 | &self, 43 | payload: PositionStateRequest, 44 | ) -> crate::Result { 45 | self.0 46 | .run_mobile_plugin("setPositionState", payload) 47 | .map_err(Into::into) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/src/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Clone, Default, Deserialize, Serialize)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct BooleanResponse { 6 | pub value: Option, 7 | } 8 | 9 | #[derive(Debug, Clone, Default, Deserialize, Serialize)] 10 | pub struct PlayMusicItemRequest { 11 | title: String, 12 | artist: Option, 13 | album: Option, 14 | cover: Option, 15 | } 16 | 17 | #[derive(Debug, Clone, Default, Deserialize, Serialize)] 18 | pub struct PlaybackStateRequest { 19 | state: String, 20 | } 21 | 22 | #[derive(Debug, Clone, Default, Deserialize, Serialize)] 23 | pub struct PositionStateRequest { 24 | duration: Option, 25 | position: Option, 26 | playback_rate: Option, 27 | } 28 | 29 | -------------------------------------------------------------------------------- /tauri-plugin-mediasession/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "esnext", 5 | "moduleResolution": "bundler", 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noUnusedLocals": true, 9 | "noImplicitAny": true, 10 | "noEmit": true 11 | }, 12 | "include": ["guest-js/*.ts"], 13 | "exclude": ["dist-js", "node_modules"] 14 | } 15 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moshstudio/wuji-tauri/9144abaff744ca4871a8a999ba18dc2b68a48368/test.html -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "jsx": "preserve", 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "useDefineForClassFields": true, 7 | "experimentalDecorators": true, 8 | "module": "ESNext", 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | 13 | "paths": { 14 | "@/*": ["./src/*"] 15 | }, 16 | "resolveJsonModule": true, 17 | "allowImportingTsExtensions": true, 18 | /* Linting */ 19 | "strict": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noEmit": true, 24 | "isolatedModules": true, 25 | "skipLibCheck": true 26 | }, 27 | "references": [{ "path": "./tsconfig.node.json" }], 28 | "include": [ 29 | "src/**/*.ts", 30 | "src/**/*.d.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "src/**/*.js" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "allowSyntheticDefaultImports": true, 7 | "skipLibCheck": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | --------------------------------------------------------------------------------