├── .gitignore
├── .prettierrc
├── 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
├── app.ico
├── app.png
├── changelog.txt
├── crx
├── devtools.html
├── devtools.js
└── manifest.json
├── 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
├── comlink.js
├── favicon.ico
├── font
│ └── VideoJS.woff
├── iconfont.css
├── iconfont.woff2
├── imgerror.png
├── lang
│ ├── en.js
│ └── zh-CN.js
├── loading.png
├── main.html
├── main2.html
├── notify.wav
├── pinyinlite_full.min.js
├── prism-vsc-dark-plus.css
├── prism.js
├── sha1filework.js
├── silvermine-videojs-quality-selector.css
├── silvermine-videojs-quality-selector.min.js
├── userface.png
├── video-js.min.css
├── video.min.js
├── wasm.wasm
└── wasm_exec.js
├── src
├── App.vue
├── aliapi
│ ├── 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
│ ├── uploaddisk.ts
│ ├── uploadhash.ts
│ ├── uploadhashpool.ts
│ ├── uploadmem.ts
│ ├── user.ts
│ └── utils.ts
├── assets
│ ├── antd.css
│ ├── fileitem.css
│ └── global.css
├── down
│ ├── DownDAL.ts
│ ├── DownDowned.vue
│ ├── DownDowning.vue
│ ├── DownM3U8.vue
│ ├── DownSync.vue
│ ├── DownUploaded.vue
│ ├── DownUploading.vue
│ ├── DownedStore.ts
│ ├── DowningStore.ts
│ ├── Index.vue
│ ├── UploadedStore.ts
│ ├── UploadingStore.ts
│ └── downmenu.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.vue
│ ├── PageOffice.vue
│ ├── PageVideo.vue
│ ├── PageVideoXBT.vue
│ ├── PageWorker.vue
│ └── pagemain.ts
├── 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
│ │ ├── CreatNewDirModal.vue
│ │ ├── CreatNewDirMultiModal.vue
│ │ ├── CreatNewFileModal.vue
│ │ ├── CreatNewShareLinkModal.vue
│ │ ├── DLNAPlayerModal.vue
│ │ ├── DaoRuShareLinkModal.vue
│ │ ├── DaoRuShareLinkMultiModal.vue
│ │ ├── DownloadModal.vue
│ │ ├── M3U8DownloadModal.vue
│ │ ├── RenameModal.vue
│ │ ├── RenameMultiModal.vue
│ │ ├── SearchPanModal.vue
│ │ ├── SelectPanDirModal.vue
│ │ ├── ShuXingModal.vue
│ │ ├── ShuXingMultiModal.vue
│ │ ├── UploadModal.vue
│ │ ├── renamemulti.ts
│ │ └── topbtn.ts
├── pic
│ ├── PicLeft.vue
│ ├── PicRight.vue
│ └── index.vue
├── 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
│ ├── 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
│ ├── serverstore.ts
│ ├── treestore.ts
│ └── winstore.ts
├── transfer
│ ├── uploaddal.ts
│ ├── uploadingdal.ts
│ └── uploadingdata.ts
├── user
│ ├── UserInfo.vue
│ ├── UserLogin.vue
│ ├── UserSpaceModal.vue
│ ├── userdal.ts
│ └── userstore.ts
├── utils
│ ├── antdtree.ts
│ ├── appcache.ts
│ ├── aria2c.ts
│ ├── aria2c.ts.bak
│ ├── 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
│ ├── openfile.ts
│ ├── selecthelper.ts
│ ├── sha1workerpool.ts
│ ├── shareurl.ts
│ ├── utils.ts
│ └── worker.ts
└── workerpage
│ ├── uidownload.ts
│ ├── uiupload.ts
│ ├── uploader.ts
│ └── workercmd.ts
├── tsconfig.json
├── tsconfig.node.json
├── types.d.ts
├── vite.config.ts
├── yarn.lock
└── 源码开发打包帮助.md
/.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 | aria2.conf
13 | aria2c.exe
14 |
15 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 阿里云盘小白羊版
2 |
3 | #### 项目说明
4 |
5 | 原阿里云盘小白羊版v3的延续版
6 |
7 | 基于阿里云盘网页版开发的PC客户端,支持win7-11,macOS,linux
8 |
9 | > **08.25:[v3开发人员测试版](https://github.com/PingKuNet/aliyunpan/releases/tag/v3.8.25.alpha),追加上传,优化打包**
10 |
11 | > **04.14:[v2.12.14版已发布](https://github.com/liupan1890/aliyunpan/issues/639),适配官网升级**
12 |
13 | > **2022-01-02:在憋大招,耐心等待v3版**
14 |
15 |
16 | v1.6.29:[https://wwe.lanzoui.com/b01npsg8h](https://wwe.lanzoui.com/b01npsg8h)
17 |
18 | v2.12.14:[https://wwe.lanzoui.com/b01nqc4gd](https://wwe.lanzoui.com/b01nqc4gd)
19 |
20 | MacOS:[https://www.macwk.com/soft/aliyun-drive-xiaobaiyang](https://www.macwk.com/soft/aliyun-drive-xiaobaiyang)
21 |
22 | Mac版由macwk.com使用自有签名打包dmg,可以简单点击安装了(不需要输入终端命令),推荐下载此版本,已测MacOS10.12-11.4,兼容M1
23 |
24 |
25 | 已经发布在小众软件发现频道,大爱小众[meta.appinn.net](https://meta.appinn.net)
26 |
27 |
28 |
29 | 已发布了使用帮助文档 [https://www.yuque.com/liupan1890/xiaobaiyang](https://www.yuque.com/liupan1890/xiaobaiyang)
30 |
31 | ``````
32 | 2021年11月28日 已完成功能:
33 | 多账号登录、常用文件操作(新建文件夹、收藏、重命名、复制、移动、删除、详情、视频雪碧图)、
34 | 在线播放原始视频、在线播放转码视频、在线预览图片、在线预览文本、在线预览 word/excel/ppt/pdf、
35 | 连接到远程 Aria2 下载、上传文件、上传文件夹、批量改名、在线解压、回收站、收藏夹、
36 | 分享文件、导入阿里云分享链接、缩略图列表、网盘内文件搜索、视频文件洗码
37 |
38 | 等待完成的功能:
39 | 相册功能、网盘和相册间文件互相复制、文件同步盘、重复文件扫描、帐号间文件复制
40 | ``````
41 |
42 |
43 |
44 | #
45 |
46 | 
47 |
48 | #
49 |
50 | #### 为什么要用小白羊?
51 |
52 | #### 一:因为更快
53 |
54 | ##### 上传和下载4.4万个json格式小文件(共24GB):
55 |
56 | | 程序 | 总用时 | 用时基准 |
57 | | --- | ---: | ---: |
58 | | 上传&小白羊版 v2.10 | 24分钟 | :zap:58% |
59 | | 上传&PC客户端 v2.2.6 | 41分钟 | 100% |
60 | | ... | | | | |
61 | | 下载&小白羊版 v2.10 | 25分钟 | :zap:42% |
62 | | 下载&PC客户端 v2.2.6 | 59分钟 | 100% |
63 |
64 |
65 | ##### 上传和下载33个大文件(共90GB):
66 |
67 | | 程序 | 总用时 | 用时基准 |
68 | | --- | ---: | ---: |
69 | | 上传&小白羊版 v2.10 | 1分10秒 | :zap:44% |
70 | | 上传&PC客户端 v2.2.6 | 2分40秒 | 100% |
71 | | ... | | | | |
72 | | 下载&小白羊版 v2.10 | 38分钟 | :zap:52% |
73 | | 下载&PC客户端 v2.2.6 | 72分钟 | 100% |
74 |
75 |
76 |
77 | 详情参阅 :[v2.10.19性能测试](https://github.com/liupan1890/aliyunpan/blob/main/v2.10.19%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.md) 的性能测试文档
78 |
79 | #### 二:因为更好
80 |
81 | 小白羊支持同时登录多个账号管理
82 |
83 | 小白羊特有文件夹树,可以快速方便的操作
84 |
85 | 小白羊支持直接在线播放网盘里的各种格式的视频并且是高清原画,支持外挂字幕/音轨/播放速度调整,比官方的格式更多更清晰
86 |
87 | 小白羊可以显示文件夹体积,可以文件夹和文件混合排序(文件名/体积/时间),并且文件名排序时更准确!
88 |
89 | 小白羊可以通过远程Aria2功能把文件直接下载到远程的VPS/NAS上
90 |
91 | 小白羊可以批量的对 大量文件/多层嵌套的文件夹 一键重命名
92 |
93 | 小白羊可以快速复制文件,可以直接预览视频的雪碧图,可以直接删除文件
94 |
95 | 小白羊支持数万文件夹和数万文件的管理,支持一次性列出文件夹里包含的全部文件
96 |
97 | 小白羊支持单次上传/下载 一百万 量级的文件/文件夹
98 |
99 | 小白羊仍在努力开发新功能,让大家使用起来更方便!
100 |
101 | #
102 |
103 | #### 常见问题请参阅帮助文档
104 |
105 |
106 | #### 特别感谢 @jkqxl @iD2073 @ybbluesky 等为小白羊提供了大量的优化建议
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/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/book.md:
--------------------------------------------------------------------------------
1 | #### 列出图书
2 |
3 | POST: `https://api.aliyundrive.com/adrive/v2/book/list`
4 |
5 | ```json
6 | { "book_progress_type": "ALL", "limit": 1, "marker": "", "order_by": "name asc", "show_hidden": false }
7 | ```
8 |
9 | Response:
10 |
11 | ```json
12 | {
13 | "items": [
14 | {
15 | "category": "doc",
16 | "content_hash": "4DBF0000000023E6E756C29AF6AC487217921D53",
17 | "content_hash_name": "sha1",
18 | "content_type": "application/oct-stream",
19 | "crc64_hash": "1548000000008183211",
20 | "created_at": "2021-11-22T03:36:19.680Z",
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": "epub",
26 | "file_id": "623b00000000d89ef21d4118838aed83de7575ba",
27 | "hidden": false,
28 | "name": "Republic.epub",
29 | "parent_file_id": "613800000000336ae9164455b135a9729a298c9c",
30 | "punish_flag": 0,
31 | "size": 128000,
32 | "starred": false,
33 | "status": "available",
34 | "type": "file",
35 | "updated_at": "2022-01-12T12:44:16.835Z",
36 | "upload_id": "ED12000000004724833D47B5D5D3C8B9",
37 | "url": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...",
38 | "user_meta": "{\"client\":\"web\"}",
39 | "user_tags": { "book_show": "true" }
40 | }
41 | ],
42 | "next_marker": ""
43 | }
44 | ```
45 |
46 | #### 列出最近阅读的图书
47 |
48 | POST: `https://api.aliyundrive.com/adrive/v2/book/recentList`
49 |
50 | ```json
51 | { "book_progress_type": "ALL", "limit": 1, "marker": "", "order_by": "name asc", "show_hidden": false }
52 | ```
53 |
54 | Response:
55 |
56 | ```json
57 | {
58 | "items": [
59 | {
60 | "category": "doc",
61 | "content_hash": "4DBF0000000023E6E756C29AF6AC487217921D53",
62 | "content_hash_name": "sha1",
63 | "content_type": "application/oct-stream",
64 | "crc64_hash": "1548000000008183211",
65 | "created_at": "2021-09-24T12:50:00.905Z",
66 | "domain_id": "bj29",
67 | "download_url": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...",
68 | "drive_id": "9600002",
69 | "encrypt_mode": "none",
70 | "file_extension": "epub",
71 | "file_id": "623b00000000d89ef21d4118838aed83de7575ba",
72 | "hidden": false,
73 | "name": "无声告白.epub",
74 | "parent_file_id": "613800000000336ae9164455b135a9729a298c9c",
75 | "punish_flag": 0,
76 | "size": 1027423,
77 | "starred": false,
78 | "status": "available",
79 | "type": "file",
80 | "updated_at": "2022-03-22T14:59:12.333Z",
81 | "url": "https://bj29.cn-beijing.data.alicloudccp.com/2GhCur3G%2F...",
82 | "book_progress": "{\"bookName\":\"无声告白\",\"chapterNumber\":1,\"chapterPercent\":0.0,\"currentPageNumber\":-1,\"progress\":29,\"totalPageNumber\":-1,\"uid\":\"9400000000bc480bbcbbb1e074f55a7f\",\"updateTime\":1647961151874}",
83 | "user_tags": {
84 | "book_progress": "{\"bookName\":\"无声告白\",\"chapterNumber\":1,\"chapterPercent\":0.0,\"currentPageNumber\":-1,\"progress\":29,\"totalPageNumber\":-1,\"uid\":\"9400000000bc480bbcbbb1e074f55a7f\",\"updateTime\":1647961151874}",
85 | "book_progress_percentage": "35",
86 | "book_show": "true",
87 | "epub_book_progress": "{\"uid\":\"9400000000bc480bbcbbb1e074f55a7f\",\"href\":\"\\/text\\/part0000.html\",\"type\":\"application\\/xhtml+xml\",\"locations\":{\"progression\":0,\"position\":2,\"totalProgression\":8.873114463176575E-4}}",
88 | "start_read": "true"
89 | },
90 | "book_name": "无声告白",
91 | "book_progress_percentage": 29
92 | }
93 | ]
94 | }
95 | ```
96 |
97 | #### 添加到阅读室
98 |
99 | POST: `https://api.aliyundrive.com/adrive/v2/book/update`
100 |
101 | ```json
102 | { "file_ids": ["623b00000000d89ef21d4118838aed83de7575ba"], "operation": 1 }
103 | ```
104 |
105 | Response:
106 |
107 | ```text
108 | HTTP/1.1 200 OK
109 | ```
110 |
111 | #### 从阅读室移除
112 |
113 | POST: `https://api.aliyundrive.com/adrive/v2/book/update`
114 |
115 | ```json
116 | { "file_ids": ["623b00000000d89ef21d4118838aed83de7575ba"], "operation": 2 }
117 | ```
118 |
119 | Response:
120 |
121 | ```text
122 | HTTP/1.1 200 OK
123 | ```
124 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/app.ico
--------------------------------------------------------------------------------
/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/app.png
--------------------------------------------------------------------------------
/crx/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/electron-builder.json:
--------------------------------------------------------------------------------
1 | {
2 | "appId": "YouAppID",
3 | "asar": true,
4 | "directories": {
5 | "output": "release"
6 | },
7 | "files": [
8 | "dist"
9 | ],
10 | "mac": {
11 | "artifactName": "${productName}_${version}.${ext}",
12 | "target": [
13 | "dmg"
14 | ]
15 | },
16 | "win": {
17 | "target": [
18 | {
19 | "target": "nsis",
20 | "arch": [
21 | "x64"
22 | ]
23 | }
24 | ],
25 | "artifactName": "${productName}_${version}.${ext}"
26 | },
27 | "nsis": {
28 | "oneClick": false,
29 | "perMachine": false,
30 | "allowToChangeInstallationDirectory": true,
31 | "deleteAppDataOnUninstall": false
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/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 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/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": "alixby",
3 | "version": "3.09.13",
4 | "main": "dist/electron/main/index.js",
5 | "author": "",
6 | "license": "",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vue-tsc --noEmit && vite build && electron-builder"
10 | },
11 | "engines": {
12 | "node": ">=14.17.0"
13 | },
14 | "dependencies": {},
15 | "devDependencies": {
16 | "@arco-design/web-vue": "^2.36.0",
17 | "@electron/remote": "^2.0.8",
18 | "@types/lodash": "^4.14.184",
19 | "@types/node": "^17.0.45",
20 | "@vitejs/plugin-vue": "^3.1.0",
21 | "@vitejs/plugin-vue-jsx": "^2.0.1",
22 | "ant-design-vue": "^3.2.12",
23 | "aria2-lib": "1.0.1",
24 | "axios": "^0.27.2",
25 | "dayjs": "^1.11.5",
26 | "dexie": "^3.2.2",
27 | "dom-to-image": "^2.6.0",
28 | "electron": "^20.1.1",
29 | "electron-builder": "^23.3.3",
30 | "fuzzysort": "^2.0.1",
31 | "isomorphic-fetch": "^3.0.0",
32 | "jschardet": "^3.0.0",
33 | "lodash": "^4.17.21",
34 | "pinia": "^2.0.22",
35 | "socks-proxy-agent": "^7.0.0",
36 | "terser": "^5.15.0",
37 | "typescript": "^4.8.2",
38 | "unplugin-vue-components": "^0.22.4",
39 | "viewerjs": "^1.10.5",
40 | "vite": "^3.1.0",
41 | "vite-plugin-electron": "^0.9.2",
42 | "vite-plugin-resolve": "^2.1.2",
43 | "vue": "^3.2.38",
44 | "vue-tsc": "^0.40.10"
45 | },
46 | "debug": {
47 | "env": {
48 | "VITE_DEV_SERVER_HOSTNAME": "127.0.0.1",
49 | "VITE_DEV_SERVER_PORT": 3344,
50 | "VITE_DEV_SERVER_URL": "http://127.0.0.1:3344"
51 | }
52 | },
53 | "keywords": [
54 | "electron",
55 | "rollup",
56 | "vite",
57 | "vue3",
58 | "vue"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/favicon.ico
--------------------------------------------------------------------------------
/public/font/VideoJS.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/font/VideoJS.woff
--------------------------------------------------------------------------------
/public/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/iconfont.woff2
--------------------------------------------------------------------------------
/public/imgerror.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/imgerror.png
--------------------------------------------------------------------------------
/public/lang/en.js:
--------------------------------------------------------------------------------
1 | videojs.addLanguage('en', {
2 | "Audio Player": "Audio Player",
3 | "Video Player": "Video Player",
4 | "Play": "Play",
5 | "Pause": "Pause",
6 | "Replay": "Replay",
7 | "Current Time": "Current Time",
8 | "Duration": "Duration",
9 | "Remaining Time": "Remaining Time",
10 | "Stream Type": "Stream Type",
11 | "LIVE": "LIVE",
12 | "Seek to live, currently behind live": "Seek to live, currently behind live",
13 | "Seek to live, currently playing live": "Seek to live, currently playing live",
14 | "Loaded": "Loaded",
15 | "Progress": "Progress",
16 | "Progress Bar": "Progress Bar",
17 | "progress bar timing: currentTime={1} duration={2}": "{1} of {2}",
18 | "Fullscreen": "Fullscreen",
19 | "Non-Fullscreen": "Exit Fullscreen",
20 | "Mute": "Mute",
21 | "Unmute": "Unmute",
22 | "Playback Rate": "Playback Rate",
23 | "Subtitles": "Subtitles",
24 | "subtitles off": "subtitles off",
25 | "Captions": "Captions",
26 | "captions off": "captions off",
27 | "Chapters": "Chapters",
28 | "Descriptions": "Descriptions",
29 | "descriptions off": "descriptions off",
30 | "Audio Track": "Audio Track",
31 | "Volume Level": "Volume Level",
32 | "You aborted the media playback": "You aborted the media playback",
33 | "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
34 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.",
35 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",
36 | "No compatible source was found for this media.": "No compatible source was found for this media.",
37 | "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.",
38 | "Play Video": "Play Video",
39 | "Close": "Close",
40 | "Close Modal Dialog": "Close Modal Dialog",
41 | "Modal Window": "Modal Window",
42 | "This is a modal window": "This is a modal window",
43 | "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.",
44 | ", opens captions settings dialog": ", opens captions settings dialog",
45 | ", opens subtitles settings dialog": ", opens subtitles settings dialog",
46 | ", opens descriptions settings dialog": ", opens descriptions settings dialog",
47 | ", selected": ", selected",
48 | "captions settings": "captions settings",
49 | "subtitles settings": "subtitles settings",
50 | "descriptions settings": "descriptions settings",
51 | "Text": "Text",
52 | "White": "White",
53 | "Black": "Black",
54 | "Red": "Red",
55 | "Green": "Green",
56 | "Blue": "Blue",
57 | "Yellow": "Yellow",
58 | "Magenta": "Magenta",
59 | "Cyan": "Cyan",
60 | "Background": "Background",
61 | "Window": "Window",
62 | "Transparent": "Transparent",
63 | "Semi-Transparent": "Semi-Transparent",
64 | "Opaque": "Opaque",
65 | "Font Size": "Font Size",
66 | "Text Edge Style": "Text Edge Style",
67 | "None": "None",
68 | "Raised": "Raised",
69 | "Depressed": "Depressed",
70 | "Uniform": "Uniform",
71 | "Dropshadow": "Dropshadow",
72 | "Font Family": "Font Family",
73 | "Proportional Sans-Serif": "Proportional Sans-Serif",
74 | "Monospace Sans-Serif": "Monospace Sans-Serif",
75 | "Proportional Serif": "Proportional Serif",
76 | "Monospace Serif": "Monospace Serif",
77 | "Casual": "Casual",
78 | "Script": "Script",
79 | "Small Caps": "Small Caps",
80 | "Reset": "Reset",
81 | "restore all settings to the default values": "restore all settings to the default values",
82 | "Done": "Done",
83 | "Caption Settings Dialog": "Caption Settings Dialog",
84 | "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.",
85 | "End of dialog window.": "End of dialog window.",
86 | "{1} is loading.": "{1} is loading.",
87 | "Exit Picture-in-Picture": "Exit Picture-in-Picture",
88 | "Picture-in-Picture": "Picture-in-Picture"
89 | });
--------------------------------------------------------------------------------
/public/lang/zh-CN.js:
--------------------------------------------------------------------------------
1 | videojs.addLanguage('zh-CN', {
2 | "Play": "播放",
3 | "Pause": "暂停",
4 | "Current Time": "当前时间",
5 | "Duration": "时长",
6 | "Remaining Time": "剩余时间",
7 | "Stream Type": "媒体流类型",
8 | "LIVE": "直播",
9 | "Loaded": "加载完成",
10 | "Progress": "进度",
11 | "Fullscreen": "全屏",
12 | "Non-Fullscreen": "退出全屏",
13 | "Picture-in-Picture": "画中画",
14 | "Exit Picture-in-Picture": "退出画中画",
15 | "Mute": "静音",
16 | "Unmute": "取消静音",
17 | "Playback Rate": "播放速度",
18 | "Subtitles": "字幕",
19 | "subtitles off": "关闭字幕",
20 | "Captions": "内嵌字幕",
21 | "captions off": "关闭内嵌字幕",
22 | "Chapters": "节目段落",
23 | "Close Modal Dialog": "关闭弹窗",
24 | "Descriptions": "描述",
25 | "descriptions off": "关闭描述",
26 | "Audio Track": "音轨",
27 | "You aborted the media playback": "视频播放被终止",
28 | "A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
29 | "The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
30 | "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
31 | "No compatible source was found for this media.": "无法找到此视频兼容的源。",
32 | "The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。",
33 | "Play Video": "播放视频",
34 | "Close": "关闭",
35 | "Modal Window": "弹窗",
36 | "This is a modal window": "这是一个弹窗",
37 | "This modal can be closed by pressing the Escape key or activating the close button.": "可以按ESC按键或启用关闭按钮来关闭此弹窗。",
38 | ", opens captions settings dialog": ", 开启标题设置弹窗",
39 | ", opens subtitles settings dialog": ", 开启字幕设置弹窗",
40 | ", opens descriptions settings dialog": ", 开启描述设置弹窗",
41 | ", selected": ", 选择",
42 | "captions settings": "字幕设定",
43 | "Audio Player": "音频播放器",
44 | "Video Player": "视频播放器",
45 | "Replay": "重新播放",
46 | "Progress Bar": "进度条",
47 | "Volume Level": "音量",
48 | "subtitles settings": "字幕设定",
49 | "descriptions settings": "描述设定",
50 | "Text": "文字",
51 | "White": "白",
52 | "Black": "黑",
53 | "Red": "红",
54 | "Green": "绿",
55 | "Blue": "蓝",
56 | "Yellow": "黄",
57 | "Magenta": "紫红",
58 | "Cyan": "青",
59 | "Background": "背景",
60 | "Window": "窗口",
61 | "Transparent": "透明",
62 | "Semi-Transparent": "半透明",
63 | "Opaque": "不透明",
64 | "Font Size": "字体尺寸",
65 | "Text Edge Style": "字体边缘样式",
66 | "None": "无",
67 | "Raised": "浮雕",
68 | "Depressed": "压低",
69 | "Uniform": "均匀",
70 | "Dropshadow": "下阴影",
71 | "Font Family": "字体库",
72 | "Proportional Sans-Serif": "比例无细体",
73 | "Monospace Sans-Serif": "单间隔无细体",
74 | "Proportional Serif": "比例细体",
75 | "Monospace Serif": "单间隔细体",
76 | "Casual": "舒适",
77 | "Script": "手写体",
78 | "Small Caps": "小型大写字体",
79 | "Reset": "重置",
80 | "restore all settings to the default values": "恢复全部设定至预设值",
81 | "Done": "完成",
82 | "Caption Settings Dialog": "字幕设定窗口",
83 | "Beginning of dialog window. Escape will cancel and close the window.": "打开对话窗口。Escape键将取消并关闭对话窗口",
84 | "End of dialog window.": "结束对话窗口",
85 | "Seek to live, currently behind live": "尝试直播,当前为延时播放",
86 | "Seek to live, currently playing live": "尝试直播,当前为实时播放",
87 | "progress bar timing: currentTime={1} duration={2}": "{1}/{2}",
88 | "{1} is loading.": "正在加载 {1}。",
89 | "Open quality selector menu":"选择清晰度"
90 | });
--------------------------------------------------------------------------------
/public/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/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 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/public/notify.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/notify.wav
--------------------------------------------------------------------------------
/public/sha1filework.js:
--------------------------------------------------------------------------------
1 | const worker = self
2 | const fspromises = global.require('fs/promises')
3 | var crypto = global.require('crypto')
4 | let running = false
5 | process.noAsar = true
6 | worker.addEventListener('message', (event) => {
7 | if (event.data.hash && event.data.hash == 'sha1') {
8 | const localFilePath = event.data.localFilePath
9 | const access_token = event.data.access_token
10 | fileSha1(localFilePath, access_token).catch((e) => {
11 | worker.postMessage({ hash: 'sha1', sha1: 'error', proof_code: '', error: FileSystemErrorMessage(e.code, e.message) })
12 | running = false
13 | })
14 | }
15 | if (event.data.stop) {
16 | running = false
17 | }
18 | if (event.data.close) {
19 | close()
20 | }
21 | })
22 | worker.addEventListener('error', (e) => {
23 | worker.postMessage({ hash: 'sha1', sha1: 'error', proof_code: '', error: FileSystemErrorMessage(e.code, e.message) })
24 | running = false
25 | })
26 |
27 | async function fileSha1(localFilePath, access_token) {
28 | running = true
29 | let hash = ''
30 | var sha1 = crypto.createHash('sha1')
31 | const fileHandle = await fspromises.open(localFilePath, 'r')
32 | try {
33 | if (fileHandle) {
34 | const stat = await fileHandle.stat()
35 | const size = stat.size
36 | const buff = Buffer.alloc(4 * 1024 * 1024)
37 | let readlen = 0
38 |
39 | let timer = setInterval(function () {
40 | const message = '计算sha1(' + Math.floor((readlen * 100) / size).toString() + '%)'
41 | worker.postMessage({ hash: 'sha1', readlen, size, message })
42 | }, 300)
43 |
44 | while (true) {
45 | if (!running) break
46 | const len = await fileHandle.read(buff, 0, buff.length, null)
47 | if (len.bytesRead > 0 && len.bytesRead == buff.length) {
48 | sha1.update(buff)
49 | } else if (len.bytesRead > 0) {
50 | sha1.update(buff.slice(0, len.bytesRead))
51 | }
52 | readlen += len.bytesRead
53 |
54 | if (len.bytesRead <= 0) break
55 | }
56 | clearInterval(timer)
57 | let proof_code = ''
58 | if (running) {
59 | hash = sha1.digest('hex')
60 | hash = hash.toUpperCase()
61 | const m = unescape(encodeURIComponent(access_token))
62 | const buffa = Buffer.from(m)
63 | const md5a = crypto.createHash('md5').update(buffa).digest('hex')
64 | const start = Number(BigInt('0x' + md5a.substr(0, 16)) % BigInt(size))
65 | const end = Math.min(start + 8, size)
66 | const buffb = Buffer.alloc(end - start)
67 | await fileHandle.read(buffb, 0, buffb.length, start)
68 | proof_code = buffb.toString('base64')
69 | } else {
70 | hash = 'error'
71 | proof_code = ''
72 | /** 这里无所谓了,外部会error='' */
73 | }
74 |
75 | worker.postMessage({ hash: 'sha1', sha1: hash, proof_code, error: '' })
76 | running = false
77 | } else {
78 | worker.postMessage({ hash: 'sha1', sha1: 'error', proof_code: '', error: '打开文件失败' })
79 | running = false
80 | }
81 | } catch {}
82 | try {
83 | await fileHandle?.close()
84 | } catch {}
85 | }
86 |
87 | function FileSystemErrorMessage(code, message) {
88 | if (!code && !message) return '读取文件失败'
89 |
90 | if (code) {
91 | switch (code) {
92 | case 'EACCES':
93 | return '没有权限访问'
94 | case 'EEXIST':
95 | return '存在重名文件'
96 | case 'EISDIR':
97 | return '不能是文件夹'
98 | case 'EMFILE':
99 | return '同时打开文件过多'
100 | case 'ENFILE':
101 | return '同时打开文件过多'
102 | case 'ENOENT':
103 | return '该路径不存在'
104 | case 'ENOTDIR':
105 | return '不能是文件'
106 | case 'ENOTEMPTY':
107 | return '文件夹不为空'
108 | case 'EPERM':
109 | return '没有权限访问'
110 | case 'EBUSY':
111 | return '文件被其他程序占用'
112 | case 'ETIMEDOUT':
113 | return '操作超时'
114 | case 'EDQUOT':
115 | return '超出磁盘配额'
116 | case 'EFBIG':
117 | return '文件太大'
118 | case 'EIDRM':
119 | return '文件已被删除'
120 | case 'EIO':
121 | return 'IO错误'
122 | case 'ELOOP':
123 | return '路径级别过多'
124 | case 'ENAMETOOLONG':
125 | return '文件名太长'
126 | case 'ENODEV':
127 | return '找不到设备'
128 | case 'ENOMEM':
129 | return '没有足够的空间'
130 | case 'ENOSPC':
131 | return '没有可用空间'
132 | case 'EROFS':
133 | return '只读文件'
134 | }
135 | }
136 | if (message && typeof message == 'string' && message.indexOf('EACCES') >= 0) return '没有权限访问'
137 | let err = (code || '') + (message || '')
138 | if (err) return err
139 | return '读取文件失败'
140 | }
141 |
--------------------------------------------------------------------------------
/public/silvermine-videojs-quality-selector.css:
--------------------------------------------------------------------------------
1 | .vjs-quality-selector .vjs-menu-button {
2 | margin: 0;
3 | padding: 0;
4 | height: 100%;
5 | width: 100%;
6 | }
7 | .vjs-quality-selector .vjs-icon-placeholder {
8 | font-family: 'VideoJS';
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 | .vjs-quality-selector .vjs-icon-placeholder::before {
13 | content: '\f110';
14 | }
15 | .vjs-quality-changing .vjs-big-play-button {
16 | display: none;
17 | }
18 | .vjs-quality-changing .vjs-control-bar {
19 | display: -webkit-box;
20 | display: -webkit-flex;
21 | display: flex;
22 | visibility: visible;
23 | opacity: 1;
24 | }
25 |
26 | .vjs-quality-selector .vjs-menu{
27 | width: 6em;
28 | left: -1em;
29 | }
30 |
--------------------------------------------------------------------------------
/public/userface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/userface.png
--------------------------------------------------------------------------------
/public/wasm.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PingKuNet/aliyunpan/b2b9a27dbfe439487c4b06c89b4e5c8e5f7a8912/public/wasm.wasm
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/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/models.ts:
--------------------------------------------------------------------------------
1 |
2 | export interface IDownloadUrl {
3 | drive_id: string
4 | file_id: string
5 | expire_sec: number
6 | url: string
7 | size: number
8 | }
9 |
10 |
11 | export interface IVideoPreviewUrl {
12 | drive_id: string
13 | file_id: string
14 | expire_sec: number
15 | url: string
16 | duration: number
17 | width: number
18 | height: number
19 | urlFHD: string
20 | urlHD: string
21 | urlSD: string
22 | urlLD: string
23 | subtitles: {
24 | language: string
25 | url: string
26 | }[]
27 | }
28 |
29 |
30 | export interface IOfficePreViewUrl {
31 | drive_id: string
32 | file_id: string
33 | access_token: string
34 | preview_url: string
35 | }
36 |
37 |
38 | export interface IVideoXBTUrl {
39 | time: string
40 | url: string
41 | }
42 |
43 |
44 | export interface IUploadCreat {
45 | user_id: string
46 | drive_id: string
47 | file_id: string
48 | israpid: boolean
49 | isexist: boolean
50 | upload_id: string
51 | part_info_list: {
52 | upload_url: string
53 | part_number: number
54 | part_size: number
55 | isupload: boolean
56 | }[]
57 | errormsg: string
58 | }
59 |
60 | export interface IUploadInfo {
61 | token_type: string
62 | access_token: string
63 | sha1: string
64 | israpid: boolean
65 | isexist: boolean
66 | part_info_list: {
67 | upload_url: string
68 | part_number: number
69 | part_size: number
70 | isupload: boolean
71 | }[]
72 | }
73 |
74 | export interface IAliBatchResult {
75 | count: number
76 | async_task: {
77 | drive_id: string
78 | file_id: string
79 | task_id: string
80 | newdrive_id: string
81 | newfile_id: string
82 | }[]
83 | reslut: {
84 | id: string
85 | file_id?: string
86 |
87 | name?: string
88 | type?: string
89 | parent_file_id?: string
90 |
91 | share_id?: string
92 | share_pwd?: string
93 | share_url?: string
94 | expiration?: string
95 | share_name?: string
96 |
97 | body?: any
98 | }[]
99 | error: {
100 | id: string
101 | code: string
102 | message: string
103 | }[]
104 | }
105 |
106 | export interface IBatchResult {
107 | count: number
108 | task: {
109 | file_id: string
110 | task_id: string
111 | newdrive_id: string
112 | newfile_id: string
113 | }[]
114 | reslut: {
115 | id: string
116 | file_id: string
117 | }[]
118 | error: {
119 | id: string
120 | code: string
121 | message: string
122 | }[]
123 | }
124 |
125 |
126 | export interface IAliGetAlbumModel {
127 | album_id: string
128 | created_at: number
129 | description: string
130 | file_count: number
131 | image_count: number
132 | name: string
133 | owner: string
134 | updated_at: number
135 | video_count: number
136 | }
137 |
138 | export interface IAliUserDriveDetails {
139 | drive_used_size: number
140 | drive_total_size: number
141 | default_drive_used_size: number
142 | album_drive_used_size: number
143 | note_drive_used_size: number
144 | sbox_drive_used_size: number
145 | share_album_drive_used_size: number
146 | }
147 |
148 | export interface IAliUserDriveCapacity {
149 | type: string
150 | size: number
151 | sizeStr: string
152 | expired: string
153 | expiredstr: string
154 | description: string
155 | latest_receive_time: string /* "2022-05-02T00:50:51.379Z" */
156 | }
157 |
158 |
159 | export interface IStateUploadFile {
160 | UploadID: string
161 | Info: {
162 | user_id: string
163 |
164 | localFilePath: string
165 |
166 | parent_file_id: string
167 | drive_id: string
168 |
169 | path: string
170 |
171 | name: string
172 |
173 | size: number
174 | sizeStr: string
175 | icon: string
176 | isDir: boolean
177 | isMiaoChuan: boolean
178 |
179 | sha1: string
180 |
181 | crc64: string
182 | }
183 |
184 | Upload: {
185 |
186 | DownState: string
187 | DownTime: number
188 | DownSize: number
189 | DownSpeed: number
190 | DownSpeedStr: string
191 | DownProcess: number
192 | IsStop: boolean
193 | IsDowning: boolean
194 | IsCompleted: boolean
195 | IsFailed: boolean
196 | /** 失败的代码 */
197 | failedCode: number
198 | /** 失败的消息 */
199 | failedMessage: string
200 |
201 | AutoTry: number
202 |
203 | upload_id: string
204 |
205 | file_id: string
206 | /** 是否覆盖上传 */
207 | IsBreakExist: boolean
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/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 |
7 | export default class AliUploadMem {
8 |
9 | static async UploadMem(user_id: string, drive_id: string, parent_file_id: string, CreatFileName: string, context: string) {
10 | const token = await UserDAL.GetUserTokenFromDB(user_id)
11 | if (!token || !token.access_token) return '账号失效,操作取消'
12 | let hash = 'DA39A3EE5E6B4B0D3255BFEF95601890AFD80709'
13 | let proof = ''
14 | let buff = Buffer.from([])
15 | if (context.length > 0) {
16 | buff = Buffer.from(context, 'utf-8')
17 | const dd = await AliUploadHashPool.GetBuffHashProof(token!.access_token, buff)
18 | hash = dd.sha1
19 | proof = dd.proof_code
20 | }
21 | const size = buff.length
22 |
23 | const upinfo = await AliUpload.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, CreatFileName, size, hash, proof, 'refuse')
24 | if (upinfo.errormsg != '') {
25 | return upinfo.errormsg
26 | }
27 | if (upinfo.isexist) return '网盘中已存在同名文件'
28 | if (upinfo.israpid) return 'success'
29 |
30 | await axios
31 | .put(upinfo.part_info_list[0].upload_url, buff, {
32 | responseType: 'text',
33 | timeout: 30000,
34 | headers: {
35 |
36 | 'Content-Type': '',
37 | Authorization: token!.token_type + ' ' + token!.access_token
38 | }
39 | })
40 | .catch(function (err: any) {
41 | DebugLog.mSaveDanger('UploadMemError', err)
42 | })
43 | const result = await AliUpload.UploadFileComplete(user_id, drive_id, upinfo.file_id, upinfo.upload_id, size, hash)
44 | if (result) return 'success'
45 | else return '合并文件失败'
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/down/DownM3U8.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
开始全部
12 |
暂停全部
13 |
清除全部
14 |
15 |
16 |
17 |
开始
18 |
暂停
19 |
清除
20 |
优先传输
21 |
22 |
23 |
开始全部
24 |
暂停全部
25 |
清除全部
26 |
27 |
28 |
29 |
32 | true">下载转码后的视频--还没做
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/down/DownSync.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
新建任务
12 |
暂停任务
13 |
删除任务
14 |
15 |
16 |
17 |
修改设置
18 |
同步日志
19 |
立即同步
20 |
21 |
22 |
23 |
26 | true">文件同步功能--还没做
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/down/Index.vue:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 | 上传下载文件
17 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/down/downmenu.ts:
--------------------------------------------------------------------------------
1 | import DownDAL from './downdal'
2 | import UploadDAL from '../transfer/uploaddal'
3 | import UploadingDAL from '../transfer/uploadingdal'
4 |
5 | export function topStartDown(isdown: boolean) {
6 | if (isdown) {
7 | // DownDAL.DowningState(false, true)
8 | } else {
9 | }
10 | }
11 | export function topStartDownAll(isdown: boolean) {
12 | if (isdown) {
13 | // DownDAL.DowningState(true, true)
14 | } else {
15 | }
16 | }
17 |
18 | export function topStopDown(isdown: boolean) {
19 | if (isdown) {
20 | // DownDAL.DowningState(false, false)
21 | } else {
22 | }
23 | }
24 | export function topStopDownAll(isdown: boolean) {
25 | if (isdown) {
26 | // DownDAL.DowningState(true, false)
27 | } else {
28 | }
29 | }
30 |
31 | export function topDeleteDown(isdown: boolean) {
32 | if (isdown) {
33 | // DownDAL.DowningDelete(false)
34 | } else {
35 | UploadingDAL.aUploadingDelete(false)
36 | }
37 | }
38 |
39 | export function topDeleteDownAll(isdown: boolean) {
40 | if (isdown) {
41 | // DownDAL.DowningDelete(true)
42 | } else {
43 | UploadingDAL.aUploadingDelete(true)
44 | }
45 | }
46 |
47 | export function topDeleteDowned(isdown: boolean) {
48 | if (isdown) {
49 | // DownDAL.DownedDelete(false)
50 | } else {
51 | UploadDAL.UploadedDelete(false)
52 | }
53 | }
54 |
55 | export function topDeleteDownedAll(isdown: boolean) {
56 | if (isdown) {
57 | // DownDAL.DownedDelete(true)
58 | } else {
59 | UploadDAL.UploadedDelete(true)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/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 | | 'overwrite' // overwrite (直接覆盖,以后多版本有用)
22 | | 'auto_rename' // auto_rename (自动换一个随机名称)
23 | | 'refuse' // refuse (不会创建,告诉你已经存在)
24 | | 'ignore' // ignore (会创建重名的)
25 |
26 | declare type FileType =
27 | | 'file' // 文件
28 | | 'folder' // 文件夹(目录)
29 |
30 | declare type UploadStates =
31 | | 'waiting' // 排队中, 等待上传
32 | | 'start' // 开始
33 | | 'computing_hash' // 计算hash,预秒传,秒传
34 | | 'created' // 创建成功
35 | | 'running' // 上传中
36 | | 'stopped' // 暂停
37 | | 'complete' // 上传完成
38 | | 'checking' // 校验中, 检查 crc64 是否一致
39 | | 'success' // 上传成功
40 | | 'rapid_success' // 秒传成功
41 | | 'error' // 上传失败
42 | | 'cancelled' // 已取消
43 |
44 | // DownloadState 没有 computing_hash & rapid_success
45 | declare type DownloadStates =
46 | | 'waiting' // 排队中, 等待下载
47 | | 'start' // 开始
48 | | 'created' // 创建成功
49 | | 'running' // 下载中
50 | | 'stopped' // 暂停
51 | | 'complete' // 下载完成
52 | | 'checking' // 校验中, 检查 crc64 是否一致
53 | | 'success' // 下载成功
54 | | 'error' // 下载失败
55 | | 'cancelled' // 已取消
56 |
57 | declare module 'Go'
58 | declare module 'dom-to-image'
59 | declare module 'jschardet'
60 | declare function pinyinlite(text: string, config: any): any
61 | declare function videojs(ref: any, options: any, cb: any): any
62 |
--------------------------------------------------------------------------------
/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 | WebPlatformSync: any
26 | UploadPort: any
27 | DownloadPort: any
28 | MainPort: any
29 | WinMsgToUpload: any
30 | WinMsgToDownload: any
31 | WinMsgToMain: any
32 | IsMainPage: boolean
33 | WebSetProxy: any
34 | speedLimte: number
35 | WebSetProgressBar: any
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/layout/MyLoading.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/src/layout/MySplit.vue:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
34 |
35 | first
36 |
37 |
38 |
41 |
42 |
43 | second
44 |
45 |
46 |
47 |
77 |
--------------------------------------------------------------------------------
/src/layout/MySwitch.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 | ✔
15 | ✖
16 |
17 |
18 |
19 |
20 |
65 |
--------------------------------------------------------------------------------
/src/layout/MyTags.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
47 |
48 |
78 |
--------------------------------------------------------------------------------
/src/layout/PageCode.vue:
--------------------------------------------------------------------------------
1 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
{{ appStore.pageCode?.file_name || '文档在线预览' }}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | {{codeString}}
87 |
88 |
{{ codeString }}
89 |
90 |
91 |
92 |
93 |
94 |
95 |
132 |
--------------------------------------------------------------------------------
/src/layout/PageHelp.vue:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
36 |
37 |
38 |
阿里云盘小白羊版
39 |
40 |
LoadObject
41 |
CreatMap
42 |
CreatObject
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/layout/PageLoading.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

9 |
10 |
11 |
12 |
49 |
--------------------------------------------------------------------------------
/src/layout/PageOffice.vue:
--------------------------------------------------------------------------------
1 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
{{ appStore.pageOffice?.file_name || '文档在线预览' }}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/layout/PageWorker.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
阿里云盘
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 工作进程
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/pan/index.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/pan/menus/DirLeftMenu.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/pan/menus/DirTopPath.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
35 | selectDir(item.drive_id, item.file_id)">
36 | {{ item.name.length > 30 ? item.name.substring(0, 27) + '...' : item.name }}
37 |
38 |
39 |
40 |
41 |
42 |
112 |
--------------------------------------------------------------------------------
/src/pan/menus/PanTopbtn.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/pan/menus/TrashRightMenu.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/pan/menus/TrashTopbtn.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
25 |
26 |
还原选中
27 |
menuTrashSelectFile(false, true)">彻底删除
28 |
29 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/pan/topbtns/AlphaModal.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 | 提示
26 |
27 |
28 |
29 | 这是Beta测试版本
30 |
31 |
这个版已经拥有了上传下载功能,但可能并不是很稳定,还有很多功能正在开发完善中,不太适合日常使用。
32 | 如有问题请打开【帮助文档】到GitHub查阅反馈!
33 |
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 |
45 |
46 |
86 |
--------------------------------------------------------------------------------
/src/pan/topbtns/ArchivePasswordModal.vue:
--------------------------------------------------------------------------------
1 |
107 |
108 |
109 |
110 |
111 | 需要解压密码
112 |
113 |
114 |
115 |
116 | 密码:
117 |
118 |
119 |
120 |
121 |
122 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/src/pan/topbtns/CreatNewDirMultiModal.vue:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/pan/topbtns/DownloadModal.vue:
--------------------------------------------------------------------------------
1 |
46 |
47 |
48 |
49 |
50 | 从网盘下载 文件/文件夹 到本地
51 |
52 |
53 |
54 |
55 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/pan/topbtns/ShuXingMultiModal.vue:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/pan/topbtns/UploadModal.vue:
--------------------------------------------------------------------------------
1 |
72 |
73 |
74 |
75 |
76 | 上传 文件/文件夹 到网盘
77 |
78 |
79 |
80 | :把{{ filelist.length }}个文件上传到网盘:
82 |
83 |
84 |
85 | {{ dirPath }}
86 |
87 |
88 |
89 |
90 |
:上传时 遇到重名文件冲突
91 |
92 |
93 | 删除网盘内文件,继续上传
94 | 覆盖网盘内文件,继续上传
95 | 保留网盘内文件,继续上传,重命名
96 | 保留网盘内文件,不上传了
97 |
98 |
99 |
100 |
101 |
102 | 默认:删除网盘内文件,继续上传
103 |
104 | 如果要上传的文件和网盘内已存在的文件重名了
105 | 当内容完全一致(sha1相同)时,则无需处理
106 | 反之,需要决定如何处理
107 |
108 | 删除网盘内文件和覆盖网盘内文件区别是:
109 | 删除会在回收站有已删除记录可以还原文件
110 | 覆盖在回收站没有记录
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
123 |
124 |
125 |
126 |
132 |
--------------------------------------------------------------------------------
/src/pic/PicLeft.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | appStore.toggleTabMenu('pic', val)" />
20 |
21 |
22 |
23 |
24 | 全部照片--还没做
25 | 我的相册--还没做
26 |
27 |
28 |
29 |
30 |
31 |
36 |
--------------------------------------------------------------------------------
/src/pic/PicRight.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/pic/index.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/rss/index.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | 好玩的插件
21 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 生成指定文件夹的全部子文件夹和子文件的详细列表(类似 cmd > dir linux > ls),功能正在规划中,以后提供
73 |
74 |
75 |
76 |
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.ApiFileInfo(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/rssjiami/jiami.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('XM GetAllFiles文件失败:' + 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/rss/rssrename/RssRename.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
78 |
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/SettingLog.vue:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 |
33 |
:运行日志
34 |
35 |
46 |
47 |
48 | [{{ item.logtime }}]
49 | {{ item.logmessage }}
50 |
51 |
52 |
53 |
54 |
55 |
56 |
刷新
57 |
58 |
清空日志
59 |
60 |
复制日志
61 |
62 |
63 |
64 |
65 |
83 |
--------------------------------------------------------------------------------
/src/setting/SettingUI.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 |
阿里云盘小白羊版 {{ Config.appVersion }}
26 |
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 |
55 |
56 |
57 |
66 |
--------------------------------------------------------------------------------
/src/setting/ShutDown.vue:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
44 | 传输完成后自动关机
45 |
46 |
47 |
48 |
49 | {{ time }}
秒
50 |
51 |
52 |
53 |
56 |
57 |
58 |
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 |
24 |
25 |
26 | 阿里云盘分享
27 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/share/share/ShareSiteRight.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
26 |
27 |
28 |
58 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia'
2 | import useAppStore from './appstore'
3 | import useKeyboardStore from './keyboardstore'
4 | import type { KeyboardState } from './keyboardstore'
5 | import useLogStore from './logstore'
6 | import useModalStore from './modalstore'
7 | import type { ModalState } from './modalstore'
8 | import useWinStore from './winstore'
9 | import type { WinState } from './winstore'
10 | import useSettingStore from '../setting/settingstore'
11 | import useUserStore from '../user/userstore'
12 | import type { ITokenInfo } from '../user/userstore'
13 | import usePanTreeStore from '../pan/pantreestore'
14 | import usePanFileStore from '../pan/panfilestore'
15 |
16 | import useServerStore from './serverstore'
17 | import type { IOtherShareLinkModel } from '../share/share/OtherShareStore'
18 | import type { IShareSiteModel } from './serverstore'
19 | import useMyShareStore from '../share/share/MyShareStore'
20 | import useOtherShareStore from '../share/share/OtherShareStore'
21 | import useMyFollowingStore from '../share/following/MyFollowingStore'
22 | import useOtherFollowingStore from '../share/following/OtherFollowingStore'
23 | import type { FollowingState } from '../share/following/OtherFollowingStore'
24 |
25 | import useUploadingStore from '../down/UploadingStore'
26 | import useUploadedStore from '../down/UploadedStore'
27 | import useDownedStore from '../down/DownedStore'
28 | import useDowningStore from '../down/DowningStore'
29 |
30 | import useFootStore from './footstore'
31 | import type { AsyncModel } from './footstore'
32 |
33 | const pinia = createPinia()
34 | export {
35 | useAppStore,
36 | useSettingStore,
37 | useLogStore,
38 | useModalStore,
39 | ModalState,
40 | useWinStore,
41 | WinState,
42 | useKeyboardStore,
43 | KeyboardState,
44 | useUserStore,
45 | ITokenInfo,
46 | usePanTreeStore,
47 | usePanFileStore,
48 | useServerStore,
49 | IOtherShareLinkModel,
50 | IShareSiteModel,
51 | useMyShareStore,
52 | useOtherShareStore,
53 | useOtherFollowingStore,
54 | FollowingState,
55 | useMyFollowingStore,
56 | useFootStore,
57 | AsyncModel,
58 | useUploadingStore,
59 | useUploadedStore,
60 | useDowningStore,
61 | useDownedStore
62 | }
63 | export default pinia
64 |
--------------------------------------------------------------------------------
/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 |
11 | Alt: boolean
12 |
13 | Repeat: boolean
14 |
15 | IsInput: boolean
16 | IsEnmpty: boolean
17 | }
18 |
19 | export interface KeyboardState {
20 | KeyDownEvent: KeyboardMessage
21 | KeyUpEvent: KeyboardMessage
22 | }
23 |
24 | const useKeyboardStore = defineStore('keyboard', {
25 | state: (): KeyboardState => ({
26 | KeyDownEvent: { Ctrl: false, Shift: false, Alt: false, Repeat: false, IsInput: false, Code: '', Key: '', IsEnmpty: true } as KeyboardMessage,
27 | KeyUpEvent: { Ctrl: false, Shift: false, Alt: false, Repeat: false, IsInput: false, Code: '', Key: '', IsEnmpty: true } as KeyboardMessage
28 | }),
29 |
30 | getters: {},
31 |
32 | actions: {
33 | updateStore(partial: Partial) {
34 | this.$patch(partial)
35 | },
36 | KeyDown(event: KeyboardEvent) {
37 | console.log(event)
38 | this.KeyDownEvent = { Ctrl: event.ctrlKey, Shift: event.shiftKey, Alt: event.altKey, Repeat: event.repeat, IsInput: false, Code: event.code, Key: event.key.toLowerCase(), IsEnmpty: false }
39 | }
40 | }
41 | })
42 |
43 | export default useKeyboardStore
44 |
--------------------------------------------------------------------------------
/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 |
20 | this.$patch({ modalName: '', modalData: {} })
21 | setTimeout(() => {
22 | this.$patch({ modalName, modalData })
23 | }, 300)
24 | } else this.$patch({ modalName, modalData })
25 | }
26 | }
27 | })
28 |
29 | export default useModalStore
30 |
--------------------------------------------------------------------------------
/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: 'aHR0cHM6Ly9naXRodWIuY29tL1BpbmdLdU5ldC9hbGl5dW5wYW4='
20 | }),
21 | actions: {
22 |
23 | mSaveShareSiteList(shareSiteList: IShareSiteModel[]) {
24 | this.shareSiteList = shareSiteList || []
25 | },
26 |
27 | mSaveHelpUrl(url: string) {
28 | this.helpUrl = url || 'aHR0cHM6Ly9naXRodWIuY29tL1BpbmdLdU5ldC9hbGl5dW5wYW4='
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 showlist = await DBUpload.getUploadedByTop(5000)
13 | const count = await DBUpload.getUploadTaskCount()
14 | uploadedStore.aLoadListData(showlist, count)
15 | uploadedStore.ListLoading = false
16 | }
17 |
18 |
19 | static async aClearUploaded() {
20 | const max = useSettingStore().debugDownedListMax
21 | return await DBUpload.deleteUploadedOutCount(max)
22 | }
23 |
24 |
25 | static async UploadedDelete(all: boolean) {
26 | if (clickWait('UploadedDelete', -1)) return
27 | if (all) {
28 | await DBUpload.clearUploadedAll()
29 | } else {
30 | const uploadedStore = useUploadedStore()
31 | const keys = Array.from(uploadedStore.ListSelected)
32 | await DBUpload.deleteUploadedBatch(keys)
33 | }
34 | await this.aReloadUploaded()
35 | clickWaitDelete('UploadedDelete')
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/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 | user_id: string
14 | user_name: string
15 | avatar: string
16 | nick_name: string
17 | default_drive_id: string
18 | default_sbox_drive_id: string
19 | role: string
20 | status: string
21 | expire_time: string
22 | state: string
23 | pin_setup: boolean
24 | is_first_login: boolean
25 | need_rp_verify: boolean
26 |
27 |
28 | name: string
29 | spu_id: string
30 | is_expires: boolean
31 | used_size: number
32 | total_size: number
33 | spaceinfo: string
34 | vipname: string
35 | vipexpire: string
36 |
37 |
38 | pic_drive_id: string
39 | }
40 |
41 | export interface UserState {
42 | user_id: string
43 | userLogined: boolean
44 | userShowLogin: boolean
45 | }
46 |
47 | const useUserStore = defineStore('user', {
48 | state: (): UserState => ({
49 | user_id: '',
50 | userLogined: false,
51 | userShowLogin: false
52 | }),
53 |
54 | getters: {
55 | GetUserToken(state: UserState): ITokenInfo {
56 | return UserDAL.GetUserToken(state.user_id)
57 | }
58 | },
59 |
60 | actions: {
61 | userLogin(user_id: string) {
62 | this.user_id = user_id
63 | this.userLogined = true
64 | },
65 | userLogOff() {
66 | this.user_id = ''
67 | this.userLogined = false
68 | }
69 | }
70 | })
71 |
72 | export default useUserStore
73 |
--------------------------------------------------------------------------------
/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 = 'v3.09.13'
3 | static referer = 'https://www.aliyundrive.com/'
4 | static downAgent = 'okhttp/4.2.2'
5 | static userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.33'
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 |
8 | 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'
9 | }
10 |
--------------------------------------------------------------------------------
/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