├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature.yml └── workflows │ └── build.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── adrive sdk ├── ReadMe.md ├── account.md ├── aims.md ├── album.md ├── archive.md ├── book.md ├── contact.md ├── drive.md ├── file.md ├── filedir.md ├── fileupload.md ├── image.md ├── member.md ├── note.md ├── offline.md ├── recyclebin.md ├── reddot.md ├── sbox.md ├── search.md ├── sfiia.md ├── share.md ├── timeline.md ├── token.md ├── user.md └── video.md ├── changelog.txt ├── electron-builder.json ├── electron ├── main │ ├── index.ts │ ├── mainfile.ts │ ├── utils │ │ └── index.ts │ └── window.ts └── preload │ ├── index.ts │ └── preload-env.d.ts ├── index.html ├── nano-staged.mjs ├── package.json ├── public ├── audio │ ├── down_finished.mp3 │ └── upload_finished.mp3 ├── comlink.js ├── font │ └── VideoJS.woff ├── iconfont.css ├── iconfont.woff2 ├── images │ ├── dark_button.png │ ├── follow_button.png │ ├── light_button.png │ ├── qrcode_1280.jpg │ ├── qrcode_258.jpg │ └── wechat_award.jpeg ├── imgerror.png ├── loading.png ├── main.html ├── main2.html ├── notify.wav ├── pinyinlite_full.min.js ├── prism-vsc-dark-plus.css ├── prism.js ├── sha1filework.js ├── thumbup.svg ├── userface.png ├── wasm.wasm └── wasm_exec.js ├── src ├── App.vue ├── aliapi │ ├── album.ts │ ├── alihttp.ts │ ├── alimodels.ts │ ├── archive.ts │ ├── batch.ts │ ├── dirfilelist.ts │ ├── dirlist.ts │ ├── file.ts │ ├── filecmd.ts │ ├── fileicon.ts │ ├── filewalk.ts │ ├── following.ts │ ├── models.ts │ ├── server.tsx │ ├── share.ts │ ├── sharelist.ts │ ├── trash.ts │ ├── upload.ts │ ├── uploadOpenApi.ts │ ├── uploaddisk.ts │ ├── uploadhash.ts │ ├── uploadhashpool.ts │ ├── uploadmem.ts │ ├── user.ts │ └── utils.ts ├── assets │ ├── antd.css │ ├── contentview.css │ ├── fileitem.css │ ├── global.css │ ├── preview.css │ ├── sidebar.css │ └── style.css ├── down │ ├── DownDAL.ts │ ├── DownDowned.vue │ ├── DownDowning.vue │ ├── DownM3U8.vue │ ├── DownSync.vue │ ├── DownUploaded.vue │ ├── DownUploading.vue │ ├── DownedStore.ts │ ├── DowningStore.ts │ ├── UploadedStore.ts │ ├── UploadingStore.ts │ ├── index.vue │ └── m3u8 │ │ ├── M3u8DownloadDAL.ts │ │ ├── M3u8Downloaded.vue │ │ ├── M3u8DownloadedStore.ts │ │ ├── M3u8Downloading.vue │ │ └── M3u8DownloadingStore.ts ├── env.d.ts ├── global.d.ts ├── layout │ ├── MyLoading.vue │ ├── MyModal.vue │ ├── MySplit.vue │ ├── MySwitch.vue │ ├── MySwitchTab.vue │ ├── MyTags.vue │ ├── PageCode.vue │ ├── PageHelp.vue │ ├── PageImage.vue │ ├── PageLoading.vue │ ├── PageMain.ts │ ├── PageMain.vue │ ├── PageOffice.vue │ ├── PageVideo.vue │ ├── PageVideoXBT.vue │ └── PageWorker.vue ├── main.ts ├── pan │ ├── PanLeft.vue │ ├── PanRight.vue │ ├── index.vue │ ├── menus │ │ ├── DirLeftMenu.vue │ │ ├── DirTopPath.vue │ │ ├── FileRightMenu.vue │ │ ├── FileTopbtn.vue │ │ ├── PanTopbtn.vue │ │ ├── TrashRightMenu.vue │ │ └── TrashTopbtn.vue │ ├── pandal.ts │ ├── panfilestore.ts │ ├── pantreestore.ts │ └── topbtns │ │ ├── AlphaModal.vue │ │ ├── ArchiveModal.vue │ │ ├── ArchivePasswordModal.vue │ │ ├── CopyFileTreeModal.vue │ │ ├── CreatNewAlbumModal.vue │ │ ├── CreatNewDirModal.vue │ │ ├── CreatNewDirMultiModal.vue │ │ ├── CreatNewFileModal.vue │ │ ├── CreatNewShareLinkModal.vue │ │ ├── DLNAPlayerModal.vue │ │ ├── DaoRuShareLinkModal.vue │ │ ├── DaoRuShareLinkMultiModal.vue │ │ ├── DownloadModal.vue │ │ ├── M3U8DownloadModal.vue │ │ ├── MoveToAlbumModal.vue │ │ ├── RenameModal.vue │ │ ├── RenameMultiModal.vue │ │ ├── SearchPanModal.vue │ │ ├── SelectPanDirModal.vue │ │ ├── ShuXingModal.vue │ │ ├── ShuXingMultiModal.vue │ │ ├── UploadModal.vue │ │ ├── renamemulti.ts │ │ └── topbtn.ts ├── pic │ ├── Content.vue │ ├── PicDAL.ts │ ├── PicLeft.vue │ ├── PicRight.vue │ ├── Preview.vue │ ├── Sidebar.vue │ ├── index.vue │ └── picstore.ts ├── rss │ ├── ScanDAL.ts │ ├── appsame │ │ ├── AppSame.vue │ │ └── same.ts │ ├── index.vue │ ├── rssdrivecopy │ │ ├── RssDriveCopy.vue │ │ └── drivecopy.ts │ ├── rssjiami │ │ ├── RssJiaMi.vue │ │ └── jiami.ts │ ├── rssrename │ │ └── RssRename.vue │ ├── rssscanclean │ │ ├── RssScanClean.vue │ │ └── ScanClean.ts │ ├── rssscanenmpty │ │ ├── RssScanEnmpty.vue │ │ └── scanenmpty.ts │ ├── rssscanpunish │ │ ├── RssScanPunish.vue │ │ └── scanpunish.ts │ ├── rssscansame │ │ ├── RssScanSame.vue │ │ └── scansame.ts │ ├── rssusercopy │ │ ├── RssUserCopy.vue │ │ └── usercopy.ts │ └── rssxima │ │ ├── RssXiMa.vue │ │ └── xima.ts ├── setting │ ├── SettingAria.vue │ ├── SettingDebug.vue │ ├── SettingDown.vue │ ├── SettingLocalAria.vue │ ├── SettingLog.vue │ ├── SettingPan.vue │ ├── SettingPlay.vue │ ├── SettingProxy.vue │ ├── SettingUI.vue │ ├── SettingUpload.vue │ ├── ShutDown.vue │ ├── index.vue │ └── settingstore.ts ├── share │ ├── following │ │ ├── FollowingDAL.ts │ │ ├── MyFollowingRight.vue │ │ ├── MyFollowingStore.ts │ │ ├── OtherFollowingRight.vue │ │ └── OtherFollowingStore.ts │ ├── index.vue │ └── share │ │ ├── EditShareLinkModal.vue │ │ ├── MyShareRight.vue │ │ ├── MyShareStore.ts │ │ ├── OtherShareRight.vue │ │ ├── OtherShareStore.ts │ │ ├── ShareDAL.ts │ │ ├── ShareSiteRight.vue │ │ └── ShowShareLinkModal.vue ├── store │ ├── appstore.ts │ ├── footstore.ts │ ├── index.ts │ ├── keyboardstore.ts │ ├── logstore.ts │ ├── modalstore.ts │ ├── mousestore.ts │ ├── serverstore.ts │ ├── treestore.ts │ └── winstore.ts ├── transfer │ ├── uploaddal.ts │ ├── uploadingdal.ts │ └── uploadingdata.ts ├── user │ ├── SponsorInfo.vue │ ├── UserInfo.vue │ ├── UserLogin.vue │ ├── UserSpaceModal.vue │ ├── userdal.ts │ └── userstore.ts ├── utils │ ├── antdtree.ts │ ├── appcache.ts │ ├── aria2c.ts │ ├── config.ts │ ├── db.ts │ ├── dbcache.ts │ ├── dbdown.ts │ ├── dbupload.ts │ ├── debounce.ts │ ├── debuglog.ts │ ├── electronhelper.ts │ ├── filehelper.ts │ ├── filenameorder.ts │ ├── foot.ts │ ├── format.ts │ ├── idhelper.ts │ ├── keyboardhelper.ts │ ├── levemap.ts │ ├── message.ts │ ├── modal.ts │ ├── mosehelper.ts │ ├── openfile.ts │ ├── selecthelper.ts │ ├── sha1workerpool.ts │ ├── shareurl.ts │ ├── utils.ts │ └── worker.ts └── workerpage │ ├── uidownload.ts │ ├── uiupload.ts │ ├── uploader.ts │ └── workercmd.ts ├── static ├── crx │ ├── devtools.html │ ├── devtools.js │ └── manifest.json ├── engine │ ├── aria2.conf │ ├── darwin │ │ ├── arm64 │ │ │ └── aria2c │ │ └── x64 │ │ │ └── aria2c │ ├── linux │ │ ├── arm64 │ │ │ └── aria2c │ │ ├── armv7l │ │ │ └── aria2c │ │ └── x64 │ │ │ └── aria2c │ └── win32 │ │ ├── ia32 │ │ └── aria2c.exe │ │ └── x64 │ │ └── aria2c.exe └── images │ ├── icon.icns │ ├── icon_24.png │ ├── icon_256.ico │ ├── icon_256.png │ └── icon_64.png ├── tsconfig.json ├── tsconfig.node.json ├── types.d.ts ├── vite.config.ts ├── yarn.lock └── 源码开发打包帮助.md /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Problems with the software 3 | title: '[Bug] {{请输入标题,不要留空 - Please enter a title, do not leave it blank.}}' 4 | labels: ['bug'] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | **非常感谢您的反馈!Thank you very much for your feedback!** 10 | 11 | 有关讨论、建议或者咨询的内容请去往[讨论区](https://github.com/gaozhangmin/aliyunpan/discussions)。 12 | 13 | For suggestions or help, please consider using [Github Discussion](https://github.com/gaozhangmin/aliyunpan/discussions) instead. 14 | 15 | - type: checkboxes 16 | attributes: 17 | label: Please search before asking 18 | description: | 19 | 辛苦提 Bug 前,检索一下 [问题](https://github.com/gaozhangmin/aliyunpan/issues?q=) 列表是否已经存在类似问题。麻烦提供系统版本、录屏或者截图、复现步骤,有助于更好的解决问题。 20 | 21 | 非 Bug 相关,烦请移步 [讨论区](https://github.com/gaozhangmin/aliyunpan/discussions) 找寻有关讨论。 22 | 23 | Please search [issues](https://github.com/gaozhangmin/aliyunpan/issues) to check if your issue has already been reported. 24 | 25 | Not related to bugs, please go to the [discussion area](https://github.com/gaozhangmin/aliyunpan/discussions) for relevant discussions. 26 | 27 | options: 28 | - label: > 29 | I searched in the [issues](https://github.com/gaozhangmin/aliyunpan/issues) and found nothing similar. 30 | required: true 31 | - type: checkboxes 32 | attributes: 33 | label: Please read README 34 | description: | 35 | 辛苦提 Bug 前,请仔细阅读一下 README 中的 [Troubleshooting](https://github.com/gaozhangmin/aliyunpan/tree/main#troubleshooting) 是否已经给出相关解决方案 36 | 37 | Before reporting bugs (especially for issues such as missing icons in the desktop application, permission pop-ups, and damaged report files), please carefully read the [Troubleshooting](https://github.com/gaozhangmin/aliyunpan/tree/main#troubleshooting) section in README to see if relevant solutions have already been provided. 38 | 39 | options: 40 | - label: I have read the troubleshooting section in the README in detail. 41 | required: true 42 | 43 | - type: input 44 | attributes: 45 | label: 使用的版本 46 | description: > 47 | 请提供您正在使用的 小白羊 的版本。Please provide the version of 小白羊 you are using. For example, `3.11.7`. 48 | validations: 49 | required: true 50 | - type: input 51 | attributes: 52 | label: 系统 System 53 | description: > 54 | 请提供您正在使用的系统。Please provide the version of the System you are using. For example, `macOS 11.2.3`. 55 | validations: 56 | required: true 57 | - type: textarea 58 | attributes: 59 | label: 复现步骤 Reproduce step 60 | description: > 61 | 请提供完整且简明的复现步骤,以方便及时定位并解决问题。Please provide complete and concise reproduction steps to facilitate timely identification and resolution of the issue. 62 | validations: 63 | required: true 64 | - type: textarea 65 | attributes: 66 | label: 你看到了什么错误?What errors do you see? 67 | validations: 68 | required: true 69 | - type: textarea 70 | attributes: 71 | label: 你期望看到什么?What did you expect to see? 72 | validations: 73 | required: true 74 | - type: textarea 75 | attributes: 76 | label: 还有其他的内容吗?Anything else? 77 | - type: checkboxes 78 | attributes: 79 | label: 你是否愿意提交一份 PR 来修改这个错误?Are you willing to submit a PR? 80 | description: > 81 | 我们期待开发人员和用户的帮助,以解决在 小白羊 中发现的任何问题。 如果您愿意通过提交 PR 来解决此问题,请勾选。We eagerly anticipate developers' and users' support and collaboration in resolving any issues found in 小白羊. If you are willing to offer a solution by submitting a PR to fix this matter, kindly mark the checkbox provided. 82 | options: 83 | - label: 我愿意提供 PR! I'm willing to submit a PR! 84 | - type: markdown 85 | attributes: 86 | value: '非常感谢您的反馈!Thank you very much for your feedback!' -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question or get support 4 | url: https://github.com/gaozhangmin/aliyunpan/discussions/categories/q-a 5 | about: Ask a question or request support for XiaoBaiYang -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature 2 | description: Add new feature, improve code, and more 3 | labels: [ "enhancement" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | **非常感谢您的功能建议!Thank you very much for your feature proposal!** 9 | - type: checkboxes 10 | attributes: 11 | label: Search before asking 12 | description: > 13 | 请在提交前搜索 [issues](https://github.com/gaozhangmin/aliyunpan/issues),以查看您的问题是否已经被提交。 14 | 15 | Please search [issues](https://github.com/gaozhangmin/aliyunpan/issues) to check if your issue has already been reported. 16 | options: 17 | - label: > 18 | 在 [issues](https://github.com/gaozhangmin/aliyunpan/issues) 中没有找到类似的内容。 19 | 20 | I searched in the [issues](https://github.com/gaozhangmin/aliyunpan/issues) and found nothing similar. 21 | required: true 22 | - type: input 23 | attributes: 24 | label: feature 25 | description: > 26 | 一句话概括你的功能建议。Please provide a brief description of your feature proposal. 27 | validations: 28 | required: true 29 | - type: textarea 30 | attributes: 31 | label: 描述 Motivation 32 | description: > 33 | 解释一下这个功能将如何解决您的问题。 34 | 35 | Explain how this feature will resolve your problem, including the way it addresses the issue that you are facing. 36 | validations: 37 | required: true 38 | - type: textarea 39 | attributes: 40 | label: Solution 41 | description: 描述建议的解决方案。Describe the proposed solution. (if you have any additional information, please add it here.) 42 | - type: textarea 43 | attributes: 44 | label: 还有其他内容吗?Anything else? 45 | - type: checkboxes 46 | attributes: 47 | label: 你是否愿意提交一份 PR?Are you willing to submit a PR? 48 | description: > 49 | 我们期待开发人员和用户的帮助,以解决在 小白羊 中发现的任何问题。 如果您愿意通过提交 PR 来解决此问题,请勾选。We eagerly anticipate developers' and users' support and collaboration in resolving any issues found in 小白羊. If you are willing to offer a solution by submitting a PR to fix this matter, kindly mark the checkbox provided. 50 | options: 51 | - label: 我愿意提供 PR! I'm willing to submit a PR! 52 | - type: markdown 53 | attributes: 54 | value: "非常感谢您的功能建议!Thank you very much for your feature proposal!" 55 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*.*.*' 7 | 8 | jobs: 9 | create-release: 10 | permissions: 11 | contents: write 12 | runs-on: ubuntu-20.04 13 | outputs: 14 | release_id: ${{ steps.create-release.outputs.id }} 15 | release_upload_url: ${{ steps.create-release.outputs.upload_url }} 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Get version 21 | id: get_version 22 | uses: battila7/get-version-action@v2 23 | 24 | - name: Create Release 25 | id: create-release 26 | uses: ncipollo/release-action@v1 27 | with: 28 | draft: true 29 | name: ${{ steps.get_version.outputs.version }} 30 | tag: ${{ steps.get_version.outputs.version }} 31 | body: "${{ steps.tag.outputs.message }}" 32 | release: 33 | needs: create-release 34 | name: build and release 35 | runs-on: ${{ matrix.os }} 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | os: [windows-latest, macos-latest, ubuntu-latest] 41 | 42 | permissions: 43 | contents: write 44 | 45 | steps: 46 | - name: Checkout Git repository 47 | uses: actions/checkout@v3 48 | 49 | - name: Install Node 50 | uses: actions/setup-node@v3 51 | with: 52 | node-version: 16 53 | registry-url: https://registry.npmjs.org/ 54 | 55 | - name: Get yarn cache directory path 56 | id: yarn-cache-dir-path 57 | run: echo "::set-output name=dir::$(yarn cache dir)" 58 | 59 | - uses: actions/cache@v3 60 | id: cache-yarn-cache 61 | with: 62 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 63 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 64 | restore-keys: | 65 | ${{ runner.os }}-yarn- 66 | 67 | - uses: actions/cache@v3 68 | id: cache-node-modules 69 | with: 70 | path: node_modules 71 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 72 | restore-keys: | 73 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 74 | 75 | - name: Install Dependencies 76 | if: | 77 | steps.cache-yarn-cache.outputs.cache-hit != 'true' || 78 | steps.cache-node-modules.outputs.cache-hit != 'true' 79 | run: | 80 | npm install -g yarn 81 | yarn install 82 | 83 | - name: Make Aria2c Executable 84 | if: matrix.os != 'windows-latest' 85 | run: | 86 | chmod -R 777 ./static/engine/ 87 | 88 | - name: Build Electron app 89 | uses: samuelmeuli/action-electron-builder@v1.6.0 90 | with: 91 | release: ${{ startsWith(github.ref, 'refs/tags') }} 92 | github_token: ${{ secrets.github_token }} 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist* 4 | *.local 5 | .debug.env 6 | 7 | tmp 8 | **/.tmp 9 | release 10 | 11 | .idea 12 | yarn.lock 13 | 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 260, 4 | "semi": false, 5 | "quoteProps": "as-needed", 6 | "jsxSingleQuote": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "jsxBracketSameLine": true, 10 | "arrowParens": "always", 11 | "requirePragma": false, 12 | "insertPragma": false, 13 | "wrapAttributes": false, 14 | "sortAttributes": true, 15 | "proseWrap": "preserve", 16 | "htmlWhitespaceSensitivity": "css", 17 | "endOfLine": "lf", 18 | "overrides": [ 19 | { 20 | "files": ".prettierrc", 21 | "options": { "parser": "json" } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Zhangao 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. -------------------------------------------------------------------------------- /adrive sdk/ReadMe.md: -------------------------------------------------------------------------------- 1 | ### 阿里云盘接口 2 | 3 | > 2022-03整理的阿里云盘SDK接口数据 4 | 5 | 仅用来记录官方提供的接口参数,共整理了144个,比较全了,与编程语言无关,方便大家据此开发 -------------------------------------------------------------------------------- /adrive sdk/account.md: -------------------------------------------------------------------------------- 1 | #### 刷新 token 2 | 3 | POST: `https://auth.aliyundrive.com/v2/account/token` 4 | 5 | ```json 6 | { "grant_type": "refresh_token", "app_id": "pJZInNHN2dZWk8qg", "refresh_token": "c65bf6d104ac510885c0124d74c4a099" } 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "default_sbox_drive_id": "9600002", 14 | "role": "user", 15 | "device_id": "2909000000004f01aa28264bfc30e4ed", 16 | "user_name": "151***111", 17 | "need_link": false, 18 | "expire_time": "2022-03-21T06:33:21Z", 19 | "pin_setup": true, 20 | "need_rp_verify": false, 21 | "avatar": "https://ccp-bj29-bj-1592982087.oss-cn-beijing.aliyuncs.com/2GhCur3G%2F...", 22 | "user_data": { 23 | "DingDingRobotUrl": "https://oapi.dingtalk.com/robot/send?access_token=0b4a936d0e...", 24 | "EncourageDesc": "内测期间有效反馈前10名用户将获得终身免费会员", 25 | "FeedBackSwitch": true, 26 | "FollowingDesc": "34848372", 27 | "back_up_config": { 28 | "手机备份": { "folder_id": "605c0c29b7acf78b6ee34bf095594f7654e57d68", "photo_folder_id": "605c0c299af37539f3d34879b2f0d1c5543f27d5", "sub_folder": {}, "video_folder_id": "605c0c29e520154c22644bed904b76b25ced317a" } 29 | }, 30 | "ding_ding_robot_url": "https://oapi.dingtalk.com/robot/send?access_token=0b4a936d0e...", 31 | "encourage_desc": "内测期间有效反馈前10名用户将获得终身免费会员", 32 | "feed_back_switch": true, 33 | "following_desc": "34848372" 34 | }, 35 | "token_type": "Bearer", 36 | "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..................", 37 | "default_drive_id": "9600002", 38 | "domain_id": "bj29", 39 | "refresh_token": "b2d9c244d8a24df38aa1a5dec59e2a92", 40 | "is_first_login": false, 41 | "user_id": "9400000000bc480bbcbbb1e074f55a7f", 42 | "nick_name": "myname", 43 | "exist_link": [], 44 | "state": "", 45 | "expires_in": 7200, 46 | "status": "enabled" 47 | } 48 | ``` 49 | 50 | #### 退出登录 51 | 52 | POST: `https://auth.aliyundrive.com/v2/account/revoke` 53 | 54 | ```json 55 | 56 | ``` 57 | 58 | Response: 59 | 60 | ```json 61 | 62 | ``` 63 | 64 | #### 检查账号是否存在 x 65 | 66 | POST: `https://auth.aliyundrive.com/v2/account/mobile/check_exist` 67 | 68 | ```json 69 | { "app_id": "pJZInNHN2dZWk8qg", "phone_number": "151***111", "phone_region": "86" } 70 | ``` 71 | 72 | Response: 73 | 74 | ```json 75 | { "is_exist": true } 76 | ``` 77 | -------------------------------------------------------------------------------- /adrive sdk/contact.md: -------------------------------------------------------------------------------- 1 | #### 通讯录列出 2 | 3 | POST: `https://api.aliyundrive.com/adrive/v1/contact/list` 4 | 5 | ```json 6 | {} 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "items": [ 14 | { 15 | "id": 223963400, 16 | "content": { 17 | "format": "vcard", 18 | "hash": "1a48648e8b140ae80be048f1681cfbe24a7b9579c2ffbabe2686eb79338dfe14", 19 | "value": "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:ez-vcard 0.11.2\r\nKIND:individual\r\nFN:高青\r\nN:高;青;;;\r\nTEL;TYPE=cell:15000065001\r\nEND:VCARD\r\n", 20 | "version": "4.0" 21 | }, 22 | "gmt_create": 1647864044589 23 | }, 24 | { 25 | "id": 224054214, 26 | "content": { 27 | "format": "vcard", 28 | "hash": "978edd2d967b94b34d3a90c00cbd4819e239f21a6f72f6a2f87b522fe81a62f4", 29 | "value": "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:ez-vcard 0.11.2\r\nKIND:individual\r\nFN:木门\r\nN:木;门;;;\r\nTEL;TYPE=cell:03100000981\r\nEND:VCARD\r\n", 30 | "version": "4.0" 31 | }, 32 | "gmt_create": 1647864055972 33 | } 34 | ], 35 | "total_count": 2 36 | } 37 | ``` 38 | 39 | #### 通讯录删除 40 | 41 | POST: `https://api.aliyundrive.com/adrive/v1/contact/delete` 42 | 43 | ```json 44 | { "ids": [224206708] } 45 | ``` 46 | 47 | Response: 48 | 49 | ```json 50 | {} 51 | ``` 52 | 53 | #### 通讯录备份添加 54 | 55 | POST: `https://api.aliyundrive.com/adrive/v1/contact/add` 56 | 57 | ```json 58 | { 59 | "items": [ 60 | { 61 | "hash": "978edd2d967b94b34d3a90c00cbd4819e239f21a6f72f6a2f87b522fe81a62f4", 62 | "version": "4.0", 63 | "value": "BEGIN:VCARD\r\nVERSION:4.0\r\nN:木;门\r\nTEL;TYPE=cell:03000080981\r\nKIND:individual\r\nEND:VCARD\r\n", 64 | "avatar": "", 65 | "format": "vcard" 66 | } 67 | ] 68 | } 69 | ``` 70 | 71 | Response: 72 | 73 | ```json 74 | { "items": [{ "id": 223963801, "content": { "format": "vcard", "hash": "978edd2d967b94b34d3a90c00cbd4819e239f21a6f72f6a2f87b522fe81a62f4", "version": "4.0" } }] } 75 | ``` 76 | -------------------------------------------------------------------------------- /adrive sdk/filedir.md: -------------------------------------------------------------------------------- 1 | #### 文件夹大小 2 | 3 | POST: `https://api.aliyundrive.com/adrive/v1/file/get_folder_size_info` 4 | 5 | ```json 6 | { "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" } 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { "size": 67200, "folder_count": 0, "file_count": 600, "reach_limit": true } 13 | ``` 14 | 15 | #### 批量读取文件夹封面 16 | 17 | POST: `https://api.aliyundrive.com/adrive/v1/file/cover/batchGet` 18 | 19 | ```json 20 | { "drive_id": "9600002", "file_ids": ["623b00000000d89ef21d4118838aed83de7575ba", "6061000000001af7c3034e3590ea7d5a50f58015"] } 21 | ``` 22 | 23 | Response: 24 | 25 | ```json 26 | { 27 | "items": [ 28 | { 29 | "folder_file_id": "623b00000000d89ef21d4118838aed83de7575ba", 30 | "cover_file_id": "6061000000001af7c3034e3590ea7d5a50f58015", 31 | "cover_file_thumbnail": "https://bj29.cn-beijing.data.alicloudccp.com/RV5OBihM%2F...", 32 | "cover_file_name": "反贪5.mp4", 33 | "cover_file_category": "video" 34 | }, 35 | { "folder_file_id": "623b00000000d89ef21d4118838aed83de7575ba", "cover_file_id": "6061000000001af7c3034e3590ea7d5a50f58015", "cover_file_name": "bbbc", "cover_file_category": "others" } 36 | ] 37 | } 38 | ``` 39 | 40 | #### 读取文件夹封面 41 | 42 | POST: `https://api.aliyundrive.com/adrive/v1/file/cover/get` 43 | 44 | ```json 45 | { "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" } 46 | ``` 47 | 48 | Response: 49 | 50 | ```json 51 | { 52 | "folder_file_id": "623b00000000d89ef21d4118838aed83de7575ba", 53 | "cover_file_id": "6061000000001af7c3034e3590ea7d5a50f58015", 54 | "cover_file_thumbnail": "https://bj29.cn-beijing.data.alicloudccp.com/RV5OBihM%2F...", 55 | "cover_file_name": "firmware.3911(1).dat", 56 | "cover_file_category": "video" 57 | } 58 | ``` 59 | 60 | #### 设置-读取开启文件夹封面 61 | 62 | POST: `https://api.aliyundrive.com/adrive/v1/file/cover/config/get` 63 | 64 | ```json 65 | {} 66 | ``` 67 | 68 | Response: 69 | 70 | ```json 71 | {"enable":true} 72 | ``` 73 | 74 | #### 设置-保存开启文件夹封面 75 | 76 | POST: `https://api.aliyundrive.com/adrive/v1/file/cover/config/set` 77 | 78 | ```json 79 | {"enable":true} 80 | ``` 81 | 82 | Response: 83 | 84 | ```text 85 | HTTP/1.1 200 OK 86 | ``` 87 | -------------------------------------------------------------------------------- /adrive sdk/offline.md: -------------------------------------------------------------------------------- 1 | #### 离线任务列表 2 | 3 | POST: `https://api.aliyundrive.com/adrive/v1/offline/jobsList` 4 | 5 | ```json 6 | {} 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { "maxResults": 10, "nextToken": "", "result": [] } 13 | ``` 14 | -------------------------------------------------------------------------------- /adrive sdk/recyclebin.md: -------------------------------------------------------------------------------- 1 | #### 列出回收站 2 | 3 | POST: `https://api.aliyundrive.com/v2/recyclebin/list` 4 | 5 | ```json 6 | { "fields": "*", "all": false, "drive_id": "9600002", "limit": 50 } 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "items": [ 14 | { 15 | "drive_id": "9600002", 16 | "domain_id": "bj29", 17 | "file_id": "623b00000000d89ef21d4118838aed83de7575ba", 18 | "name": "[1.3.1].mp4", 19 | "type": "file", 20 | "content_type": "application/oct-stream", 21 | "created_at": "2022-01-19T04:51:12.832Z", 22 | "updated_at": "2022-03-10T03:10:04.074Z", 23 | "trashed_at": "2022-03-10T03:10:04.074Z", 24 | "file_extension": "mp4", 25 | "mime_type": "application/octet-stream", 26 | "mime_extension": "unknown", 27 | "hidden": false, 28 | "size": 94814980, 29 | "starred": false, 30 | "status": "available", 31 | "labels": ["艺术品"], 32 | "parent_file_id": "613800000000336ae9164455b135a9729a298c9c", 33 | "crc64_hash": "1548000000008183211", 34 | "content_hash": "4DBF0000000023E6E756C29AF6AC487217921D53", 35 | "content_hash_name": "sha1", 36 | "url": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...", 37 | "thumbnail": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...", 38 | "category": "video", 39 | "encrypt_mode": "none", 40 | "video_media_metadata": { 41 | "width": 1280, 42 | "height": 720, 43 | "video_media_video_stream": [{ "duration": "1530.680000", "clarity": "720", "fps": "25/1", "code_name": "h264" }], 44 | "video_media_audio_stream": [{ "duration": "1530.581333", "channels": 2, "channel_layout": "stereo", "bit_rate": "143625", "code_name": "aac", "sample_rate": "48000" }], 45 | "duration": "1530.701333" 46 | }, 47 | "punish_flag": 0, 48 | "creator_type": "User", 49 | "creator_id": "9400000000bc480bbcbbb1e074f55a7f", 50 | "creator_name": "myname", 51 | "last_modifier_type": "User", 52 | "last_modifier_id": "9400000000bc480bbcbbb1e074f55a7f", 53 | "last_modifier_name": "myname", 54 | "revision_id": "6138000000000b81a8164550b1e7cba1d7fbe111" 55 | } 56 | ], 57 | "next_marker": "" 58 | } 59 | ``` 60 | 61 | #### 恢复文件 62 | 63 | POST: `https://api.aliyundrive.com/v2/recyclebin/restore` 64 | 65 | ```json 66 | { "file_id": "623b00000000d89ef21d4118838aed83de7575ba", "drive_id": "9600002" } 67 | ``` 68 | 69 | Response: 70 | 71 | ```text 72 | HTTP/1.1 204 No Content 73 | ``` 74 | 75 | #### 删除文件(放入回收站) 76 | 77 | POST: `https://api.aliyundrive.com/v2/recyclebin/trash` 78 | 79 | ```json 80 | { "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" } 81 | ``` 82 | 83 | Response: 84 | 85 | ```text 86 | HTTP/1.1 204 No Content 87 | ``` 88 | 89 | #### 删除文件(从回收站彻底删除) 90 | 91 | POST: `https://api.aliyundrive.com/v3/file/delete` 92 | 93 | ```json 94 | { "permanently": true, "file_id": "623b00000000d89ef21d4118838aed83de7575ba", "drive_id": "9600002" } 95 | ``` 96 | 97 | Response: 98 | 99 | ```text 100 | HTTP/1.1 204 No Content 101 | ``` 102 | 103 | #### 清空回收站 104 | 105 | POST: `https://api.aliyundrive.com/v2/recyclebin/clear` 106 | 107 | ```json 108 | { "drive_id": "9600002" } 109 | ``` 110 | 111 | Response: 112 | 113 | ```json 114 | { "domain_id": "bj29", "drive_id": "9600002", "task_id": "e026000000007f609bcd6aa71b8fde94" } 115 | ``` 116 | -------------------------------------------------------------------------------- /adrive sdk/reddot.md: -------------------------------------------------------------------------------- 1 | #### 订阅的账号有更新 2 | 3 | POST: `https://api.aliyundrive.com/adrive/v1/reddot/get` 4 | 5 | ```json 6 | {} 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "items": [ 14 | { 15 | "code": "followed_user_has_new_activity", 16 | "context": { 17 | "creator": { 18 | "description": "中国国家地理景观官方账号,带你领略目酣神醉的壮美景观、发现中国各地独具特色的人文胜迹。", 19 | "avatar": "https://ccp-bj29-bj-1592982087.oss-cn-beijing.aliyuncs.com/2GhCur3G%2F...", 20 | "user_id": "9400000000bc480bbcbbb1e074f55a7f", 21 | "nick_name": "中国国家地理景观", 22 | "phone": "136***902" 23 | } 24 | } 25 | } 26 | ] 27 | } 28 | ``` 29 | 30 | #### 标记已读 31 | 32 | POST: `https://api.aliyundrive.com/adrive/v1/reddot/read` 33 | 34 | ```json 35 | {"code":"followed_user_has_new_activity"} 36 | ``` 37 | 38 | Response: 39 | 40 | ```json 41 | {} 42 | ``` 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /adrive sdk/sbox.md: -------------------------------------------------------------------------------- 1 | #### 保险箱 2 | 3 | POST: `https://api.aliyundrive.com/v2/sbox/get` 4 | 5 | ```json 6 | {} 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { "drive_id": "9600002", "sbox_used_size": 0, "sbox_real_used_size": 0, "sbox_total_size": 53687091200, "recommend_vip": "svip", "pin_setup": true, "locked": true, "insurance_enabled": false } 13 | ``` 14 | 15 | #### 解锁 16 | 17 | POST: `https://api.aliyundrive.com/v2/sbox/unlock` 18 | 19 | ```json 20 | { 21 | "drive_id": "9600002", 22 | "app_id": "25dzX3vbYqktVxyX", 23 | "encrypted_pin": "pteN00000000/gLZpQaFKA==", 24 | "encrypted_key": "nNaV......r13doYbpmJxag==" 25 | } 26 | ``` 27 | 28 | Response: 29 | 30 | ```json 31 | { "drive_id": "9600002" } 32 | ``` 33 | 34 | #### 重新锁定 35 | 36 | POST: `https://api.aliyundrive.com/v2/sbox/lock` 37 | 38 | ```json 39 | {"drive_id":"9600002"} 40 | ``` 41 | 42 | Response: 43 | 44 | ```json 45 | { "drive_id": "9600002" } 46 | ``` 47 | -------------------------------------------------------------------------------- /adrive sdk/search.md: -------------------------------------------------------------------------------- 1 | #### 首页 widgets 2 | 3 | POST: `https://api.aliyundrive.com/v2/file/search` 4 | 5 | ```json 6 | //截图 7 | { 8 | "return_total_count": true, 9 | "image_thumbnail_process": "image/resize,m_lfit,w_256,limit_0/format,jpg", 10 | "order_by": "last_access_at DESC,updated_at DESC,image_time DESC", 11 | "query": "(label = '手机截图' or label = '截图') and category in ['video','image'] and status = 'available' and hidden = false", 12 | "limit": 100, 13 | "drive_id": "9600002" 14 | } 15 | ``` 16 | 17 | ```json 18 | //证件 19 | { 20 | "return_total_count": true, 21 | "image_thumbnail_process": "image/resize,m_lfit,w_256,limit_0/format,jpg", 22 | "order_by": "last_access_at DESC,updated_at DESC,image_time DESC", 23 | "query": "(label = '身份证明' or label = '证件' or label = '身份证' or label = '银行卡' or label = '护照') and category in ['video','image'] and status = 'available' and hidden = false", 24 | "limit": 100, 25 | "drive_id": "9600002" 26 | } 27 | ``` 28 | 29 | ```json 30 | //最近图片 31 | { 32 | "return_total_count": true, 33 | "image_thumbnail_process": "image/resize,m_lfit,w_256,limit_0/format,jpg", 34 | "order_by": "last_access_at DESC,updated_at DESC,image_time DESC", 35 | "query": "category = 'image' and status = 'available' and hidden = false", 36 | "limit": 100, 37 | "drive_id": "9600002" 38 | } 39 | ``` 40 | 41 | ```json 42 | //最近视频 43 | { 44 | "return_total_count": true, 45 | "image_thumbnail_process": "image/resize,m_lfit,w_256,limit_0/format,jpg", 46 | "order_by": "last_access_at DESC,updated_at DESC,image_time DESC", 47 | "query": "category = 'video' and status = 'available' and hidden = false", 48 | "limit": 100, 49 | "drive_id": "9600002" 50 | } 51 | ``` 52 | 53 | Response: 54 | 55 | ```json 56 | filelist 57 | ``` 58 | 59 | #### 列出人物(face)的图片 60 | 61 | POST: `https://api.aliyundrive.com/v2/file/search` 62 | 63 | ```json 64 | { 65 | "drive_id": "9600002", 66 | "limit": 100, 67 | "order_by": "created_at DESC", 68 | "query": "type = 'file' and category in ['image', 'video'] and face_group_id = 'Group-00000000-1703-4fc0-bf56-369478ed14df' and status = 'available' and hidden = false", 69 | "return_total_count": true 70 | } 71 | ``` 72 | 73 | Response: 74 | 75 | ```json 76 | filelist 77 | ``` 78 | -------------------------------------------------------------------------------- /adrive sdk/sfiia.md: -------------------------------------------------------------------------------- 1 | #### 文件中的图片 2 | 3 | POST: `https://api.aliyundrive.com/adrive/v1/sfiia/get_recommends` 4 | 5 | ```json 6 | { "drive_id": "9600002" } 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "items": [ 14 | { 15 | "category": "image", 16 | "content_hash": "4DBF0000000023E6E756C29AF6AC487217921D53", 17 | "content_hash_name": "sha1", 18 | "content_type": "application/oct-stream", 19 | "crc64_hash": "1548000000008183211", 20 | "created_at": "2021-10-16T02:10:51.625Z", 21 | "domain_id": "bj29", 22 | "download_url": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...", 23 | "drive_id": "9600002", 24 | "encrypt_mode": "none", 25 | "file_extension": "png", 26 | "file_id": "623b00000000d89ef21d4118838aed83de7575ba", 27 | "hidden": false, 28 | "labels": ["衣服", "外貌特征", "其他事物", "艺术品", "笑脸", "墨镜", "护目镜", "微笑", "颜色", "动画", "黄色"], 29 | "mime_type": "image/png", 30 | "name": "cool_11.png", 31 | "parent_file_id": "613800000000336ae9164455b135a9729a298c9c", 32 | "punish_flag": 0, 33 | "size": 80480, 34 | "starred": false, 35 | "status": "available", 36 | "thumbnail": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...", 37 | "type": "file", 38 | "updated_at": "2021-10-16T02:10:51.625Z", 39 | "upload_id": "ED12000000004724833D47B5D5D3C8B9", 40 | "url": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F..." 41 | } 42 | ], 43 | "total_image_count": 28943 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /adrive sdk/timeline.md: -------------------------------------------------------------------------------- 1 | #### 用户信息 2 | 3 | POST: `https://api.aliyundrive.com/adrive/v1/timeline/user/get` 4 | 5 | ```json 6 | { "user_id": "9400000000bc480bbcbbb1e074f55a7f" } 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "description": "", 14 | "avatar": "https://ccp-bj29-bj-1592982087.oss-cn-beijing.aliyuncs.com/2GhCur3G%2F...", 15 | "user_id": "9400000000bc480bbcbbb1e074f55a7f", 16 | "nick_name": "myname", 17 | "phone": "151***111", 18 | "is_following": false, 19 | "follower_count": 0, 20 | "homepage_visibility": 1, 21 | "latest_messages": [], 22 | "homepage_visible_time_range_text": "三个月", 23 | "homepage_visible_time_range_in_millis": 7776000000 24 | } 25 | ``` 26 | 27 | #### 用户发布的动态 28 | 29 | POST: `https://api.aliyundrive.com/adrive/v1/timeline/homepage/list_message` 30 | 31 | ```json 32 | { "order_direction": "DESC", "user_id": "9400000000bc480bbcbbb1e074f55a7f", "limit": 10 } 33 | ``` 34 | 35 | Response: 36 | 37 | ```json 38 | { "items": [], "pin_items": [] } 39 | ``` 40 | 41 | #### 推荐订阅 42 | 43 | POST: `https://api.aliyundrive.com/adrive/v1/timeline/user/recommend` 44 | 45 | ```json 46 | { "user_id": "9400000000bc480bbcbbb1e074f55a7f", "limit": 20, "order_by": "updated_at", "order_direction": "DESC" } 47 | ``` 48 | 49 | Response: 50 | 51 | ```json 52 | { 53 | "items": [ 54 | { 55 | "description": "Hi~小可爱!感恩关注~盘盘酱会不定时发放福利哦!让盘酱陪伴你更久✧( •˓◞•̀ ) ", 56 | "avatar": "https://ccp-bj29-bj-1592982087.oss-cn-beijing.aliyuncs.com/2GhCur3G%2F...", 57 | "user_id": "9400000000bc480bbcbbb1e074f55a7f", 58 | "nick_name": "阿里盘盘酱", 59 | "phone": "131***325", 60 | "is_following": true 61 | }, 62 | { 63 | "description": "中国国家地理景观官方账号,带你领略目酣神醉的壮美景观、发现中国各地独具特色的人文胜迹。", 64 | "avatar": "https://ccp-bj29-bj-1592982087.oss-cn-beijing.aliyuncs.com/2GhCur3G%2F...", 65 | "user_id": "9400000000bc480bbcbbb1e074f55a7f", 66 | "nick_name": "中国国家地理景观", 67 | "phone": "136***902", 68 | "is_following": true 69 | } 70 | ], 71 | "next_marker": "MjA=" 72 | } 73 | ``` 74 | -------------------------------------------------------------------------------- /adrive sdk/token.md: -------------------------------------------------------------------------------- 1 | #### 网页版登录 2 | 3 | POST: `https://api.aliyundrive.com/token/get` 4 | 5 | ```json 6 | { "code": "f98788cef51641728f2aad9c64a96a63", "loginType": "normal", "deviceId": "CPH800000000AbfFPI5QSJjO" } 7 | ``` 8 | 9 | Response: 10 | 11 | ```json 12 | { 13 | "default_sbox_drive_id": "9600002", 14 | "role": "user", 15 | "user_name": "151***111", 16 | "need_link": false, 17 | "expire_time": "2022-03-21T09:48:46Z", 18 | "pin_setup": true, 19 | "need_rp_verify": false, 20 | "avatar": "https://ccp-bj29-bj-1592982087.oss-cn-beijing.aliyuncs.com/2GhCur3G%2F...", 21 | "user_data": { 22 | "DingDingRobotUrl": "https://oapi.dingtalk.com/robot/send?access_token=0b4a00000000c08608cd99f693393c18fa905aa0868215485a28497501916fec", 23 | "EncourageDesc": "内测期间有效反馈前10名用户将获得终身免费会员", 24 | "FeedBackSwitch": true, 25 | "FollowingDesc": "34848372", 26 | "ding_ding_robot_url": "https://oapi.dingtalk.com/robot/send?access_token=0b4a00000000c08608cd99f693393c18fa905aa0868215485a28497501916fec", 27 | "encourage_desc": "内测期间有效反馈前10名用户将获得终身免费会员", 28 | "feed_back_switch": true, 29 | "following_desc": "34848372" 30 | }, 31 | "token_type": "Bearer", 32 | "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9......aixJ4k", 33 | "default_drive_id": "9600002", 34 | "domain_id": "bj29", 35 | "refresh_token": "82ad000000004fbda61b01b5a5cf103b", 36 | "is_first_login": false, 37 | "user_id": "9400000000bc480bbcbbb1e074f55a7f", 38 | "nick_name": "mynane", 39 | "exist_link": [], 40 | "state": "", 41 | "expires_in": 7200, 42 | "status": "enabled" 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /electron-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "com.xby.app", 3 | "productName": "小白羊云盘", 4 | "copyright": "copyright ©2023 Zhangmin gao", 5 | "asar": true, 6 | "compression": "maximum", 7 | "directories": { "output": "release" }, 8 | "files": [ "dist" ], 9 | "extraResources": [ 10 | { "from": "./static/engine/aria2.conf", "to": "./engine/aria2.conf"}, 11 | { "from": "./static/crx", "to": "./crx"}, 12 | { "from": "./public/images/qrcode_1280.jpg", "to": "./images/qrcode_1280.jpg"}, 13 | { "from": "./static/images/icon_24.png", "to": "./images/icon_24.png"}, 14 | { "from": "./static/images/icon_64.png", "to": "./images/icon_64.png"}, 15 | { "from": "./static/images/icon_256.png", "to": "./images/icon_256.png"} 16 | ], 17 | "mac": { 18 | "icon": "./static/images/icon.icns", 19 | "type": "distribution", 20 | "artifactName": "XBYDriver-${version}-mac-${arch}.${ext}", 21 | "darkModeSupport": true, 22 | "hardenedRuntime": true, 23 | "category": "public.app-category.utilities", 24 | "extraResources": [ 25 | { "from": "./static/engine/darwin/${arch}", "to": "./engine" }, 26 | { "from": "./static/images/icon.icns", "to": "./images/icon.icns"} 27 | ], 28 | "target": [ 29 | { "target": "dmg", "arch": [ "x64", "arm64" ] } 30 | ] 31 | }, 32 | "linux": { 33 | "icon": "./static/images/icon_256.png", 34 | "category": "Network", 35 | "artifactName": "XBYDriver-${version}-linux-${arch}.${ext}", 36 | "extraResources": [ 37 | { "from": "./static/engine/linux/${arch}", "to": "./engine"} 38 | ], 39 | "target": [ 40 | { "target": "AppImage", "arch": [ "x64", "arm64", "armv7l" ] }, 41 | { "target": "deb", "arch": [ "x64", "arm64", "armv7l" ] } 42 | ] 43 | }, 44 | "win": { 45 | "icon": "./static/images/icon_256.ico", 46 | "artifactName": "XBYDriver-${version}-win-${arch}.${ext}", 47 | "requestedExecutionLevel": "asInvoker", 48 | "extraResources": [ 49 | { "from": "./static/engine/win32/${arch}", "to": "./engine"}, 50 | { "from": "./static/images/icon_256.ico", "to": "./images/icon_256.ico"} 51 | ], 52 | "target": [ 53 | { "target": "nsis", "arch": [ "x64", "ia32" ] } 54 | ] 55 | }, 56 | "dmg": { 57 | "window": { "width": 540, "height": 380 }, 58 | "contents": [ 59 | { "x": 410, "y": 230, "type": "link", "path": "/Applications" }, 60 | { "x": 130, "y": 230, "type": "file" } 61 | ] 62 | }, 63 | "nsis": { 64 | "oneClick": false, 65 | "perMachine": true, 66 | "allowElevation": true, 67 | "allowToChangeInstallationDirectory": true, 68 | "deleteAppDataOnUninstall": false 69 | }, 70 | "publish": [ 71 | { 72 | "provider": "github", 73 | "releaseType": "draft" 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /electron/main/mainfile.ts: -------------------------------------------------------------------------------- 1 | import { app } from 'electron' 2 | import path from 'path' 3 | import { copyFileSync, existsSync, rmSync } from 'fs' 4 | import is from 'electron-is' 5 | 6 | const DEBUGGING = !app.isPackaged 7 | 8 | let NewCopyed = false 9 | let NewSaved = false 10 | 11 | export function getAsarPath(fileName: string) { 12 | if (DEBUGGING) { 13 | const basePath = path.resolve(app.getAppPath()) 14 | return path.join(basePath, fileName) 15 | } else { 16 | const basePath = path.resolve(app.getAppPath()) 17 | const baseNew = path.join(basePath, '..', 'app.new') 18 | const baseSave = path.join(basePath, '..', 'app.asar') 19 | if (!NewCopyed) { 20 | // 热更新asar 21 | if (existsSync(baseNew)) { 22 | try { 23 | console.log('copyFileSync', baseNew, '-->', baseSave) 24 | copyFileSync(baseNew, baseSave) 25 | rmSync(baseNew, { force: true }) 26 | NewCopyed = true 27 | } catch (err: any) { 28 | console.log(err) 29 | } 30 | } 31 | } 32 | if (!NewSaved) NewSaved = existsSync(baseSave) 33 | if (NewSaved) return path.join(baseSave, fileName) 34 | return path.join(basePath, fileName) 35 | } 36 | } 37 | 38 | export function getResourcesPath(fileName: string) { 39 | let basePath = path.resolve(app.getAppPath(), '..') 40 | if (DEBUGGING) basePath = path.resolve(app.getAppPath(), '.') 41 | return path.join(basePath, fileName) 42 | } 43 | 44 | export function getStaticPath(fileName: string) { 45 | let basePath = path.resolve(app.getAppPath(), '..') 46 | if (DEBUGGING) basePath = path.resolve(app.getAppPath(), './static') 47 | if (fileName.startsWith('icon')) { 48 | if (fileName == 'icon_256.ico' && !is.windows()) { 49 | fileName = path.join('images', 'icon_24.png') 50 | } else { 51 | fileName = path.join('images', fileName) 52 | } 53 | } 54 | return path.join(basePath, fileName) 55 | } 56 | 57 | export function getUserDataPath(fileName: string) { 58 | return path.join(app.getPath('userData'), fileName) 59 | } 60 | -------------------------------------------------------------------------------- /electron/main/utils/index.ts: -------------------------------------------------------------------------------- 1 | import net from 'net' 2 | 3 | export function portIsOccupied(port: number) { 4 | 5 | const server = net.createServer().listen(port, '0.0.0.0') 6 | 7 | return new Promise((resolve, reject) => { 8 | 9 | server.on('listening', () => { 10 | console.log(`the server is runnint on port ${port}`) 11 | server.close() 12 | resolve(port) // 返回可用端口 13 | }) 14 | 15 | server.on('error', (err: any) => { 16 | if (err.code === 'EADDRINUSE') { 17 | resolve(portIsOccupied(port + 1)) // 如传入端口号被占用则 +1 18 | console.log(`this port ${port} is occupied.try another.`) 19 | } else { 20 | console.log(err) 21 | // reject(err) 22 | resolve(port) 23 | } 24 | }) 25 | 26 | }) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /electron/preload/preload-env.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | declare namespace NodeJS { 3 | interface ProcessEnv { 4 | NODE_ENV: 'development' | 'production' 5 | readonly VITE_DEV_SERVER_HOST: string 6 | readonly VITE_DEV_SERVER_PORT: string 7 | } 8 | } 9 | declare interface Window { 10 | Electron: any 11 | platform: any 12 | WinMsg: any 13 | WebToElectron: any 14 | WebToElectronCB: any 15 | WebSpawnSync: any 16 | WebExecSync: any 17 | WebShowOpenDialogSync: any 18 | WebShowSaveDialogSync: any 19 | WebShowItemInFolder: any 20 | WebPlatformSync: any 21 | WebClearCookies: any 22 | WebClearCache: any 23 | WebUserToken: any 24 | WebSaveTheme: any 25 | WebReload: any 26 | WebRelaunch: any 27 | WebRelaunchAria: () => Promise 28 | WebSetProgressBar: any 29 | WebSetCookies: any 30 | WebOpenWindow: any 31 | WebOpenUrl: any 32 | WebShutDown: any 33 | WebSetProxy: any 34 | } 35 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 小白羊云盘 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 |
28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /nano-staged.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | // eslint 3 | '*.{js,ts,tsx,jsx}': ['prettier --write', 'eslint --cache --fix'], 4 | '*.{vue}': ['stylelint --fix', 'prettier --write', 'eslint --cache --fix'], 5 | '*.{less,css}': ['stylelint --fix', 'prettier --write'], 6 | // typecheck 7 | 'packages/renderer/**/{*.ts,*.tsx,*.vue,tsconfig.json}': ({ filenames }) => 'npm run typecheck' 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xbyyunpan", 3 | "description": "小白羊云盘", 4 | "version": "3.11.15", 5 | "license": "MIT", 6 | "main": "dist/electron/main/index.js", 7 | "author": { 8 | "name": "gaozhangmin", 9 | "email": "gaozhangmin@gmail.com" 10 | }, 11 | "scripts": { 12 | "dev": "vite", 13 | "build": "vue-tsc --noEmit && vite build && electron-builder -wml" 14 | }, 15 | "engines": { 16 | "node": ">=16.0.0" 17 | }, 18 | "dependencies": {}, 19 | "devDependencies": { 20 | "@arco-design/web-vue": "^2.45.3", 21 | "@electron/remote": "^2.0.9", 22 | "@types/crypto-js": "^4.1.1", 23 | "@types/fast-levenshtein": "^0.0.2", 24 | "@types/howler": "^2.2.7", 25 | "@types/lodash": "^4.14.184", 26 | "@types/node": "^17.0.45", 27 | "@types/secp256k1": "^4.0.3", 28 | "@types/uuid": "^9.0.1", 29 | "@vitejs/plugin-vue": "^3.1.0", 30 | "@vitejs/plugin-vue-jsx": "^2.0.1", 31 | "ant-design-vue": "^3.2.20", 32 | "aria2-lib": "1.0.1", 33 | "artplayer": "^5.0.9", 34 | "axios": "^1.4.0", 35 | "consola": "^3.1.0", 36 | "crypto-js": "^4.1.1", 37 | "dayjs": "^1.11.7", 38 | "dexie": "^3.2.3", 39 | "dom-to-image": "^2.6.0", 40 | "electron": "^21.4.4", 41 | "electron-builder": "^23.6.0", 42 | "electron-is": "^3.0.0", 43 | "electron-log": "^4.4.8", 44 | "fast-levenshtein": "^3.0.0", 45 | "fix-path": "^4.0.0", 46 | "fuzzysort": "^2.0.1", 47 | "hls.js": "^1.4.3", 48 | "howler": "^2.2.3", 49 | "isomorphic-fetch": "^3.0.0", 50 | "jschardet": "^3.0.0", 51 | "lodash": "^4.17.21", 52 | "pinia": "^2.0.35", 53 | "secp256k1": "^5.0.0", 54 | "socks-proxy-agent": "^7.0.0", 55 | "sudo-prompt": "^9.2.1", 56 | "terser": "^5.15.0", 57 | "typescript": "^4.8.2", 58 | "uuid": "^9.0.0", 59 | "uuid-by-string": "^4.0.0", 60 | "viewerjs": "^1.10.5", 61 | "vite": "^3.1.0", 62 | "vite-plugin-electron": "^0.9.2", 63 | "vite-plugin-resolve": "^2.1.2", 64 | "vue": "^3.2.47", 65 | "vue-tsc": "^1.6.4" 66 | }, 67 | "debug": { 68 | "env": { 69 | "VITE_DEV_SERVER_HOSTNAME": "127.0.0.1", 70 | "VITE_DEV_SERVER_PORT": 3344, 71 | "VITE_DEV_SERVER_URL": "http://127.0.0.1:3344" 72 | } 73 | }, 74 | "keywords": [ 75 | "aliyunpan", 76 | "electron", 77 | "rollup", 78 | "vite", 79 | "vue3", 80 | "vue" 81 | ] 82 | } -------------------------------------------------------------------------------- /public/audio/down_finished.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/audio/down_finished.mp3 -------------------------------------------------------------------------------- /public/audio/upload_finished.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/audio/upload_finished.mp3 -------------------------------------------------------------------------------- /public/font/VideoJS.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/font/VideoJS.woff -------------------------------------------------------------------------------- /public/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/iconfont.woff2 -------------------------------------------------------------------------------- /public/images/dark_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/images/dark_button.png -------------------------------------------------------------------------------- /public/images/follow_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/images/follow_button.png -------------------------------------------------------------------------------- /public/images/light_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/images/light_button.png -------------------------------------------------------------------------------- /public/images/qrcode_1280.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/images/qrcode_1280.jpg -------------------------------------------------------------------------------- /public/images/qrcode_258.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/images/qrcode_258.jpg -------------------------------------------------------------------------------- /public/images/wechat_award.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/images/wechat_award.jpeg -------------------------------------------------------------------------------- /public/imgerror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/imgerror.png -------------------------------------------------------------------------------- /public/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/loading.png -------------------------------------------------------------------------------- /public/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 小白羊云盘 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/main2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 小白羊云盘 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/notify.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/notify.wav -------------------------------------------------------------------------------- /public/thumbup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 14 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /public/userface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/userface.png -------------------------------------------------------------------------------- /public/wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/public/wasm.wasm -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/aliapi/batch.ts: -------------------------------------------------------------------------------- 1 | import message from '../utils/message' 2 | 3 | 4 | export async function RunBatch(title: string, list: any[], max: number, func: (t: any) => Promise) { 5 | const loadingKey = 'runbatch' + Date.now().toString() + '_' + title 6 | if (title) message.success('正在执行' + title + '( 0 / ' + list.length + ' )', 0, loadingKey) 7 | let parr: Promise[] = [] 8 | for (let i = 0, maxi = list.length; i < maxi; i++) { 9 | parr.push(func(list[i])) 10 | if (parr.length == max) { 11 | await Promise.all(parr) 12 | .catch(() => {}) 13 | .then(() => { 14 | parr = [] 15 | }) 16 | if (title) message.success('正在执行' + title + '( ' + i + ' / ' + maxi + ' )', 0, loadingKey) 17 | } 18 | } 19 | if (parr.length > 0) 20 | await Promise.all(parr) 21 | .catch(() => {}) 22 | .then(() => { 23 | parr = [] 24 | }) 25 | 26 | if (title) message.success('成功执行' + title, 1, loadingKey) 27 | } 28 | -------------------------------------------------------------------------------- /src/aliapi/fileicon.ts: -------------------------------------------------------------------------------- 1 | export default function getFileIcon(category: string | undefined, ext: string | undefined, mimext: string | undefined, mime: string | undefined, size: number): string[] { 2 | if (!ext) ext = '' 3 | if (!mime) mime = '' 4 | if (!mimext) mimext = '' 5 | if (!category) category = 'others' 6 | 7 | /** 8 | * 1、图片支持以下格式:JPEG、BMP、PNG、JPG 9 | * 2、视频文件支持以下格式:MP4、3GP、AVI、FLV、Webm、MOV、AMR、ASF、VCD(MPEG-1 video)、DVD(MPEG-2)、M4V、3G2、MJPEG、DATA、AVI(H261,H263,H264)、DV、GXF、CAVS video、DNxHD、FFM 10 | * 3、音频文件支持以下格式:MP3、FLAC、AC3、Ogg、ADX、WAV、AIFF、ALAW、AU、DTS、MP2、Dirac、HLS 11 | * 4、文档/文本文件支持以下格式:PDF、WORD、TXT、PPT、EXCEL 12 | */ 13 | 14 | ext = '.' + ext.toLowerCase().replace('.', '').trim() + '.' 15 | mimext = '.' + mimext.toLowerCase().replace('.', '').trim() + '.' 16 | 17 | switch (ext) { 18 | case '.txt.': 19 | return ['doc', 'iconfile-txt'] 20 | case '.rar.': 21 | return ['zip', 'iconfile-rar'] 22 | case '.rtf.': 23 | return ['doc', 'iconfile-doc'] 24 | case '.psd.': 25 | return ['others', 'iconfile-psd'] 26 | case '.torrent.': 27 | return ['others', 'iconfile-bt'] 28 | case '.iso.': 29 | return ['others', 'iconfile-iso'] 30 | case '.exe.': 31 | return ['others', 'iconfile-exe'] 32 | case '.apk.': 33 | return ['others', 'iconfile-apk'] 34 | case '.tar.': 35 | return ['others', 'iconfile-tar'] 36 | case '.7z.': 37 | return ['others', 'iconfile-7z'] 38 | case '.svg.': 39 | return ['image3', 'iconfile-image'] 40 | case '.azw.': 41 | return ['doc', 'iconwenjian'] 42 | case '.azw3.': 43 | return ['doc', 'iconwenjian'] 44 | case '.epub.': 45 | return ['doc', 'iconwenjian'] 46 | } 47 | 48 | if (category == 'zip' || mimext == '.zip.') { 49 | 50 | return ['zip', 'iconfile-zip'] 51 | } 52 | 53 | 54 | if (';.apng.avif.ico.webp.gif.'.indexOf(ext) > 0) { 55 | return ['image2', 'iconfile-img'] 56 | } 57 | 58 | if (category == 'image') { 59 | return ['image', 'iconfile-img'] 60 | } 61 | 62 | if (mime.startsWith('image/')) return ['image3', 'iconfile-image'] 63 | if (ext == '.pdf.' || mimext == '.pdf.') return ['doc', 'iconfile-pdf'] 64 | 65 | if (';.doc.docm.docx.dot.dotm.dotx.wps.wpt.'.indexOf(ext) > 0) return ['doc', 'iconfile-doc'] 66 | if (';.pot.ett.'.indexOf(ext) > 0) return ['doc2', 'iconfile-doc'] 67 | if ((mimext.startsWith('.txt') || mimext.startsWith('.doc') || mimext.startsWith('.ppt')) && ';.dps.dpt.potm.potx.pps.ppsm.ppsx.ppt.pptm.pptx.'.indexOf(ext) > 0) return ['doc', 'iconfile-ppt'] 68 | if ((mimext.startsWith('.txt') || mimext.startsWith('.xls')) && ';.xls.xlsx.et.xlsm.xlt.xltm.xltx.'.indexOf(ext) > 0) return ['doc', 'iconfile-xsl'] 69 | 70 | if (mime.startsWith('text/')) return ['others', 'iconfile_txt2'] 71 | if (ext == '.json.') return ['others', 'iconfile_txt2'] 72 | 73 | if (category == 'video') { 74 | 75 | return ['video', 'iconfile_video'] 76 | } 77 | if (mime.startsWith('video/')) return ['video2', 'iconfile_video'] 78 | if (ext == '.ts.' && size > 5 * 1024 * 1024) return ['video2', 'iconfile_video'] 79 | if (';.3iv.cpk.divx.hdv.fli.f4v.f4p.m2t.m2ts.mts.trp.mkv.mp4.mpg4.nsv.nut.nuv.rm.rmvb.vob.wmv.mk3d.hevc.yuv.y4m.mov.avi.flv.mpg.3gp.m4v.mpeg.asf.wmz.webm.pmp.mpga'.indexOf(ext) > 0) { 80 | return ['video2', 'iconfile_video'] 81 | } 82 | if (ext == '.mp3.' && category == 'audio') return ['audio', 'iconfile-mp3'] 83 | if (category == 'audio' && mimext != '.unknown.') { 84 | 85 | return ['audio', 'iconfile-audio'] 86 | } 87 | if (mime.startsWith('audio/')) return ['audio', 'iconfile-audio'] 88 | if (';.ape.aac.cda.dsf.dtshd.eac3.m1a.m2a.m4a.mka.mpa.mpc.opus.ra.tak.tta.wma.wv.'.indexOf(ext) > 0) { 89 | return ['audio2', 'iconfile-audio'] 90 | } 91 | 92 | return ['others', 'iconwenjian'] 93 | } 94 | -------------------------------------------------------------------------------- /src/aliapi/filewalk.ts: -------------------------------------------------------------------------------- 1 | import DebugLog from '../utils/debuglog' 2 | import AliHttp, { IUrlRespData } from './alihttp' 3 | import { IAliFileItem } from './alimodels' 4 | import AliDirFileList, { IAliFileResp } from './dirfilelist' 5 | 6 | export default class AliFileWalk { 7 | static async ApiWalkFileList(user_id: string, drive_id: string, dirID: string, dirName: string, order: string, type: string = '', max: number = 3000): Promise { 8 | const dir: IAliFileResp = { 9 | items: [], 10 | itemsKey: new Set(), 11 | punished_file_count: 0, 12 | next_marker: '', 13 | m_user_id: user_id, 14 | m_drive_id: drive_id, 15 | dirID: dirID, 16 | dirName: dirName 17 | } 18 | 19 | if (!order) order = 'updated_at desc' 20 | const orders = order.split(' ') 21 | do { 22 | const isGet = await AliFileWalk._ApiWalkFileListOnePage(orders[0], orders[1], dir, type) 23 | if (isGet != true) { 24 | break 25 | } 26 | if (dir.items.length >= max && max > 0) { 27 | dir.next_marker = '' 28 | break 29 | } 30 | } while (dir.next_marker != '') 31 | return dir 32 | } 33 | 34 | private static async _ApiWalkFileListOnePage(orderby: string, order: string, dir: IAliFileResp, type: string = '') { 35 | const url = 'v2/file/walk?jsonmask=next_marker%2Cpunished_file_count%2Ctotal_count%2Citems(category%2Ccreated_at%2Cdomain_id%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription)' 36 | let postData = { 37 | drive_id: dir.m_drive_id, 38 | parent_file_id: dir.dirID, 39 | marker: dir.next_marker, 40 | limit: 1000, 41 | all: false, 42 | url_expire_sec: 14400, 43 | fields: 'thumbnail' 44 | // order_by: orderby, 45 | // order_direction: order.toUpperCase() 46 | } 47 | if (type) postData = Object.assign(postData, { type }) 48 | const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') 49 | return AliFileWalk._FileListOnePage(dir, resp) 50 | } 51 | 52 | private static _FileListOnePage(dir: IAliFileResp, resp: IUrlRespData) { 53 | try { 54 | if (AliHttp.IsSuccess(resp.code)) { 55 | dir.next_marker = resp.body.next_marker 56 | const isRecover = dir.dirID == 'recover' 57 | const downUrl = isRecover ? '' : 'https://api.aliyundrive.com/v2/file/download?t=' + Date.now().toString() 58 | 59 | for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { 60 | const item = resp.body.items[i] as IAliFileItem 61 | if (dir.itemsKey.has(item.file_id)) continue 62 | const add = AliDirFileList.getFileInfo(item, downUrl) 63 | if (isRecover) add.description = item.content_hash 64 | dir.items.push(add) 65 | dir.itemsKey.add(item.file_id) 66 | } 67 | dir.punished_file_count += resp.body.punished_file_count || 0 68 | 69 | return true 70 | } else if (resp.code == 404) { 71 | 72 | dir.items.length = 0 73 | dir.next_marker = '' 74 | return true 75 | } else if (resp.body && resp.body.code) { 76 | dir.items.length = 0 77 | dir.next_marker = resp.body.code 78 | // message.warning('列出文件出错 ' + resp.body.code, 2) 79 | return false 80 | } else { 81 | DebugLog.mSaveWarning('_FileListOnePage err=' + (resp.code || '')) 82 | } 83 | } catch (err: any) { 84 | DebugLog.mSaveDanger('_FileListOnePage ' + dir.dirID, err) 85 | } 86 | dir.next_marker = 'error ' + resp.code 87 | console.log(resp) 88 | return false 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/aliapi/uploadmem.ts: -------------------------------------------------------------------------------- 1 | import UserDAL from '../user/userdal' 2 | import DebugLog from '../utils/debuglog' 3 | import axios from 'axios' 4 | import AliUpload from './upload' 5 | import AliUploadHashPool from './uploadhashpool' 6 | import AliUploadOpenApi from "./uploadOpenApi"; 7 | 8 | export default class AliUploadMem { 9 | 10 | static async UploadMem(user_id: string, drive_id: string, parent_file_id: string, CreatFileName: string, context: string) { 11 | const token = await UserDAL.GetUserTokenFromDB(user_id) 12 | if (!token || !token.access_token || !token.access_token_v2) return '账号失效,操作取消' 13 | let hash = 'DA39A3EE5E6B4B0D3255BFEF95601890AFD80709' 14 | let proof = '' 15 | let buff = Buffer.from([]) 16 | if (context.length > 0) { 17 | buff = Buffer.from(context, 'utf-8') 18 | const dd = await AliUploadHashPool.GetBuffHashProof(token!.access_token_v2, buff) 19 | hash = dd.sha1 20 | proof = dd.proof_code 21 | } 22 | const size = buff.length 23 | 24 | const upinfo = await AliUploadOpenApi.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, CreatFileName, size, hash, proof, 'refuse') 25 | if (upinfo.errormsg != '') { 26 | return upinfo.errormsg 27 | } 28 | if (upinfo.isexist) return '网盘中已存在同名文件' 29 | if (upinfo.israpid) return 'success' 30 | 31 | await axios 32 | .put(upinfo.part_info_list[0].upload_url, buff, { 33 | responseType: 'text', 34 | timeout: 30000, 35 | headers: { 36 | 37 | 'Content-Type': '', 38 | Authorization: token!.token_type + ' ' + token!.access_token 39 | } 40 | }) 41 | .catch(function (err: any) { 42 | DebugLog.mSaveDanger('UploadMemError', err) 43 | }) 44 | const result = await AliUploadOpenApi.UploadFileComplete(user_id, drive_id, upinfo.file_id, upinfo.upload_id, size, hash) 45 | if (result) return 'success' 46 | else return '合并文件失败' 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/contentview.css: -------------------------------------------------------------------------------- 1 | div.cnav { 2 | height: 100px; 3 | position: fixed; 4 | width: 100%; 5 | 6 | /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#000000+0,000000+24,000000+100&0.37+0,0.29+24,0+100 */ 7 | background: -moz-linear-gradient(top, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.2) 24%, rgba(0,0,0,0) 100%); /* FF3.6-15 */ 8 | background: -webkit-linear-gradient(top, rgba(0,0,0,0.3) 0%,rgba(0,0,0,0.2) 24%,rgba(0,0,0,0) 100%); /* Chrome10-25,Safari5.1-6 */ 9 | background: linear-gradient(to bottom, rgba(0,0,0,0.3) 0%,rgba(0,0,0,0.2) 24%,rgba(0,0,0,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ 10 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#5e000000', endColorstr='#00000000',GradientType=0 ); /* IE6-9 */ 11 | 12 | z-index: 999; 13 | } 14 | 15 | div.cnav .title { 16 | position: absolute; 17 | top: 50px; 18 | } 19 | 20 | div.cnav .title.left { 21 | left: 20px; 22 | } 23 | 24 | div.cnav .title.right { 25 | position: fixed; 26 | right: 20px; 27 | } 28 | 29 | div.cnav .title-text { 30 | color: #fff; 31 | font-size: 22px; 32 | font-weight: bold; 33 | } 34 | 35 | .title-text { 36 | margin-top: 10px; 37 | margin-left: 10px; 38 | font-size: 22px; 39 | font-weight: bold; 40 | color: var(--color-text-1); 41 | } 42 | 43 | div.cnav button{ 44 | border: none; 45 | border-radius: 20px; 46 | padding: 5px 15px 5px 15px; 47 | background: rgba(180,180,180,0.5); 48 | color: white; 49 | border-bottom: 1px solid #ddd; 50 | backdrop-filter: blur(5px); 51 | -webkit-backdrop-filter: blur(5px); 52 | outline: none; 53 | cursor: pointer; 54 | font-size: 15px; 55 | } 56 | 57 | div.cnav button:active { 58 | background: rgba(80,80,80,0.5); 59 | } 60 | 61 | div.cnav .back { 62 | display: none; 63 | } 64 | 65 | div.cnav .back .backtext { 66 | margin-left: 2px; 67 | color: white; 68 | font-size: 16px; 69 | cursor: pointer; 70 | } 71 | 72 | div.cnav .back .backtext:active { 73 | color: #aaaaff; 74 | } 75 | 76 | div.cnav .back.sidebar-hidden { 77 | position: absolute; 78 | top: 20px; 79 | left: 20px; 80 | display: block; 81 | } 82 | div.cnav .title.left.sidebar-hidden { 83 | top: 45px; 84 | left: 15px; 85 | } 86 | 87 | /* Photos */ 88 | .box { 89 | position: relative; 90 | width: 20%; /* desired width */ 91 | } 92 | .box:before { 93 | content: ""; 94 | display: block; 95 | padding-top: 100%; /* initial ratio of 1:1*/ 96 | } 97 | .content { 98 | position: absolute; 99 | top: 0; 100 | left: 0; 101 | bottom: 0; 102 | right: 0; 103 | } 104 | .ratio2_1:before{ 105 | padding-top: 50%; 106 | } 107 | .ratio1_2:before{ 108 | padding-top: 200%; 109 | } 110 | .ratio4_3:before{ 111 | padding-top: 75%; 112 | } 113 | .ratio16_9:before{ 114 | padding-top: 56.25%; 115 | } 116 | 117 | div.photo { 118 | display: inline-block; 119 | margin-top: -5px; 120 | background-color: #eee; 121 | background-size:cover; 122 | } 123 | 124 | 125 | @media screen and (max-width: 1100px) { 126 | div.cnav .back { 127 | position: absolute; 128 | top: 20px; 129 | left: 20px; 130 | display: block; 131 | } 132 | 133 | div.cnav .title.left { 134 | top: 45px; 135 | left: 15px; 136 | } 137 | 138 | .sidebar-hidden-left { 139 | display: none !important; 140 | } 141 | } 142 | 143 | @media screen and (max-width: 960px) { 144 | .box { 145 | position: relative; 146 | width: 25%; /* desired width */ 147 | } 148 | } 149 | 150 | @media screen and (max-width: 720px) { 151 | .box { 152 | position: relative; 153 | width: 33%; /* desired width */ 154 | } 155 | } 156 | 157 | @media screen and (max-width: 480px) { 158 | .box { 159 | position: relative; 160 | width: 50%; /* desired width */ 161 | } 162 | } 163 | 164 | -------------------------------------------------------------------------------- /src/assets/preview.css: -------------------------------------------------------------------------------- 1 | .preview-with-navbar { 2 | background-color: white; 3 | } 4 | 5 | .preview-hidden-navbar { 6 | background: #111; 7 | } 8 | 9 | div.nav .backtext { 10 | margin-left: 2px; 11 | color: white; 12 | font-size: 16px; 13 | cursor: pointer; 14 | } 15 | 16 | .backtext:active { 17 | color: #aaaaff; 18 | } 19 | 20 | .preview-photo-base { 21 | position: absolute; 22 | top: 0; 23 | bottom: 0; 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | .preview-photo-high-res { 29 | position: absolute; 30 | top: 0; 31 | bottom: 0; 32 | width: 100%; 33 | height: 100%; 34 | } 35 | 36 | .preview-bg { 37 | background-repeat: no-repeat; 38 | background-position: center; 39 | } 40 | 41 | .preview-mask { 42 | position: absolute; 43 | top: 0; 44 | bottom: 0; 45 | width: 100%; 46 | height: 100%; 47 | } -------------------------------------------------------------------------------- /src/assets/sidebar.css: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 1100px) { 2 | .hidden-btn { 3 | display: none; 4 | } 5 | } 6 | 7 | .list_img { 8 | position: absolute; 9 | top:8px; 10 | left: -6px; 11 | background-color: white; 12 | display: inline-block; 13 | width: 24px; 14 | height: 24px; 15 | border-radius: 3px; 16 | background-size: cover; 17 | } -------------------------------------------------------------------------------- /src/down/DownM3U8.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/down/DownSync.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/down/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | /// 3 | 4 | declare module '*.vue' { 5 | import { DefineComponent } from 'vue' 6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 7 | const component: DefineComponent<{ tabindex: string; id: string }, {}, any> 8 | export default component 9 | } 10 | 11 | declare enum TaskState { 12 | Success, // 已成功 13 | Error, // 出错停止 14 | Running, // 上传中 15 | Stoped, // 已暂停 16 | Waiting, // 排队中 17 | Autotry // 稍后自动重试 18 | } 19 | 20 | declare type CheckNameMode = 21 | | 'auto_rename' // auto_rename (自动换一个随机名称) 22 | | 'refuse' // refuse (不会创建,告诉你已经存在) 23 | | 'ignore' // ignore (会创建重名的) 24 | 25 | declare type FileType = 26 | | 'file' // 文件 27 | | 'folder' // 文件夹(目录) 28 | 29 | declare type UploadStates = 30 | | 'waiting' // 排队中, 等待上传 31 | | 'start' // 开始 32 | | 'computing_hash' // 计算hash,预秒传,秒传 33 | | 'created' // 创建成功 34 | | 'running' // 上传中 35 | | 'stopped' // 暂停 36 | | 'complete' // 上传完成 37 | | 'checking' // 校验中, 检查 crc64 是否一致 38 | | 'success' // 上传成功 39 | | 'rapid_success' // 秒传成功 40 | | 'error' // 上传失败 41 | | 'cancelled' // 已取消 42 | 43 | // DownloadState 没有 computing_hash & rapid_success 44 | declare type DownloadStates = 45 | | 'waiting' // 排队中, 等待下载 46 | | 'start' // 开始 47 | | 'created' // 创建成功 48 | | 'running' // 下载中 49 | | 'stopped' // 暂停 50 | | 'complete' // 下载完成 51 | | 'checking' // 校验中, 检查 crc64 是否一致 52 | | 'success' // 下载成功 53 | | 'error' // 下载失败 54 | | 'cancelled' // 已取消 55 | 56 | declare module 'Go' 57 | declare module 'dom-to-image' 58 | declare module 'jschardet' 59 | declare function pinyinlite(text: string, config: any): any 60 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | declare global { 4 | // eslint-disable-next-line no-unused-vars 5 | interface Window { 6 | Go: any 7 | require: any 8 | Electron: any 9 | openDatabase: any 10 | WebRelaunchAria: () => Promise 11 | platform: string 12 | WinMsg: any 13 | postdataFunc: any 14 | Prism: any 15 | WebUserToken: any 16 | WebToElectron: any 17 | WebClearCache: any 18 | WebRelaunch: any 19 | WebClearCookies: any 20 | WebShutDown: any 21 | WebOpenWindow: any 22 | WebOpenUrl: any 23 | WebShowOpenDialogSync: any 24 | WebExecSync: any 25 | WebSpawnSync: any 26 | WebPlatformSync: any 27 | UploadPort: any 28 | DownloadPort: any 29 | MainPort: any 30 | WinMsgToUpload: any 31 | WinMsgToDownload: any 32 | WinMsgToMain: any 33 | AutoLanuchAtStartup: any 34 | CheckUpdate: any 35 | IsMainPage: boolean 36 | WebSetProxy: any 37 | speedLimte: number 38 | WebSetProgressBar: any 39 | Aria2cDownloadSpeed: any 40 | Aria2cUploadSpeed: any 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/layout/MyLoading.vue: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /src/layout/MySplit.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 63 | 93 | -------------------------------------------------------------------------------- /src/layout/MySwitch.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 66 | -------------------------------------------------------------------------------- /src/layout/MyTags.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 48 | 78 | -------------------------------------------------------------------------------- /src/layout/PageHelp.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/layout/PageLoading.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 49 | -------------------------------------------------------------------------------- /src/layout/PageOffice.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/layout/PageWorker.vue: -------------------------------------------------------------------------------- 1 | 6 | 25 | 26 | -------------------------------------------------------------------------------- /src/pan/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/pan/menus/DirTopPath.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 42 | 112 | -------------------------------------------------------------------------------- /src/pan/menus/PanTopbtn.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 59 | 60 | -------------------------------------------------------------------------------- /src/pan/menus/TrashRightMenu.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 36 | 37 | -------------------------------------------------------------------------------- /src/pan/menus/TrashTopbtn.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 34 | 35 | -------------------------------------------------------------------------------- /src/pan/topbtns/AlphaModal.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 45 | 46 | 86 | -------------------------------------------------------------------------------- /src/pan/topbtns/ArchivePasswordModal.vue: -------------------------------------------------------------------------------- 1 | 107 | 108 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/pan/topbtns/CreatNewAlbumModal.vue: -------------------------------------------------------------------------------- 1 | 89 | 90 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/pan/topbtns/CreatNewDirMultiModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/pan/topbtns/DownloadModal.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/pan/topbtns/ShuXingMultiModal.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/pic/PicDAL.ts: -------------------------------------------------------------------------------- 1 | import DB from '../utils/db' 2 | import useUserStore from '../user/userstore' 3 | import AliAlbum from '../aliapi/album' 4 | import usePanTreeStore from '../pan/pantreestore' 5 | import { GetDriveID } from '../aliapi/utils' 6 | 7 | export default class PicDAL { 8 | static async aReLoadPicListToShow(drive_id: string, file_id: string) { 9 | const userId = useUserStore().user_id 10 | if (!drive_id) drive_id = GetDriveID(userId, 'pic') 11 | if (!drive_id) return false 12 | 13 | // if (file_id == 'refresh') file_id = pantreeStore.selectDir.file_id 14 | // const isBack = file_id == 'back' 15 | // if (isBack) { 16 | // if (pantreeStore.History.length > 0) { 17 | // pantreeStore.History.splice(0, 1) 18 | // if (pantreeStore.History.length > 0) { 19 | // drive_id = pantreeStore.History[0].drive_id 20 | // file_id = pantreeStore.History[0].file_id 21 | // } 22 | // } 23 | // pantreeStore.History = [] 24 | // file_id = 'root' 25 | // } 26 | // const picList = await AliAlbum.ApiAlbumFileList(userId, drive_id, file_id, 'name', 'DESC', 50) 27 | // console.log('picList', picList) 28 | return [] 29 | } 30 | } -------------------------------------------------------------------------------- /src/pic/PicLeft.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /src/pic/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 99 | 100 | -------------------------------------------------------------------------------- /src/rss/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 77 | 78 | 84 | -------------------------------------------------------------------------------- /src/rss/rssdrivecopy/drivecopy.ts: -------------------------------------------------------------------------------- 1 | import { IAliGetFileModel } from '../../aliapi/alimodels' 2 | import AliFile from '../../aliapi/file' 3 | import message from '../../utils/message' 4 | import { fileiconfn, foldericonfn } from '../ScanDAL' 5 | import AliTrash from '../../aliapi/trash' 6 | 7 | export interface ICopyTreeInfo { 8 | user_id: string 9 | drive_id: string 10 | driveType: string 11 | dirID: string 12 | dirName: string 13 | parentID: string 14 | loading: boolean 15 | onlyDir: boolean 16 | } 17 | export function NewCopyTreeInfo(onlyDir: boolean) { 18 | const info: ICopyTreeInfo = { 19 | user_id: '', 20 | driveType: '', 21 | drive_id: '', 22 | dirID: '', 23 | dirName: '', 24 | parentID: '', 25 | loading: false, 26 | onlyDir: onlyDir 27 | } 28 | return info 29 | } 30 | 31 | export interface ICopyTreeNode { 32 | key: string 33 | title: string 34 | icon: any 35 | download_url: string 36 | disabled: boolean 37 | children?: ICopyTreeNode[] 38 | } 39 | 40 | export async function LoadDir(dirID: string, DirData: ICopyTreeInfo, treeData: ICopyTreeNode[], disabledFile: boolean): Promise { 41 | DirData.loading = true 42 | if (!dirID) dirID = 'root' 43 | if (dirID.startsWith('dir_')) dirID = dirID.substring('dir_'.length) 44 | if (dirID == 'root') { 45 | DirData.dirID = 'root' 46 | DirData.dirName = '根目录' 47 | DirData.parentID = 'root' 48 | } else { 49 | const getDir = await AliFile.ApiFileInfoOpenApi(DirData.user_id, DirData.drive_id, dirID) 50 | if (getDir) { 51 | DirData.dirID = getDir.file_id 52 | DirData.dirName = getDir.name 53 | DirData.parentID = getDir.parent_file_id 54 | } else { 55 | message.error('读取文件夹信息失败') 56 | } 57 | } 58 | 59 | const resp = await AliTrash.ApiDirFileListNoLock(DirData.user_id, DirData.drive_id, dirID, '', '', '') 60 | DirData.loading = false 61 | const list: ICopyTreeNode[] = [] 62 | const items = resp.items 63 | let item: IAliGetFileModel 64 | for (let i = 0, maxi = items.length; i < maxi; i++) { 65 | item = items[i] 66 | list.push({ 67 | key: (item.isDir ? 'dir_' : 'file_') + item.file_id, 68 | title: item.name, 69 | disabled: item.isDir ? false : disabledFile, 70 | icon: item.isDir ? foldericonfn : fileiconfn, 71 | download_url: '' 72 | } as ICopyTreeNode) 73 | } 74 | treeData.splice(0, treeData.length, ...list) 75 | } -------------------------------------------------------------------------------- /src/rss/rssrename/RssRename.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 79 | 80 | 89 | -------------------------------------------------------------------------------- /src/rss/rssxima/xima.ts: -------------------------------------------------------------------------------- 1 | import { FileSystemErrorMessage } from '../../utils/filehelper' 2 | import DebugLog from '../../utils/debuglog' 3 | import message from '../../utils/message' 4 | import fsPromises from 'fs/promises' 5 | import { Buffer } from 'buffer' 6 | import path from 'path' 7 | 8 | export async function DoXiMa(dirPath: string, breakSmall: boolean, matchExtList: string[]): Promise { 9 | const fileList: string[] = [] 10 | await GetAllFiles(dirPath, breakSmall, fileList) 11 | if (fileList.length == 0) { 12 | message.error('选择的文件夹下找不到任何文件') 13 | return 0 14 | } else { 15 | let rand = Date.now() 16 | const rand1 = rand % 256 17 | rand = rand / 128 18 | const rand2 = Math.floor(rand % 256) 19 | let rand3 = Math.floor(Math.random() * 255) 20 | 21 | let runCount = 0 22 | for (let i = 0, maxi = fileList.length; i < maxi; i++) { 23 | const file = fileList[i].toLowerCase().trimEnd() 24 | if (matchExtList.length > 0) { 25 | 26 | let find = false 27 | for (let j = 0; j < matchExtList.length; j++) { 28 | if (file.endsWith(matchExtList[j])) { 29 | find = true 30 | break 31 | } 32 | } 33 | if (find == false) continue 34 | } 35 | try { 36 | const rand4 = (i % 255) + 1 37 | if (rand4 == 200) rand3 = Math.floor(Math.random() * 255) 38 | const buff = Buffer.from([0, rand1, rand2, rand3, rand4]) 39 | fsPromises.appendFile(fileList[i], buff).catch(() => {}) 40 | runCount++ 41 | } catch (err: any) { 42 | DebugLog.mSaveDanger('XM appendFile' + (err.message || '') + fileList[i]) 43 | } 44 | } 45 | return runCount 46 | } 47 | } 48 | 49 | async function GetAllFiles(dir: string, breakSmall: boolean, fileList: string[]) { 50 | if (dir.endsWith(path.sep) == false) dir = dir + path.sep 51 | try { 52 | const childfiles = await fsPromises.readdir(dir).catch((err: any) => { 53 | err = FileSystemErrorMessage(err.code, err.message) 54 | DebugLog.mSaveDanger('XMGetAllFiles文件失败:' + dir, err) 55 | message.error('跳过文件夹:' + err + ' ' + dir) 56 | return [] 57 | }) 58 | 59 | let allTask: Promise[] = [] 60 | const dirList: string[] = [] 61 | for (let i = 0, maxi = childfiles.length; i < maxi; i++) { 62 | const name = childfiles[i] as string 63 | if (name.startsWith('.')) continue 64 | if (name.startsWith('#')) continue 65 | const item = dir + name 66 | allTask.push( 67 | fsPromises 68 | .lstat(item) 69 | .then((stat: any) => { 70 | if (stat.isDirectory()) dirList.push(item) 71 | else if (stat.isSymbolicLink()) { 72 | // donothing 73 | } else if (stat.isFile()) { 74 | if (breakSmall == false || stat.size > 5 * 1024 * 1024) fileList.push(item) 75 | } 76 | }) 77 | .catch() 78 | ) 79 | if (allTask.length > 10) { 80 | await Promise.all(allTask).catch(() => {}) 81 | allTask = [] 82 | } 83 | } 84 | 85 | if (allTask.length > 0) { 86 | await Promise.all(allTask).catch(() => {}) 87 | allTask = [] 88 | } 89 | 90 | for (let i = 0, maxi = dirList.length; i < maxi; i++) { 91 | await GetAllFiles(dirList[i], breakSmall, fileList) 92 | } 93 | } catch (err: any) { 94 | DebugLog.mSaveDanger('GetAllFiles' + (err.message || '')) 95 | } 96 | 97 | return true 98 | } 99 | -------------------------------------------------------------------------------- /src/setting/SettingLocalAria.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/setting/SettingLog.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 64 | 65 | 83 | -------------------------------------------------------------------------------- /src/setting/ShutDown.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 59 | 66 | -------------------------------------------------------------------------------- /src/share/following/OtherFollowingStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { IAliOtherFollowingModel } from '../../aliapi/alimodels' 3 | 4 | export declare interface FollowingState { 5 | 6 | TuiJianLoading: boolean 7 | 8 | TuiJianLoaded: boolean 9 | 10 | TuiJianList: { key: string; color: string; list: IAliOtherFollowingModel[] }[] 11 | } 12 | 13 | const useFollowingStore = defineStore('following', { 14 | state: (): FollowingState => ({ 15 | TuiJianLoading: false, 16 | TuiJianLoaded: false, 17 | TuiJianList: [{ key: '官方推荐', color: 'arcoblue', list: [] }] 18 | }), 19 | getters: {}, 20 | actions: { 21 | 22 | aSaveOtherFollowingList(key: string, color: string, list: IAliOtherFollowingModel[]) { 23 | list.sort((a, b) => b.follower_count - a.follower_count) 24 | for (let i = 0, maxi = this.TuiJianList.length; i < maxi; i++) { 25 | if (this.TuiJianList[i].key == key) { 26 | this.TuiJianList[i].color = color 27 | this.TuiJianList[i].list = list 28 | return 29 | } 30 | } 31 | this.TuiJianList.push({ key, color, list }) 32 | } 33 | } 34 | }) 35 | 36 | export default useFollowingStore 37 | -------------------------------------------------------------------------------- /src/share/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/share/share/ShareSiteRight.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 27 | 28 | 58 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | import useAppStore from './appstore' 3 | import useKeyboardStore from './keyboardstore' 4 | import useMouseStore from './mousestore' 5 | import type { KeyboardState } from './keyboardstore' 6 | import type { MouseState } from './mousestore' 7 | import useLogStore from './logstore' 8 | import useModalStore from './modalstore' 9 | import type { ModalState } from './modalstore' 10 | import useWinStore from './winstore' 11 | import type { WinState } from './winstore' 12 | import useSettingStore from '../setting/settingstore' 13 | import useUserStore from '../user/userstore' 14 | import type { ITokenInfo } from '../user/userstore' 15 | import usePanTreeStore from '../pan/pantreestore' 16 | import usePanFileStore from '../pan/panfilestore' 17 | 18 | import useServerStore from './serverstore' 19 | import type { IOtherShareLinkModel } from '../share/share/OtherShareStore' 20 | import type { IShareSiteModel } from './serverstore' 21 | import useMyShareStore from '../share/share/MyShareStore' 22 | import useOtherShareStore from '../share/share/OtherShareStore' 23 | import useMyFollowingStore from '../share/following/MyFollowingStore' 24 | import useOtherFollowingStore from '../share/following/OtherFollowingStore' 25 | import type { FollowingState } from '../share/following/OtherFollowingStore' 26 | 27 | import useUploadingStore from '../down/UploadingStore' 28 | import useUploadedStore from '../down/UploadedStore' 29 | import useDownedStore from '../down/DownedStore' 30 | import useDowningStore from '../down/DowningStore' 31 | 32 | import useFootStore from './footstore' 33 | import type { AsyncModel } from './footstore' 34 | import useM3u8DownloadingStore from '../down/m3u8/M3u8DownloadingStore' 35 | import useM3u8DownloadedStore from '../down/m3u8/M3u8DownloadedStore' 36 | 37 | const pinia = createPinia() 38 | export { 39 | useAppStore, 40 | useSettingStore, 41 | useLogStore, 42 | useModalStore, 43 | ModalState, 44 | useWinStore, 45 | WinState, 46 | useMouseStore, 47 | useKeyboardStore, 48 | KeyboardState, 49 | MouseState, 50 | useUserStore, 51 | ITokenInfo, 52 | usePanTreeStore, 53 | usePanFileStore, 54 | useServerStore, 55 | IOtherShareLinkModel, 56 | IShareSiteModel, 57 | useMyShareStore, 58 | useOtherShareStore, 59 | useOtherFollowingStore, 60 | FollowingState, 61 | useMyFollowingStore, 62 | useFootStore, 63 | AsyncModel, 64 | useUploadingStore, 65 | useUploadedStore, 66 | useDowningStore, 67 | useDownedStore, 68 | useM3u8DownloadingStore, 69 | useM3u8DownloadedStore 70 | } 71 | export default pinia 72 | -------------------------------------------------------------------------------- /src/store/keyboardstore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export interface KeyboardMessage { 4 | 5 | Code: string 6 | 7 | Key: string 8 | Ctrl: boolean 9 | Shift: boolean 10 | Alt: boolean 11 | 12 | Repeat: boolean 13 | 14 | IsInput: boolean 15 | IsEnmpty: boolean 16 | } 17 | 18 | export interface KeyboardState { 19 | KeyDownEvent: KeyboardMessage 20 | KeyUpEvent: KeyboardMessage 21 | } 22 | 23 | const useKeyboardStore = defineStore('keyboard', { 24 | state: (): KeyboardState => ({ 25 | KeyDownEvent: { 26 | Ctrl: false, 27 | Shift: false, 28 | Alt: false, 29 | Repeat: false, 30 | IsInput: false, 31 | Code: '', 32 | Key: '', 33 | IsEnmpty: true 34 | } as KeyboardMessage, 35 | KeyUpEvent: { 36 | Ctrl: false, 37 | Shift: false, 38 | Alt: false, 39 | Repeat: false, 40 | IsInput: false, 41 | Code: '', 42 | Key: '', 43 | IsEnmpty: true 44 | } as KeyboardMessage 45 | }), 46 | 47 | getters: {}, 48 | 49 | actions: { 50 | updateStore(partial: Partial) { 51 | this.$patch(partial) 52 | }, 53 | KeyDown(event: KeyboardEvent) { 54 | console.log('KeyboardEvent', event) 55 | this.KeyDownEvent = { 56 | Ctrl: event.ctrlKey, 57 | Shift: event.shiftKey, 58 | Alt: event.altKey, 59 | Repeat: event.repeat, 60 | IsInput: false, 61 | Code: event.code, 62 | Key: event.key.toLowerCase(), 63 | IsEnmpty: false 64 | } 65 | } 66 | } 67 | }) 68 | 69 | export default useKeyboardStore 70 | -------------------------------------------------------------------------------- /src/store/logstore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export interface LogState { 4 | logTime: number 5 | } 6 | 7 | const useLogStore = defineStore('log', { 8 | state: (): LogState => ({ 9 | logTime: Date.now() 10 | }), 11 | 12 | getters: {}, 13 | 14 | actions: { 15 | logRefresh(time: number) { 16 | this.logTime = time 17 | } 18 | } 19 | }) 20 | 21 | export default useLogStore 22 | -------------------------------------------------------------------------------- /src/store/modalstore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { onHideRightMenuScroll } from '../utils/keyboardhelper' 3 | 4 | export interface ModalState { 5 | modalName: string 6 | modalData: any 7 | } 8 | 9 | const useModalStore = defineStore('modal', { 10 | state: (): ModalState => ({ 11 | modalName: '', 12 | modalData: {} 13 | }), 14 | 15 | actions: { 16 | showModal(modalName: string, modalData: any) { 17 | if (modalName) onHideRightMenuScroll() 18 | if (modalName && modalName == this.modalName) { 19 | this.$patch({ modalName: '', modalData: {} }) 20 | setTimeout(() => { 21 | this.$patch({ modalName, modalData }) 22 | }, 300) 23 | } else this.$patch({ modalName, modalData }) 24 | } 25 | } 26 | }) 27 | 28 | export default useModalStore 29 | -------------------------------------------------------------------------------- /src/store/mousestore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export interface MouseMessage { 4 | isTrusted: boolean; 5 | Ctrl: boolean; 6 | Shift: boolean; 7 | Alt: boolean; 8 | button: number; 9 | buttons: number; 10 | srcElement: EventTarget | null; 11 | } 12 | 13 | export interface MouseState { 14 | MouseEvent: MouseMessage 15 | } 16 | 17 | const useMouseStore = defineStore('mouse', { 18 | state: (): MouseState => ({ 19 | MouseEvent: { 20 | isTrusted: false, 21 | Ctrl: false, 22 | Shift: false, 23 | Alt: false, 24 | button: 0, 25 | buttons: 0 26 | } as MouseMessage, 27 | }), 28 | getters: {}, 29 | actions: { 30 | updateStore(partial: Partial) { 31 | this.$patch(partial) 32 | }, 33 | KeyDown(event: MouseEvent) { 34 | // console.log('MouseEvent', event) 35 | this.MouseEvent = { 36 | isTrusted: event.isTrusted, 37 | Ctrl: event.ctrlKey, 38 | Shift: event.shiftKey, 39 | Alt: event.altKey, 40 | button: event.button, 41 | buttons: event.buttons, 42 | srcElement: event.srcElement 43 | } 44 | } 45 | } 46 | }) 47 | 48 | export default useMouseStore 49 | -------------------------------------------------------------------------------- /src/store/serverstore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | 4 | export interface IShareSiteModel { 5 | title: string 6 | url: string 7 | tip: string 8 | } 9 | 10 | export interface ServerState { 11 | 12 | shareSiteList: IShareSiteModel[] 13 | helpUrl: string 14 | } 15 | 16 | const useServerStore = defineStore('serverstore', { 17 | state: (): ServerState => ({ 18 | shareSiteList: [], 19 | helpUrl: 'aHR0cHM6Ly9naXRodWIuY29tL2dhb3poYW5nbWluL2FsaXl1bnBhbg==' 20 | }), 21 | actions: { 22 | 23 | mSaveShareSiteList(shareSiteList: IShareSiteModel[]) { 24 | this.shareSiteList = shareSiteList || [] 25 | }, 26 | 27 | mSaveHelpUrl(url: string) { 28 | this.helpUrl = url || 'aHR0cHM6Ly9naXRodWIuY29tL2dhb3poYW5nbWluL2FsaXl1bnBhbg==' 29 | } 30 | } 31 | }) 32 | 33 | export default useServerStore 34 | -------------------------------------------------------------------------------- /src/store/winstore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export interface WinState { 4 | width: number 5 | height: number 6 | } 7 | 8 | const useWinStore = defineStore('win', { 9 | state: (): WinState => ({ 10 | width: 0, 11 | height: 0 12 | }), 13 | 14 | getters: { 15 | GetListHeight(state: WinState): string { 16 | return (state.height - 192).toString() + 'px' 17 | }, 18 | GetListHeightNumber(state: WinState): number { 19 | return state.height - 192 20 | } 21 | }, 22 | 23 | actions: { 24 | updateStore(partial: Partial) { 25 | this.$patch(partial) 26 | } 27 | } 28 | }) 29 | 30 | export default useWinStore 31 | -------------------------------------------------------------------------------- /src/transfer/uploaddal.ts: -------------------------------------------------------------------------------- 1 | import { useSettingStore } from '../store' 2 | import DBUpload from '../utils/dbupload' 3 | import { clickWait, clickWaitDelete } from '../utils/debounce' 4 | import useUploadedStore from '../down/UploadedStore' 5 | 6 | export default class UploadDAL { 7 | 8 | static async aReloadUploaded() { 9 | const uploadedStore = useUploadedStore() 10 | if (uploadedStore.ListLoading == true) return 11 | uploadedStore.ListLoading = true 12 | const max = useSettingStore().debugDownedListMax 13 | const showlist = await DBUpload.getUploadedByTop(max) 14 | const count = await DBUpload.getUploadTaskCount() 15 | uploadedStore.aLoadListData(showlist, count) 16 | uploadedStore.ListLoading = false 17 | } 18 | 19 | 20 | static async aClearUploaded() { 21 | const max = useSettingStore().debugDownedListMax 22 | return await DBUpload.deleteUploadedOutCount(max) 23 | } 24 | 25 | 26 | static async UploadedDelete(all: boolean) { 27 | if (clickWait('UploadedDelete', -1)) return 28 | if (all) { 29 | await DBUpload.clearUploadedAll() 30 | } else { 31 | const uploadedStore = useUploadedStore() 32 | const keys = Array.from(uploadedStore.ListSelected) 33 | await DBUpload.deleteUploadedBatch(keys) 34 | } 35 | await this.aReloadUploaded() 36 | clickWaitDelete('UploadedDelete') 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/user/SponsorInfo.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | 41 | -------------------------------------------------------------------------------- /src/user/userstore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import UserDAL from './userdal' 3 | 4 | 5 | export interface ITokenInfo { 6 | tokenfrom: 'token' | 'account' 7 | 8 | 9 | access_token: string 10 | refresh_token: string 11 | expires_in: number 12 | token_type: string 13 | 14 | access_token_v2?: string 15 | refresh_token_v2?: string 16 | expires_in_v2?: number 17 | token_type_v2?: string 18 | 19 | signature: string 20 | device_id: string 21 | user_id: string 22 | user_name: string 23 | avatar: string 24 | nick_name: string 25 | default_drive_id: string 26 | default_sbox_drive_id: string 27 | role: string 28 | status: string 29 | expire_time: string 30 | state: string 31 | pin_setup: boolean 32 | is_first_login: boolean 33 | need_rp_verify: boolean 34 | 35 | name: string 36 | spu_id: string 37 | is_expires: boolean 38 | used_size: number 39 | total_size: number 40 | spaceinfo: string 41 | vipname: string 42 | vipexpire: string 43 | 44 | 45 | pic_drive_id: string 46 | 47 | signInfo: { 48 | signMon: number; 49 | signDay: number; 50 | } 51 | } 52 | 53 | export interface UserState { 54 | user_id: string 55 | userLogined: boolean 56 | userShowLogin: boolean 57 | } 58 | 59 | const useUserStore = defineStore('user', { 60 | state: (): UserState => ({ 61 | user_id: '', 62 | userLogined: false, 63 | userShowLogin: false 64 | }), 65 | 66 | getters: { 67 | GetUserToken(state: UserState): ITokenInfo { 68 | return UserDAL.GetUserToken(state.user_id) 69 | } 70 | }, 71 | 72 | actions: { 73 | userLogin(user_id: string) { 74 | this.user_id = user_id 75 | this.userLogined = true 76 | }, 77 | userLogOff() { 78 | this.user_id = '' 79 | this.userLogined = false 80 | } 81 | } 82 | }) 83 | 84 | export default useUserStore 85 | -------------------------------------------------------------------------------- /src/utils/antdtree.ts: -------------------------------------------------------------------------------- 1 | import { EventDataNode } from 'ant-design-vue/es/tree' 2 | 3 | 4 | export function treeSelectToExpand(keys: any[], info: { event: string; selected: Boolean; nativeEvent: MouseEvent; node: EventDataNode }) { 5 | let parent = info.nativeEvent.target as HTMLElement 6 | if (parent) { 7 | for (let i = 0; i < 10; i++) { 8 | if (parent.nodeName == 'DIV' && (parent.className == 'ant-tree-treenode' || parent.className.indexOf('ant-tree-treenode ') >= 0)) break 9 | if (parent.parentElement) parent = parent.parentElement 10 | } 11 | const children = parent.children 12 | if (children) { 13 | for (let i = 0, maxi = children.length; i < maxi; i++) { 14 | if (info.node.isLeaf) { 15 | 16 | if (children[i].className.indexOf('ant-tree-checkbox') >= 0) (children[i] as HTMLElement).click() 17 | } else { 18 | 19 | if (children[i].className.indexOf('ant-tree-switcher') >= 0) (children[i] as HTMLElement).click() 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/appcache.ts: -------------------------------------------------------------------------------- 1 | import { useSettingStore } from '../store' 2 | import DebugLog from './debuglog' 3 | import { getUserData } from './electronhelper' 4 | import { FileSystemErrorMessage } from './filehelper' 5 | import { humanSize, Sleep } from './format' 6 | import message from './message' 7 | 8 | import path from 'path' 9 | import fsPromises from 'fs/promises' 10 | 11 | export default class AppCache { 12 | 13 | static async LoadDirSize(dir: string): Promise { 14 | try { 15 | const childFiles = await fsPromises.readdir(dir, { withFileTypes: true }).catch((err: any) => { 16 | err = FileSystemErrorMessage(err.code, err.message) 17 | DebugLog.mSaveDanger('LoadDirSize失败:' + dir, err) 18 | message.error(err + ' ' + dir) 19 | return [] 20 | }) 21 | let total = 0 22 | for (let i = 0, maxi = childFiles.length; i < maxi; i++) { 23 | if (childFiles[i].isFile()) { 24 | 25 | const stat = await fsPromises.lstat(path.join(dir, childFiles[i].name)).catch(() => { 26 | return { size: 0 } 27 | }) 28 | total += stat.size 29 | } else if (childFiles[i].isDirectory()) { 30 | 31 | total += await AppCache.LoadDirSize(path.join(dir, childFiles[i].name)) 32 | } 33 | } 34 | return total 35 | } catch { 36 | return 0 37 | } 38 | } 39 | 40 | static DeleteDir(dir: string): Promise { 41 | return fsPromises 42 | .rm(dir, { force: true, recursive: true }) 43 | .then(() => {}) 44 | .catch(() => {}) 45 | } 46 | 47 | 48 | static async aLoadCacheSize(): Promise { 49 | const userData = getUserData() 50 | if (!userData) return 51 | const dirSize = await AppCache.LoadDirSize(userData) 52 | if (dirSize > 800 * 1024 * 1024) message.warning('缓存文件夹体积较大,该去 设置 里清理了') 53 | 54 | useSettingStore().debugCacheSize = humanSize(dirSize) 55 | } 56 | 57 | 58 | static async aClearCache(delby: string): Promise { 59 | const dir = getUserData() 60 | // await AppCache.DeleteDir(path.join(dir, 'Cache')) 61 | if (delby == 'all') { 62 | // window.WebClearCache({ cache: true }) 63 | if (window.WebClearCache) 64 | window.WebClearCache({ 65 | storages: ['appcache', 'cookies', 'filesystem', 'shadercache', 'serviceworkers', 'cachestorage', 'indexdb', 'localstorage', 'websql'], 66 | quotas: ['temporary', 'persistent', 'syncable'] 67 | }) 68 | } else { 69 | // window.WebClearCache({ cache: true }) 70 | if (window.WebClearCache) 71 | window.WebClearCache({ 72 | storages: ['appcache', 'cookies', 'filesystem', 'shadercache', 'serviceworkers', 'cachestorage'], 73 | quotas: ['temporary', 'persistent', 'syncable'] 74 | }) 75 | } 76 | if (delby == 'all') { 77 | await AppCache.DeleteDir(path.join(dir, 'databases')).catch(() => {}) 78 | await AppCache.DeleteDir(path.join(dir, 'IndexedDB')).catch(() => {}) 79 | await AppCache.DeleteDir(path.join(dir, 'Local Storage')).catch(() => {}) 80 | await AppCache.DeleteDir(path.join(dir, 'Session Storage')).catch(() => {}) 81 | } else if (delby == 'db') { 82 | await AppCache.DeleteDir(path.join(dir, 'databases')).catch(() => {}) 83 | } 84 | await AppCache.DeleteDir(path.join(dir, 'Code Cache', 'js')).catch(() => {}) 85 | await AppCache.DeleteDir(path.join(dir, 'Code Cache', 'wasm')).catch(() => {}) 86 | 87 | await Sleep(4000) 88 | 89 | 90 | if (delby == 'all') { 91 | message.success('删除全部数据成功,自动重启小白羊') 92 | Sleep(3000).then(() => { 93 | window.WebRelaunch() 94 | }) 95 | } else if (delby == 'db') { 96 | message.success('删除数据库成功,自动重启小白羊') 97 | Sleep(3000).then(() => { 98 | window.WebRelaunch() 99 | }) 100 | } else { 101 | message.success('清理缓存成功,自动重启小白羊') 102 | Sleep(3000).then(() => { 103 | // window.WebReload() 104 | window.WebRelaunch() 105 | }) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/utils/config.ts: -------------------------------------------------------------------------------- 1 | export default class Config { 2 | static appVersion = '3.11.15' 3 | static referer = 'https://www.aliyundrive.com/' 4 | static downAgent = 'okhttp/4.2.2' 5 | static userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) aDrive/4.1.0 Chrome/108.0.5359.215 Electron/22.3.1 Safari/537.36' 6 | static loginUrl = 'https://auth.aliyundrive.com/v2/oauth/authorize?login_type=custom&response_type=code&redirect_uri=https%3A%2F%2Fwww.aliyundrive.com%2Fsign%2Fcallback&client_id=25dzX3vbYqktVxyX&state=%7B%22origin%22%3A%22https%3A%2F%2Fwww.aliyundrive.com%2F%22%7D' 7 | static loginUrlAccount = 'https://passport.aliyundrive.com/mini_login.htm?lang=zh_cn&appName=aliyun_drive&appEntrance=web&styleType=auto&bizParams=¬LoadSsoView=false¬KeepLogin=false&isMobile=false&&rnd=0.1100330129139' 8 | static qrCodeLoginUrl = 'https://open.aliyundrive.com/oauth/authorize/qrcode' 9 | static accessTokenUrl = 'https://open.aliyundrive.com/oauth/access_token' 10 | static driverInfoUrl = 'https://open.aliyundrive.com/adrive/v1.0/user/getDriveInfo' 11 | static listFile = 'adrive/v1.0/openFile/list' 12 | static recentPlayListUrl = 'https://openapi.aliyundrive.com/adrive/v1.0/openFile/video/recentList' 13 | static tmpUrl = 'https:///api-cf.nn.ci/alist/ali_open/token' 14 | static tmpQrCodeUrl = 'https:///api-cf.nn.ci/alist/ali_open/qr' 15 | static tokenUrl = 'https:///api-cf.nn.ci/alist/ali_open/code' 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/dbcache.ts: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie' 2 | import { IStateDebugLog } from './debuglog' 3 | 4 | export interface IStateFileHash { 5 | size: number 6 | mtime: number 7 | 8 | presha1: string 9 | sha1: string 10 | name: string 11 | } 12 | class XBYDB3Cache extends Dexie { 13 | ilog: Dexie.Table 14 | ifilehash: Dexie.Table 15 | iobject: Dexie.Table 16 | 17 | constructor() { 18 | super('XBYDB3Cache') 19 | 20 | this.version(10) 21 | .stores({ 22 | ilog: '&logid', 23 | ifilehash: '++id,[size+mtime]', 24 | iobject: '' 25 | }) 26 | .upgrade((tx: any) => { 27 | console.log('upgrade', tx) 28 | }) 29 | this.ilog = this.table('ilog') 30 | this.ifilehash = this.table('ifilehash') 31 | this.iobject = this.table('iobject') 32 | } 33 | 34 | async saveLog(value: IStateDebugLog) { 35 | if (!this.isOpen()) await this.open().catch(() => {}) 36 | return this.ilog.put(value).catch(() => {}) 37 | } 38 | 39 | async getLogAll(): Promise { 40 | if (!this.isOpen()) await this.open().catch(() => {}) 41 | return this.transaction('r', this.ilog, () => { 42 | return this.ilog.reverse().limit(500).toArray() 43 | }) 44 | } 45 | 46 | async deleteLogAll(): Promise { 47 | if (!this.isOpen()) await this.open().catch(() => {}) 48 | return this.ilog.clear() 49 | } 50 | 51 | async deleteLogOutCount(max: number): Promise { 52 | if (!this.isOpen()) await this.open().catch(() => {}) 53 | const count = await this.ilog.count() 54 | if (count > max) { 55 | return this.ilog.limit(max - count).delete() 56 | } 57 | return 0 58 | } 59 | 60 | async getFileHashList(size: number, mtime: number): Promise { 61 | if (!this.isOpen()) await this.open().catch(() => {}) 62 | return this.ifilehash.where({ size, mtime }).toArray() 63 | } 64 | 65 | async getFileHash(size: number, mtime: number, prehash: string, name: string): Promise { 66 | if (!this.isOpen()) await this.open().catch(() => {}) 67 | const hashList = await this.ifilehash.where({ size, mtime }).toArray() 68 | for (let i = 0, maxi = hashList.length; i < maxi; i++) { 69 | if (hashList[i].presha1 == prehash && hashList[i].name == name) { 70 | return hashList[i].sha1 71 | } 72 | } 73 | return '' 74 | } 75 | 76 | async saveFileHash(value: IStateFileHash) { 77 | if (!this.isOpen()) await this.open().catch(() => {}) 78 | return this.ifilehash.put(value).catch(() => {}) 79 | } 80 | } 81 | 82 | const DBCache = new XBYDB3Cache() 83 | export default DBCache 84 | -------------------------------------------------------------------------------- /src/utils/dbdown.ts: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie' 2 | import { IStateDownFile } from '../down/DownDAL' 3 | import useUserStore from '../user/userstore' 4 | 5 | class XBYDB3Down extends Dexie { 6 | idowning: Dexie.Table 7 | idowned: Dexie.Table 8 | 9 | constructor() { 10 | super('XBYDB3Down') 11 | 12 | this.version(10) 13 | .stores({ 14 | idowning: 'DownID, Info.drive_id, Info.user_id', 15 | idowned: 'DownID, Info.drive_id, Info.user_id, Down.DownTime', 16 | }) 17 | .upgrade((tx: any) => { 18 | console.log('upgrade', tx) 19 | }) 20 | this.idowning = this.table('idowning') 21 | this.idowned = this.table('idowned') 22 | } 23 | 24 | async getDowning(key: string): Promise { 25 | if (!this.isOpen()) await this.open().catch(() => {}) 26 | const val = await this.idowning.get(key) 27 | if (val) return val 28 | else return undefined 29 | } 30 | async getDowningAll(): Promise { 31 | if (!this.isOpen()) await this.open().catch(() => {}) 32 | return this.idowning.where('Info.user_id').equals(useUserStore().user_id).toArray() 33 | } 34 | async deleteDowning(key: string) { 35 | if (!this.isOpen()) await this.open().catch(() => {}) 36 | return this.idowning.delete(key) 37 | } 38 | async deleteDownings(keys: string[]) { 39 | if (!this.isOpen()) await this.open().catch(() => {}) 40 | return this.idowning.bulkDelete(keys) 41 | } 42 | async saveDowning(key: string, value: IStateDownFile) { 43 | if (!this.isOpen()) await this.open().catch(() => {}) 44 | return this.idowning.put(value, key).catch(() => {}) 45 | } 46 | async saveDownings(values: IStateDownFile[]) { 47 | if (!this.isOpen()) await this.open().catch(() => {}) 48 | return this.idowning.bulkPut(values).catch(() => {}) 49 | } 50 | async deleteDowningAll() { 51 | if (!this.isOpen()) await this.open().catch(() => {}) 52 | return this.idowning.where('Info.user_id').equals(useUserStore().user_id).delete() 53 | } 54 | 55 | async getDowned(key: string): Promise { 56 | if (!this.isOpen()) await this.open().catch(() => {}) 57 | const val = await this.idowned.get(key) 58 | if (val) return val 59 | else return undefined 60 | } 61 | 62 | async getDownedAll(): Promise { 63 | if (!this.isOpen()) await this.open().catch(() => {}) 64 | return this.idowned.where('Info.user_id').equals(useUserStore().user_id).reverse().toArray() 65 | } 66 | 67 | async getDownedByTop(limit: number) { 68 | if (!this.isOpen()) await this.open().catch(() => {}) 69 | return this.transaction('r', this.idowned, () => { 70 | return this.idowned.reverse().limit(limit).toArray() 71 | }) 72 | } 73 | 74 | async getDownedTaskCount(): Promise { 75 | if (!this.isOpen()) await this.open().catch(() => {}) 76 | return this.transaction('r', this.idowned, () => { 77 | return this.idowned.count() 78 | }) 79 | } 80 | 81 | async deleteDowned(key: string) { 82 | if (!this.isOpen()) await this.open().catch(() => {}) 83 | return this.idowned.delete(key) 84 | } 85 | async deleteDowneds(keys: string[]) { 86 | if (!this.isOpen()) await this.open().catch(() => {}) 87 | return this.idowned.bulkDelete(keys) 88 | } 89 | 90 | async deleteDownedOutCount(max: number): Promise { 91 | if (!this.isOpen()) await this.open().catch(() => {}) 92 | const count = await this.idowned.count() 93 | if (count > max) { 94 | return this.idowned.limit(max - count).delete() 95 | } 96 | return 0 97 | } 98 | 99 | async saveDowned(key: string, value: IStateDownFile) { 100 | if (!this.isOpen()) await this.open().catch(() => {}) 101 | return this.idowned.put(value, key).catch(() => {}) 102 | } 103 | async deleteDownedAll() { 104 | if (!this.isOpen()) await this.open().catch(() => {}) 105 | return this.idowned.where('Info.user_id').equals(useUserStore().user_id).delete() 106 | } 107 | } 108 | 109 | const DBDown = new XBYDB3Down() 110 | export default DBDown 111 | -------------------------------------------------------------------------------- /src/utils/debounce.ts: -------------------------------------------------------------------------------- 1 | import message from './message' 2 | 3 | 4 | export function debounce(func: Function, wait: number, immediate: boolean = true, lastCall: boolean = true, leakCall: boolean = false) { 5 | if (lastCall !== false) lastCall = true 6 | if (immediate !== false) immediate = true 7 | let previous = 0 8 | let timer: any 9 | return function (...args: any) { 10 | // @ts-ignore 11 | const context = this 12 | const now = Date.now() 13 | 14 | const timeoutToCall = function timeoutToCall() { 15 | if (!leakCall && timer) { 16 | clearTimeout(timer) 17 | timer = undefined 18 | } 19 | 20 | if (!timer) { 21 | timer = setTimeout(function () { 22 | timer = undefined 23 | func.apply(context, args) 24 | }, wait) 25 | } 26 | } 27 | 28 | if (now - previous > wait) { 29 | previous = now 30 | 31 | if (immediate) { 32 | func.apply(context, args) 33 | } else if (lastCall) { 34 | timeoutToCall() 35 | } 36 | } else { 37 | previous = now 38 | if (lastCall) timeoutToCall() 39 | } 40 | } 41 | } 42 | 43 | 44 | export function throttle(func: Function, wait: number, immediate: boolean = true, lastCall: boolean = true) { 45 | return debounce(func, wait, immediate, lastCall, true) 46 | } 47 | 48 | const clkcimap = new Set() 49 | 50 | export function clickWait(cmdkey: string, wait: number = -1): boolean { 51 | if (clkcimap.has(cmdkey)) { 52 | message.info('上一个操作还在执行中,稍等1秒再点') 53 | return true 54 | } 55 | clkcimap.add(cmdkey) 56 | if (wait > 0) { 57 | setTimeout(() => { 58 | clkcimap.delete(cmdkey) 59 | }, wait) 60 | } 61 | return false 62 | } 63 | 64 | export function clickWaitDelete(cmdkey: string): void { 65 | clkcimap.delete(cmdkey) 66 | } 67 | -------------------------------------------------------------------------------- /src/utils/debuglog.ts: -------------------------------------------------------------------------------- 1 | import { useLogStore } from '../store' 2 | import DBCache from './dbcache' 3 | 4 | export interface IStateDebugLog { 5 | logid: number 6 | logtime: string 7 | logtype: string 8 | logmessage: string 9 | } 10 | 11 | class DebugLogC { 12 | public logList: IStateDebugLog[] = [] 13 | public logTime: number = 0 14 | mSaveLogClear() { 15 | this.logList = [] 16 | this.logTime = Date.now() 17 | 18 | try { 19 | DBCache.deleteLogAll().catch(() => {}) 20 | useLogStore().logRefresh(this.logTime) 21 | } catch {} 22 | } 23 | 24 | mSaveDanger(logmessage: string, err: any = undefined) { 25 | this.mSaveLog('danger', logmessage, err) 26 | } 27 | 28 | mSaveWarning(logmessage: string, err: any = undefined) { 29 | this.mSaveLog('warning', logmessage, err) 30 | } 31 | 32 | mSaveSuccess(logmessage: string, err: any = undefined) { 33 | this.mSaveLog('success', logmessage, err) 34 | } 35 | 36 | mSaveLog(logtype: string, logmessage: string, err: any) { 37 | if (!logmessage && !err) return 38 | if (logmessage && typeof logmessage == 'string' && logmessage.length > 500) logmessage = logmessage.substring(0, 500) + '...' 39 | const time = new Date() 40 | if (this.logList.length > 500) { 41 | this.logList.splice(400) 42 | DBCache.deleteLogOutCount(400) 43 | } 44 | 45 | const log = { 46 | logid: time.getTime(), 47 | logtime: time.getDate().toString().padStart(2, '0') + ' ' + time.getHours().toString().padStart(2, '0') + ':' + time.getMinutes().toString().padStart(2, '0') + ':' + time.getSeconds().toString().padStart(2, '0'), 48 | logtype: logtype, 49 | logmessage: logmessage 50 | } 51 | 52 | if (err) { 53 | if (typeof err == 'string') { 54 | log.logmessage = logmessage + ' \n//== Error ==//\n ' + err 55 | } else if (err.message) { 56 | let m = err.message + (err.stack ? ' \n//== Stack ===//\n ' + err.stack : '') 57 | if (m.length > 500) m = m.substring(0, 500) + '...' 58 | log.logmessage = logmessage + ' \n//== Error ==//\n ' + m 59 | } else { 60 | try { 61 | log.logmessage = logmessage + ' \n//== Error ==//\n ' + JSON.stringify(err) 62 | } catch { 63 | log.logmessage = logmessage + ' \n//== Error ==//\n stringify failed' 64 | } 65 | } 66 | } 67 | this.logList = [log].concat(this.logList) 68 | this.logTime = time.getTime() 69 | try { 70 | DBCache.saveLog(log).catch(() => {}) 71 | useLogStore().logRefresh(this.logTime) 72 | } catch {} 73 | } 74 | 75 | async aLoadFromDB() { 76 | const logList2 = await DBCache.getLogAll() 77 | if (logList2) this.logList = logList2 as IStateDebugLog[] 78 | this.logTime = Date.now() 79 | } 80 | } 81 | 82 | const DebugLog = new DebugLogC() 83 | export default DebugLog 84 | -------------------------------------------------------------------------------- /src/utils/electronhelper.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { throttle } from './debounce' 3 | 4 | export function getFromClipboard(): string { 5 | return window.Electron.clipboard.readText() as string 6 | } 7 | 8 | export function copyToClipboard(text: string): void { 9 | window.Electron.clipboard.writeText(text, 'clipboard') 10 | } 11 | export function openExternal(url: string): void { 12 | window.Electron.shell.openExternal(url) 13 | } 14 | 15 | const ElectronPath = { 16 | 17 | AppUserData: '', 18 | 19 | AppResourcesPath: '', 20 | 21 | AppPlatform: '', 22 | 23 | AppArch: '', 24 | 25 | AppExecPath: '', 26 | 27 | AppUserName: '', 28 | env: '' 29 | } 30 | 31 | 32 | function LoadElectronPath(): void { 33 | if (!ElectronPath.AppUserData) { 34 | ElectronPath.AppPlatform = process.platform 35 | ElectronPath.AppArch = process.arch 36 | ElectronPath.AppExecPath = process.execPath 37 | ElectronPath.env = JSON.stringify(process.env) 38 | ElectronPath.AppUserName = process.env.USERNAME || process.env.USER || '' 39 | ElectronPath.AppResourcesPath = (process as any).resourcesPath 40 | if (window.WebPlatformSync) { 41 | window.WebPlatformSync((data: { appPath: string; execPath: string }) => { 42 | ElectronPath.AppUserData = data.appPath 43 | ElectronPath.AppExecPath = data.execPath 44 | window.Electron.WebPlatformSync = data 45 | }) 46 | } 47 | 48 | window.Electron.ElectronPath = ElectronPath 49 | } 50 | } 51 | 52 | export function getUserData(): string { 53 | LoadElectronPath() 54 | return ElectronPath.AppUserData 55 | } 56 | 57 | export function getUserDataPath(fileName: string): string { 58 | try { 59 | LoadElectronPath() 60 | return path.join(ElectronPath.AppUserData, fileName) as string 61 | } catch { 62 | return '' 63 | } 64 | } 65 | 66 | export function getResourcesPath(fileName: string): string { 67 | try { 68 | LoadElectronPath() 69 | return path.join(ElectronPath.AppResourcesPath, fileName) as string 70 | } catch { 71 | return '' 72 | } 73 | } 74 | 75 | export function getAppNewPath(): string { 76 | try { 77 | LoadElectronPath() 78 | return path.join(ElectronPath.AppResourcesPath, 'app.new') as string 79 | } catch { 80 | return '' 81 | } 82 | } 83 | 84 | let ProgressBarBy = '' 85 | let ProgressBarValue = -1 86 | let ProgressBarNew = -1 87 | const setProgressBar = throttle(() => { 88 | ProgressBarValue = ProgressBarNew 89 | const mode = ProgressBarValue < 0 ? 'none' : ProgressBarBy == 'download' ? 'normal' : 'paused' 90 | if (window.WebSetProgressBar) window.WebSetProgressBar({ pro: ProgressBarValue, mode }) 91 | }, 5000) 92 | 93 | 94 | export function SetProgressBar(value: number, by: string): void { 95 | if (value < 0) value = -1 96 | if (ProgressBarValue == value && ProgressBarBy == by) return 97 | 98 | ProgressBarNew = value 99 | ProgressBarBy = by 100 | if (value < 0 || (ProgressBarValue < 0 && value > 0)) { 101 | 102 | const mode = value < 0 ? 'none' : ProgressBarBy == 'download' ? 'normal' : 'paused' 103 | ProgressBarValue = value 104 | if (window.WebSetProgressBar) window.WebSetProgressBar({ pro: ProgressBarValue, mode: mode }) 105 | } else { 106 | 107 | setProgressBar() 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/utils/foot.ts: -------------------------------------------------------------------------------- 1 | const FootMap = new Map() 2 | 3 | export function FootLoading(msg: string, key: string) { 4 | console.log('FootLoading', key, msg) 5 | 6 | if (msg != '') FootMap.set(key, msg) 7 | else FootMap.delete(key) 8 | 9 | let info = '' 10 | FootMap.forEach(function (value, key) { 11 | const item = '' + value + '' 12 | if (info.includes(item) == false) info += item 13 | }) 14 | 15 | const doc = document.getElementById('footLoading') 16 | if (doc) { 17 | if (!info) { 18 | doc.innerHTML = '' 19 | } else { 20 | doc.innerHTML = 21 | '
' + 22 | info 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/utils/idhelper.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/src/utils/idhelper.ts -------------------------------------------------------------------------------- /src/utils/levemap.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import { message } from 'ant-design-vue' 3 | import DB from './db' 4 | 5 | interface Dir { 6 | file_id: string 7 | } 8 | 9 | // 1122334455667788 10 | 11 | // datamap 12 | 13 | // 11 14 | 15 | // 62ad76cd1f7dfdc28ad64c1eb6f22492a672a510 16 | function GetDirSize(file_id: string) { 17 | let obj = DirSizeDataMap 18 | let i = 0 19 | const maxi = file_id.length - 4 20 | while (i < maxi) { 21 | const key = file_id.substring(i, i + 4) 22 | obj = obj.get(key) 23 | if (!obj) return 0 24 | i += 4 25 | } 26 | 27 | return obj.get(file_id.substring(i)) || 0 28 | } 29 | 30 | let DirSizeDataMap: Map = new Map() 31 | function SetDirSizeMap(file_id: string, size: number) { 32 | let obj = DirSizeDataMap 33 | 34 | let i = 0 35 | const maxi = file_id.length - 8 36 | while (i < maxi) { 37 | const key = file_id.substring(i, i + 8) 38 | let find = obj.get(key) 39 | if (!find) { 40 | find = new Map() 41 | obj.set(key, find) 42 | } 43 | obj = find 44 | i += 8 45 | } 46 | 47 | obj.set(file_id.substring(i), size) 48 | } 49 | 50 | let DirSizeDataObj: { [key: string]: any } = Object.create(null) 51 | function SetDirSizeObject(file_id: string, size: number) { 52 | let obj = DirSizeDataObj 53 | 54 | let i = 0 55 | const maxi = file_id.length - 8 56 | while (i < maxi) { 57 | const key = file_id.substring(i, i + 8) 58 | let find = obj[key] 59 | if (!find) { 60 | find = Object.create(null) 61 | obj[key] = find 62 | } 63 | obj = find 64 | i += 8 65 | } 66 | 67 | obj[file_id.substring(i)] = size 68 | } 69 | 70 | export async function LoadObject() { 71 | console.time('LoadObject') 72 | window.openDatabase = {} 73 | const drive_id = '8699982' 74 | const jsonsize = await DB.getValueObject('DirFileSize_' + drive_id) 75 | window.openDatabase.sizemap = jsonsize ? (jsonsize as { [key: string]: number }) : {} 76 | // const jsonsizetime = await DB.getValueObject('DirFileSizeTime_' + drive_id) 77 | // window.openDatabase.sizetimemap = jsonsizetime ? (jsonsizetime as { [key: string]: number }) : {} 78 | console.timeEnd('LoadObject') 79 | message.success('LoadObject') 80 | } 81 | 82 | export async function CreatMap() { 83 | console.time('CreatMap') 84 | DirSizeDataMap = new Map() 85 | const sizemap = window.openDatabase.sizemap as { [key: string]: number } 86 | const keys = Object.keys(sizemap) 87 | for (let i = 0, maxi = keys.length; i < maxi; i++) { 88 | // SetDirSizeMap(keys[i], sizemap[keys[i]]) 89 | DirSizeDataMap.set(keys[i], [sizemap[keys[i]], 'size', true]) 90 | } 91 | window.openDatabase.DirSizeDataMap = DirSizeDataMap 92 | DirSizeDataMap = new Map() 93 | console.timeEnd('CreatMap') 94 | message.success('CreatMap') 95 | } 96 | 97 | export async function CreatObject() { 98 | console.time('CreatObject') 99 | DirSizeDataObj = Object.create(null) 100 | const sizemap = window.openDatabase.sizemap as { [key: string]: number } 101 | const keys = Object.keys(sizemap) 102 | for (let i = 0, maxi = keys.length; i < maxi; i++) { 103 | // SetDirSizeObject(keys[i], sizemap[keys[i]]) 104 | DirSizeDataObj[keys[i]] = [sizemap[keys[i]], 'size', true] 105 | } 106 | window.openDatabase.DirSizeDataObj = DirSizeDataObj 107 | DirSizeDataObj = Object.create(null) 108 | console.timeEnd('CreatObject') 109 | message.success('CreatObject') 110 | } 111 | -------------------------------------------------------------------------------- /src/utils/message.ts: -------------------------------------------------------------------------------- 1 | import { Message } from '@arco-design/web-vue' 2 | import { h } from 'vue' 3 | 4 | const MessageMap = new Map() 5 | function getCount(msg: string) { 6 | let count = MessageMap.get(msg) || 0 7 | count++ 8 | MessageMap.set(msg, count) 9 | return count 10 | } 11 | export default class message { 12 | static info(msg: string, duration: number = 3, key: string = '') { 13 | const count = getCount(key || msg) 14 | return Message.info({ 15 | id: key || msg, 16 | content: count > 1 ? () => h('div', { innerHTML: msg + '' + count + '' }) : msg, 17 | position: 'bottom', 18 | duration: duration * 1000, 19 | onClose: (id) => MessageMap.delete(key || msg) 20 | }) 21 | } 22 | 23 | static error(msg: string, duration: number = 3, key: string = '') { 24 | const count = getCount(key || msg) 25 | return Message.error({ 26 | id: key || msg, 27 | content: count > 1 ? () => h('div', { innerHTML: msg + '' + count + '' }) : msg, 28 | position: 'bottom', 29 | duration: duration * 1000, 30 | onClose: (id) => MessageMap.delete(key || msg) 31 | }) 32 | } 33 | 34 | static success(msg: string, duration: number = 3, key: string = '') { 35 | const count = getCount(key || msg) 36 | return Message.success({ 37 | id: key || msg, 38 | content: count > 1 ? () => h('div', { innerHTML: msg + '' + count + '' }) : msg, 39 | position: 'bottom', 40 | duration: duration == 0 ? 1 : duration * 1000, 41 | onClose: (id) => MessageMap.delete(key || msg) 42 | }) 43 | } 44 | 45 | static warning(msg: string, duration: number = 3, key: string = '') { 46 | const count = getCount(key || msg) 47 | return Message.warning({ 48 | id: key || msg, 49 | content: count > 1 ? () => h('div', { innerHTML: msg + '' + count + '' }) : msg, 50 | position: 'bottom', 51 | duration: duration * 1000, 52 | onClose: (id) => MessageMap.delete(key || msg) 53 | }) 54 | } 55 | 56 | static loading(msg: string, duration: number = 3, key: string = '') { 57 | const count = 0 58 | return Message.loading({ 59 | id: key || msg, 60 | content: count > 1 ? () => h('div', { innerHTML: msg + '' + count + '' }) : msg, 61 | position: 'bottom', 62 | duration: duration * 1000 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/utils/modal.ts: -------------------------------------------------------------------------------- 1 | import { IAliGetFileModel, IAliShareItem } from '../aliapi/alimodels' 2 | import { useModalStore } from '../store' 3 | 4 | export function modalCloseAll() { 5 | useModalStore().showModal('', {}) 6 | } 7 | 8 | export function modalUserSpace() { 9 | useModalStore().showModal('userspace', {}) 10 | } 11 | export function modalCreatNewFile() { 12 | useModalStore().showModal('creatfile', {}) 13 | } 14 | export function modalCreatNewDir(dirtype: string, parentdirid: string = '', callback: any = undefined) { 15 | useModalStore().showModal('creatdir', { dirtype, parentdirid, callback }) 16 | } 17 | 18 | export function modalCreatNewAlbum() { 19 | useModalStore().showModal('createalbum', { }) 20 | } 21 | 22 | export function modalMoveToAlbum(photos_file_id:string[]) { 23 | useModalStore().showModal('movetoalubm', { photos_file_id }) 24 | } 25 | 26 | export function modalCreatNewShareLink(sharetype: string, filelist: IAliGetFileModel[]) { 27 | useModalStore().showModal('creatshare', { sharetype, filelist }) 28 | } 29 | 30 | export function modalDaoRuShareLink() { 31 | useModalStore().showModal('daorushare', {}) 32 | } 33 | export function modalDaoRuShareLinkMulti() { 34 | useModalStore().showModal('daorusharemulti', {}) 35 | } 36 | 37 | export function modalRename(istree: boolean, ismulti: boolean) { 38 | useModalStore().showModal(ismulti ? 'renamemulti' : 'rename', { istree }) 39 | } 40 | 41 | export function modalEditShareLink(sharelist: IAliShareItem[]) { 42 | useModalStore().showModal('editshare', { sharelist }) 43 | } 44 | 45 | export function modalShowShareLink(share_id: string, share_pwd: string, share_token: string, withsave: boolean, file_id_list: string[]) { 46 | useModalStore().showModal('showshare', { share_id, share_pwd, share_token, withsave, file_id_list }) 47 | } 48 | 49 | export function modalSelectPanDir(selecttype: string, selectid: string, 50 | callback: (user_id: string, drive_id: string, dirID: string, dirName: string) => void, 51 | category?: string, 52 | extFilter?: RegExp) { 53 | useModalStore().showModal('selectpandir', { selecttype, selectid, category, extFilter, callback }) 54 | } 55 | 56 | export function modalShuXing(istree: boolean, ismulti: boolean) { 57 | ismulti = false 58 | useModalStore().showModal(ismulti ? 'shuxingmulti' : 'shuxing', { istree }) 59 | } 60 | 61 | export function modalSearchPan() { 62 | useModalStore().showModal('searchpan', {}) 63 | } 64 | 65 | export function modalDLNAPlayer() { 66 | useModalStore().showModal('dlna', {}) 67 | } 68 | export function modalM3U8Download() { 69 | useModalStore().showModal('m3u8download', {}) 70 | } 71 | 72 | export function modalCopyFileTree(filelist: IAliGetFileModel[]) { 73 | useModalStore().showModal('copyfiletree', { filelist }) 74 | } 75 | 76 | export function modalArchive(user_id: string, drive_id: string, file_id: string, file_name: string, parent_file_id: string, password: string) { 77 | useModalStore().showModal('archive', { user_id, drive_id, file_id, file_name, parent_file_id, password }) 78 | } 79 | 80 | export function modalArchivePassword(user_id: string, drive_id: string, file_id: string, file_name: string, parent_file_id: string, domain_id: string, ext: string) { 81 | useModalStore().showModal('archivepassword', { user_id, drive_id, file_id, file_name, parent_file_id, domain_id, ext }) 82 | } 83 | 84 | export function modalUpload(file_id: string, filelist: string[]) { 85 | useModalStore().showModal('upload', { file_id, filelist }) 86 | } 87 | 88 | export function modalDownload(istree: boolean) { 89 | useModalStore().showModal('download', { istree }) 90 | } 91 | -------------------------------------------------------------------------------- /src/utils/mosehelper.ts: -------------------------------------------------------------------------------- 1 | import { MouseMessage } from '../store/mousestore' 2 | 3 | export function TestButton(button: number, event: MouseMessage, fun: any): boolean { 4 | if (event.button == button && !event.Ctrl && !event.Shift && !event.Alt) { 5 | fun() 6 | return true 7 | } 8 | return false 9 | } 10 | 11 | export function TestButtonAlt(button: number, event: MouseMessage, fun: any): boolean { 12 | if (event.button == button && event.Alt) { 13 | fun() 14 | return true 15 | } 16 | return false 17 | } -------------------------------------------------------------------------------- /src/utils/sha1workerpool.ts: -------------------------------------------------------------------------------- 1 | const MAXSIZE = Math.max(2, navigator.hardwareConcurrency - 1) 2 | 3 | export interface Sha1Model { 4 | hash: string 5 | localFilePath: string 6 | access_token: string 7 | } 8 | 9 | export default class Sha1WorkerPool { 10 | queueWithCallback: [args: any, callback: (result: any, worker: Worker) => void, error: (err: any, worker: Worker) => void][] = [] 11 | freeWorkers: Worker[] = [] 12 | workers: Set = new Set() 13 | 14 | public Init() { 15 | if (this.workers.size > 0) return 16 | 17 | for (let i = 0; i < MAXSIZE; i++) { 18 | const worker = new Worker('./sha1filework.js') 19 | this.freeWorkers.push(worker) 20 | this.workers.add(worker) 21 | } 22 | } 23 | 24 | 25 | public StartWithCallback(args: Sha1Model, callback: (result: any, worker: Worker) => void, error: (err: any, worker: Worker) => void) { 26 | this.Init() 27 | if (this.freeWorkers.length > 0) { 28 | const worker = this.freeWorkers.pop()! 29 | worker.onmessage = (e: any) => { 30 | callback(e.data, worker) 31 | } 32 | worker.onerror = (e: any) => { 33 | error(e, worker) 34 | } 35 | worker.postMessage(args) 36 | } else { 37 | this.queueWithCallback.push([args, callback, error]) 38 | } 39 | } 40 | 41 | public FinishWithCallback(worker: Worker) { 42 | worker.onmessage = null 43 | worker.onerror = null 44 | this.freeWorkers.push(worker) 45 | if (this.queueWithCallback.length > 0) { 46 | const item = this.queueWithCallback.shift()! 47 | this.StartWithCallback(item[0], item[1], item[2]) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/shareurl.ts: -------------------------------------------------------------------------------- 1 | import { useSettingStore } from '../store' 2 | 3 | export function GetShareUrlFormate(share_name: string, share_url: string, share_pwd: string): string { 4 | let Formate = useSettingStore().uiShareFormate.replaceAll('\\n', '\n') 5 | const s1 = Formate.indexOf('URL') 6 | if (!share_pwd) { 7 | 8 | const s2 = Formate.indexOf('PWD') 9 | if (s1 >= 0 && s2 > s1) Formate = Formate.substring(0, s1 + 3) + Formate.substring(s2 + 3) 10 | console.log(Formate) 11 | } 12 | const url = Formate.replace('URL', share_url).replace('PWD', share_pwd).replace('NAME', share_name) 13 | if (url && s1 >= 0) return url 14 | return share_name + ' ' + share_url + (share_pwd ? ' 提取码:' + share_pwd : '') 15 | } 16 | 17 | export interface IID { 18 | id: string 19 | pwd: string 20 | } 21 | 22 | 23 | export function ParseShareIDList(txt: string): IID[] { 24 | txt = txt.replaceAll('密码', '提取码').replaceAll('password', '提取码').replaceAll('pwd', '提取码').replaceAll('PWD', '提取码') 25 | txt = txt.replaceAll('\n提取码', '提取码') 26 | const list: IID[] = [] 27 | txt.split('\n').map((t) => { 28 | const p = GetShareID(t) 29 | if (p.id) list.push(p) 30 | return true 31 | }) 32 | return list 33 | } 34 | 35 | export function ParseShareIDOne(txt: string): IID { 36 | txt = txt.replaceAll('密码', '提取码').replaceAll('password', '提取码').replaceAll('pwd', '提取码').replaceAll('PWD', '提取码') 37 | txt = txt.replaceAll('\n提取码', '提取码') 38 | return GetShareID(txt) 39 | } 40 | 41 | 42 | function GetShareID(txt: string): IID { 43 | const ret = { id: '', pwd: '' } 44 | const id = txt.match(/(?<=\/s\/)[0-9a-zA-Z]{11,12}/) 45 | if (id && id.length > 0) ret.id = id[0] 46 | const pwd = txt.match(/(?<=提取码[^0-9a-zA-Z]{0,6})[0-9a-zA-Z]{4}/) 47 | if (pwd && pwd.length > 0) ret.pwd = pwd[0] 48 | return ret 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import { deflateRawSync, inflateRawSync } from 'zlib' 2 | import crypto from 'crypto' 3 | 4 | export function ArrayCopyReverse(arr: any[]): any[] { 5 | const copy: any[] = [] 6 | for (let i = arr.length - 1; i >= 0; i--) { 7 | copy.push(arr[i]) 8 | } 9 | return copy 10 | } 11 | export function ArrayCopy(arr: any[]): any[] { 12 | const copy: any[] = [] 13 | for (let i = 0, maxi = arr.length; i < maxi; i++) { 14 | copy.push(arr[i]) 15 | } 16 | return copy 17 | } 18 | 19 | export function MapKeyToArray(map: Map): T[] { 20 | const arr: T[] = [] 21 | const keys = map.keys() 22 | for (let i = 0, maxi = map.size; i < maxi; i++) { 23 | arr.push(keys.next().value) 24 | } 25 | return arr 26 | } 27 | 28 | export function MapValueToArray(map: Map): T[] { 29 | const arr: T[] = [] 30 | const keys = map.values() 31 | for (let i = 0, maxi = map.size; i < maxi; i++) { 32 | arr.push(keys.next().value) 33 | } 34 | return arr 35 | } 36 | 37 | export function ArrayToMap(keyname: string, arr: T[]) { 38 | const map = new Map() 39 | let item: any 40 | for (let i = 0, maxi = arr.length; i < maxi; i++) { 41 | item = arr[i] 42 | map.set(item[keyname], item) 43 | } 44 | return map 45 | } 46 | 47 | export function ArrayKeyList(keyname: string, arr: any[]): T[] { 48 | const selectkeys: T[] = [] 49 | for (let i = 0, maxi = arr.length; i < maxi; i++) { 50 | selectkeys.push(arr[i][keyname]) 51 | } 52 | return selectkeys 53 | } 54 | 55 | export function BlobToString(body: Blob, encoding: string): Promise { 56 | return new Promise((resolve) => { 57 | const reader = new FileReader() 58 | reader.readAsText(body, encoding) 59 | reader.onload = function () { 60 | resolve((reader.result as string) || '') 61 | } 62 | }) 63 | } 64 | 65 | export function BlobToBuff(body: Blob): Promise { 66 | return new Promise((resolve) => { 67 | const reader = new FileReader() 68 | reader.readAsArrayBuffer(body) 69 | reader.onload = function () { 70 | resolve(reader.result as ArrayBuffer) 71 | } 72 | }) 73 | } 74 | 75 | export function GzipObject(input: object): Buffer { 76 | return deflateRawSync(JSON.stringify(input)) 77 | } 78 | 79 | export function UnGzipObject(input: Buffer): object { 80 | return JSON.parse(inflateRawSync(input).toString()) 81 | } 82 | 83 | export function HanToPin(input: string): string { 84 | if (!input) return '' 85 | // eslint-disable-next-line no-undef 86 | const arr = pinyinlite(input, { keepUnrecognized: true }) 87 | const strarr = new Array(arr.length * 2 + 1) 88 | let l = false 89 | for (let p = 1, i = 0, maxi = arr.length; i < maxi; p += 2, i++) { 90 | strarr[p] = arr[i].join(' ') 91 | l = strarr[p].length > 1 92 | if (l) { 93 | strarr[p - 1] = ' ' 94 | strarr[p + 1] = ' ' 95 | } else { 96 | strarr[p + 1] = '' 97 | } 98 | } 99 | strarr[0] = '' 100 | return strarr.join('') 101 | } 102 | 103 | 104 | export function GetOssExpires(downUrl: string) { 105 | if (!downUrl || !downUrl.includes('x-oss-expires=')) return 0 106 | try { 107 | 108 | let expires = downUrl.substring(downUrl.indexOf('x-oss-expires=') + 'x-oss-expires='.length) 109 | expires = expires.substring(0, expires.indexOf('&')) 110 | return parseInt(expires) - Math.floor(Date.now() / 1000) 111 | } catch { 112 | return 0 113 | } 114 | } 115 | 116 | export function hashCode(key: string) { 117 | let hash = 0 118 | for (let i = 0, maxi = key.length; i < maxi; i++) { 119 | hash = ((hash << 5) - hash + key.charCodeAt(i++)) << 0 120 | } 121 | return (hash >>> 0).toString(16).padStart(8, '0') 122 | } 123 | 124 | export function md5Code(key: string) { 125 | const buffa = Buffer.from(key) 126 | const md5a = crypto.createHash('md5').update(buffa).digest('hex') 127 | return md5a 128 | } 129 | -------------------------------------------------------------------------------- /src/utils/worker.ts: -------------------------------------------------------------------------------- 1 | export async function WorkerUploadFiles(ingoredList: string[], user_id: string, drive_id: string, parent_file_id: string, files: string[]) { 2 | let worker: any 3 | let result: any 4 | await new Promise((resolve) => { 5 | worker = new Worker('uploadfilesworker.js') 6 | worker.addEventListener('message', (event: any) => { 7 | if (event.data.state == 'success') { 8 | console.log(event) 9 | result = event.data.result 10 | resolve() 11 | } 12 | }) 13 | worker.addEventListener('error', function (event: any) { 14 | resolve() 15 | }) 16 | worker.postMessage({ ingoredList, user_id, drive_id, parent_file_id, files }) 17 | }) 18 | .catch(() => {}) 19 | .then(() => { 20 | if (worker) worker.terminate() 21 | }) 22 | return result 23 | } 24 | -------------------------------------------------------------------------------- /src/workerpage/uidownload.ts: -------------------------------------------------------------------------------- 1 | export function DownloadTrigger() { 2 | 3 | 4 | 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/workerpage/workercmd.ts: -------------------------------------------------------------------------------- 1 | import AliDirList from '../aliapi/dirlist' 2 | import { useSettingStore } from '../store' 3 | import TreeStore from '../store/treestore' 4 | import UserDAL from '../user/userdal' 5 | import DebugLog from '../utils/debuglog' 6 | import { DownloadTrigger } from './uidownload' 7 | import { UploadAdd, UploadCmd, UploadReport } from './uiupload' 8 | 9 | // eslint-disable-next-line no-unused-vars 10 | let workerTimer: any 11 | export function WorkerPage(type: string) { 12 | if (window.WinMsg) return 13 | 14 | if (type == 'upload') { 15 | window.WinMsg = WinMsgUpload 16 | const func = () => { 17 | try { 18 | UploadReport().catch() 19 | } catch {} 20 | workerTimer = setTimeout(func, 1000) 21 | } 22 | workerTimer = setTimeout(func, 6000) 23 | const element = document.createElement('div') 24 | element.innerHTML = '

上传进程

' 25 | document.body.append(element) 26 | } 27 | if (type == 'download') { 28 | window.WinMsg = WinMsgDownload 29 | const func = () => { 30 | try { 31 | DownloadTrigger() 32 | } catch {} 33 | workerTimer = setTimeout(func, 1000) 34 | } 35 | workerTimer = setTimeout(func, 6000) 36 | const element = document.createElement('div') 37 | element.innerHTML = '

下载进程

' 38 | document.body.append(element) 39 | } 40 | } 41 | const AllDirLock = new Map() 42 | export const WinMsgUpload = function (arg: any) { 43 | // console.log(arg) 44 | try { 45 | if (arg.cmd == 'SettingRefresh') { 46 | useSettingStore().$reset() 47 | } else if (arg.cmd == 'ClearUserToken') { 48 | UserDAL.ClearUserTokenMap() 49 | } else if (arg.cmd == 'UploadAdd') { 50 | UploadAdd(arg.UploadList) 51 | } else if (arg.cmd == 'UploadCmd') { 52 | UploadCmd(arg.Command, arg.IsAll, arg.UploadIDList, arg.TaskIDList) 53 | } else if (arg.cmd == 'AllDirList') { 54 | LoadAllDirList(arg.user_id, arg.drive_id) 55 | } 56 | } catch {} 57 | } 58 | 59 | function LoadAllDirList(user_id: string, drive_id: string): void { 60 | console.time('AllDirList') 61 | const lock = AllDirLock.get(drive_id) || 0 62 | const time = Date.now() / 1000 63 | console.log('AllDirList', 'lock=', lock, 'time=', time) 64 | if (lock) { 65 | if (time - lock < 300) { 66 | console.log('AllDirList Break') 67 | window.WinMsgToMain({ cmd: 'MainSaveAllDir', OneDriver: undefined, ErrorMessage: 'time' }) 68 | return 69 | } 70 | } 71 | AllDirLock.set(drive_id, time) 72 | AliDirList.ApiFastAllDirListByPID(user_id, drive_id) 73 | .then((data) => { 74 | console.timeEnd('AllDirList') 75 | AllDirLock.delete(drive_id) 76 | if (!data.next_marker) { 77 | TreeStore.ConvertToOneDriver(drive_id, data.items, true, false).then((one) => { 78 | window.WinMsgToMain({ cmd: 'MainSaveAllDir', OneDriver: one, ErrorMessage: '' }) 79 | }) 80 | } else { 81 | DebugLog.mSaveWarning('列出文件夹失败file_id=all' + ' next_marker=' + data.next_marker) 82 | window.WinMsgToMain({ cmd: 'MainSaveAllDir', OneDriver: undefined, ErrorMessage: data.next_marker }) 83 | } 84 | }) 85 | .catch((err: any) => { 86 | DebugLog.mSaveWarning('列出文件夹失败file_id=all', err) 87 | window.WinMsgToMain({ cmd: 'MainSaveAllDir', OneDriver: undefined, ErrorMessage: err.message || '未知错误' }) 88 | }) 89 | } 90 | 91 | export const WinMsgDownload = function (arg: any) { 92 | // console.log(arg) 93 | try { 94 | if (arg.cmd == 'SettingRefresh') { 95 | useSettingStore().$reset() 96 | } else if (arg.cmd == 'ClearUserToken') { 97 | UserDAL.ClearUserTokenMap() 98 | } 99 | } catch {} 100 | } 101 | -------------------------------------------------------------------------------- /static/crx/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /static/crx/devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.network.onRequestFinished.addListener(function (detail) { 2 | let url = detail.request.url; 3 | 4 | let isbreak = false; 5 | if (url.indexOf("api.aliyundrive.com") > 0) isbreak = true; /** 跳过api */ 6 | if (url.indexOf("img.aliyundrive.com") > 0) isbreak = true; /** 跳过img */ 7 | if (url.indexOf("_tmd_") > 0) isbreak = true; /** 跳过滑动验证 */ 8 | if (url.indexOf(".aliyuncs.com") > 0) isbreak = true; /** 跳过滑动验证 */ 9 | if (url.indexOf(".aliyun.com") > 0) isbreak = true; /** 跳过滑动验证 */ 10 | if (url.indexOf(".taobao.com") > 0) isbreak = true; /** 跳过滑动验证 */ 11 | if (url.indexOf(".mmstat.com") > 0) isbreak = true; /** 跳过日志 */ 12 | 13 | if (url.indexOf(".aliyundrive.com") < 0) isbreak = true; /** 跳过无效域名 */ 14 | if (isbreak) return; 15 | 16 | detail.getContent(function (content, mimeType) { 17 | try { 18 | if (typeof content == "string" && content.indexOf('"bizExt"') > 0) { 19 | let bizExt = ""; 20 | try { 21 | /** https://passport.aliyundrive.com/newlogin/login.do?appName=aliyun_drive&fromSite=52&_bx-v=2.0.31 */ 22 | const data = JSON.parse(content); 23 | bizExt = data.content?.data?.bizExt || ""; 24 | } catch (e) { 25 | bizExt = ""; 26 | chrome.devtools.inspectedWindow.eval( 27 | "console.log('" + JSON.stringify({ url, e, content }) + "')" 28 | ); 29 | } 30 | 31 | if (!bizExt) { 32 | /** https://passport.aliyundrive.com/newlogin/safe/ivCheckLogin.htm?havana_iv_token=... 二次短信验证 */ 33 | try { 34 | let temp = content.substring( 35 | content.indexOf('"bizExt"') + '"bizExt"'.length 36 | ); 37 | temp = temp.substring(temp.indexOf('"') + 1); // :"eyJ...", 38 | temp = temp.substring(0, temp.indexOf('"')); //eyJ... 39 | 40 | if (temp.startsWith("eyJ")) bizExt = temp; 41 | } catch (e) { 42 | bizExt = ""; 43 | chrome.devtools.inspectedWindow.eval( 44 | "console.log('" + JSON.stringify({ url, e, content }) + "')" 45 | ); 46 | } 47 | } 48 | 49 | if (bizExt) { 50 | chrome.devtools.inspectedWindow.eval( 51 | "console.log('" + JSON.stringify({ bizExt: bizExt }) + "')" 52 | ); 53 | } 54 | } 55 | } catch {} 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /static/crx/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "demo", 4 | "version": "1.0.0", 5 | "description": "demo", 6 | "devtools_page": "devtools.html", 7 | "host_permissions": [ 8 | "http://*/*", 9 | "https://*/*" 10 | ] 11 | } -------------------------------------------------------------------------------- /static/engine/aria2.conf: -------------------------------------------------------------------------------- 1 | # debug, info, notice, warn or error 2 | log-level=error 3 | file-allocation=trunc 4 | user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36 5 | max-concurrent-downloads=64 6 | max-connection-per-server=16 7 | enable-rpc=true 8 | rpc-allow-origin-all=true 9 | rpc-listen-all=false 10 | rpc-secret=S4znWTaZYQi3cpRNb 11 | rpc-secure=false 12 | pause-metadata=true 13 | http-no-cache=true 14 | disk-cache=64M 15 | no-file-allocation-limit=64M 16 | remote-time=true 17 | summary-interval=0 18 | content-disposition-default-utf8=true 19 | continue=true 20 | allow-overwrite=true 21 | auto-file-renaming=false 22 | max-download-result=1000 23 | no-netrc=true 24 | reuse-uri=true 25 | quiet=true 26 | disable-ipv6=false 27 | check-certificate=false 28 | save-session= 29 | save-session-interval=0 30 | follow-metalink=false 31 | follow-torrent=false 32 | enable-dht=false 33 | enable-dht6=false 34 | bt-enable-lpd=false 35 | enable-peer-exchange=false 36 | bt-request-peer-speed-limit=1 37 | dht-file-path= 38 | dht-file-path6= 39 | seed-time=0 40 | force-save=false 41 | bt-save-metadata=false -------------------------------------------------------------------------------- /static/engine/darwin/arm64/aria2c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/darwin/arm64/aria2c -------------------------------------------------------------------------------- /static/engine/darwin/x64/aria2c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/darwin/x64/aria2c -------------------------------------------------------------------------------- /static/engine/linux/arm64/aria2c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/linux/arm64/aria2c -------------------------------------------------------------------------------- /static/engine/linux/armv7l/aria2c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/linux/armv7l/aria2c -------------------------------------------------------------------------------- /static/engine/linux/x64/aria2c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/linux/x64/aria2c -------------------------------------------------------------------------------- /static/engine/win32/ia32/aria2c.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/win32/ia32/aria2c.exe -------------------------------------------------------------------------------- /static/engine/win32/x64/aria2c.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/engine/win32/x64/aria2c.exe -------------------------------------------------------------------------------- /static/images/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/images/icon.icns -------------------------------------------------------------------------------- /static/images/icon_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/images/icon_24.png -------------------------------------------------------------------------------- /static/images/icon_256.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/images/icon_256.ico -------------------------------------------------------------------------------- /static/images/icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/images/icon_256.png -------------------------------------------------------------------------------- /static/images/icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaozhangmin/aliyunpan/6fc8b1f9c5fa2bccc84143d4bd68d2b2b4c0c757/static/images/icon_64.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "importHelpers": true, 7 | "jsx": "preserve", 8 | "esModuleInterop": true, 9 | "resolveJsonModule": true, 10 | "sourceMap": false, 11 | "allowJs": true, 12 | "baseUrl": "./", 13 | "strict": true, 14 | "allowSyntheticDefaultImports": true, 15 | "skipLibCheck": true 16 | }, 17 | "include": ["src"], 18 | "references": [ 19 | { "path": "./tsconfig.node.json" } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "composite": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "jsx": "preserve", 8 | "resolveJsonModule": true, 9 | "allowSyntheticDefaultImports": true, 10 | "types": [ 11 | "vite-plugin-electron/electron-env" 12 | ] 13 | }, 14 | "include": ["package.json", "electron"] 15 | } 16 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare namespace NodeJS { 3 | interface ProcessEnv { 4 | NODE_ENV: 'development' | 'production' 5 | readonly VITE_DEV_SERVER_HOST: string 6 | readonly VITE_DEV_SERVER_PORT: string 7 | } 8 | } 9 | 10 | declare interface Window { 11 | Electron: any 12 | platform: any 13 | WebToElectron: any 14 | WebToElectronCB: any 15 | WebSpawnSync: any 16 | WebExecSync: any 17 | WebShowOpenDialogSync: any 18 | WebShowSaveDialogSync: any 19 | WebShowItemInFolder: any 20 | WebPlatformSync: any 21 | WebClearCookies: any 22 | WebClearCache: any 23 | WebSaveTheme: any 24 | WebUserToken: any 25 | WebReload: any 26 | WebRelaunch: any 27 | WebRelaunchAria: () => Promise 28 | WebSetProgressBar: any 29 | WebSetCookies: any 30 | WebOpenWindow: any 31 | WebShutDown: any 32 | openDatabase: any 33 | loginfn: any 34 | postdataFunc: any 35 | Prism: any 36 | winmain: number 37 | winworker: number 38 | WinMsg: any 39 | WinMsgToMain: any 40 | WinMsgToUI: any 41 | getDvaApp: any 42 | dark: boolean 43 | test: any 44 | } 45 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { rmSync } from 'fs' 2 | import path from 'path' 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | import electron, { onstart } from 'vite-plugin-electron' 6 | import pkg from './package.json' 7 | 8 | rmSync('dist', { recursive: true, force: true }) // v14.14.0 9 | 10 | // https://vitejs.dev/config/ 11 | export default defineConfig({ 12 | build: { 13 | rollupOptions: { 14 | output: { 15 | chunkFileNames: '[name].js', 16 | entryFileNames: '[name].js', 17 | assetFileNames: '[name].[ext]', 18 | } 19 | } 20 | }, 21 | plugins: [ 22 | vue({ 23 | template:{ 24 | compilerOptions:{ 25 | isCustomElement: tag => tag == 'Webview' 26 | } 27 | } 28 | }), 29 | electron({ 30 | main: { 31 | entry: 'electron/main/index.ts', 32 | vite: { 33 | build: { 34 | // For Debug 35 | // sourcemap: true, 36 | outDir: 'dist/electron/main', 37 | }, 38 | // Will start Electron via VSCode Debug 39 | plugins: [process.env.VSCODE_DEBUG ? onstart() : null], 40 | }, 41 | }, 42 | preload: { 43 | input: { 44 | // You can configure multiple preload here 45 | index: path.join(__dirname, 'electron/preload/index.ts'), 46 | }, 47 | vite: { 48 | build: { 49 | // For Debug 50 | // sourcemap: 'inline', 51 | outDir: 'dist/electron/preload', 52 | }, 53 | }, 54 | }, 55 | // Enables use of Node.js API in the Renderer-process 56 | // https://github.com/electron-vite/vite-plugin-electron/tree/main/packages/electron-renderer#electron-renderervite-serve 57 | renderer: {}, 58 | }), 59 | ], 60 | server: process.env.VSCODE_DEBUG ? { 61 | host: pkg.debug.env.VITE_DEV_SERVER_HOSTNAME, 62 | port: pkg.debug.env.VITE_DEV_SERVER_PORT, 63 | } : undefined, 64 | }) 65 | -------------------------------------------------------------------------------- /源码开发打包帮助.md: -------------------------------------------------------------------------------- 1 | ### 小白羊v3版本源码帮助 2 | 3 | v3采用 ts + vue3 + vite + electron 模板开发 4 | 5 | #### 1.下载源代码 6 | 7 | ``` 8 | https://github.com/gaozhangmin/aliyunpan 9 | ``` 10 | 11 | #### 2.打开代码目录,安装依赖 12 | 13 | ```cmd 14 | yarn install 15 | ``` 16 | 17 | #### 3.开发调试运行 18 | 19 | ```cmd 20 | yarn run dev 21 | ``` 22 | 23 | 执行命令后会调起electron窗口,配合vscode正常开发调试即可 24 | 25 | #### 4.打包发布 26 | 27 | ```cmd 28 | yarn run build 29 | ``` 30 | 31 | 执行命令后会生成`release\win-unpacked\resources\app.asar`文件 32 | 把该文件复制到任意electron版本的`resources`目录下即可打包出安装包 33 | --------------------------------------------------------------------------------