diff --git a/.github/update.log b/.github/update.log index 0e5526b328..ef795daa55 100644 --- a/.github/update.log +++ b/.github/update.log @@ -601,3 +601,4 @@ Update On Mon Mar 25 19:35:07 CET 2024 Update On Tue Mar 26 19:28:27 CET 2024 Update On Wed Mar 27 19:27:19 CET 2024 Update On Thu Mar 28 19:29:20 CET 2024 +Update On Sat Mar 30 19:27:32 CET 2024 diff --git a/aliyunpan/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/aliyunpan/.github/ISSUE_TEMPLATE/BUG-REPORT.yml new file mode 100644 index 0000000000..9e65209ff7 --- /dev/null +++ b/aliyunpan/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -0,0 +1,86 @@ +name: Bug report +description: Problems with the software +title: '[Bug] {{请输入标题,不要留空 - Please enter a title, do not leave it blank.}}' +labels: ['bug'] +body: + - type: markdown + attributes: + value: | + **非常感谢您的反馈!Thank you very much for your feedback!** + + 有关讨论、建议或者咨询的内容请去往[讨论区](https://github.com/gaozhangmin/aliyunpan/discussions)。 + + For suggestions or help, please consider using [Github Discussion](https://github.com/gaozhangmin/aliyunpan/discussions) instead. + + - type: checkboxes + attributes: + label: Please search before asking + description: | + 辛苦提 Bug 前,检索一下 [问题](https://github.com/gaozhangmin/aliyunpan/issues?q=) 列表是否已经存在类似问题。麻烦提供系统版本、录屏或者截图、复现步骤,有助于更好的解决问题。 + + 非 Bug 相关,烦请移步 [讨论区](https://github.com/gaozhangmin/aliyunpan/discussions) 找寻有关讨论。 + + Please search [issues](https://github.com/gaozhangmin/aliyunpan/issues) to check if your issue has already been reported. + + Not related to bugs, please go to the [discussion area](https://github.com/gaozhangmin/aliyunpan/discussions) for relevant discussions. + + options: + - label: > + I searched in the [issues](https://github.com/gaozhangmin/aliyunpan/issues) and found nothing similar. + required: true + - type: checkboxes + attributes: + label: Please read README + description: | + 辛苦提 Bug 前,请仔细阅读一下 README 中的 [Troubleshooting](https://github.com/gaozhangmin/aliyunpan/tree/main#troubleshooting) 是否已经给出相关解决方案 + + Before reporting bugs (especially for issues such as missing icons in the desktop application, permission pop-ups, and damaged report files), please carefully read the [Troubleshooting](https://github.com/gaozhangmin/aliyunpan/tree/main#troubleshooting) section in README to see if relevant solutions have already been provided. + + options: + - label: I have read the troubleshooting section in the README in detail. + required: true + + - type: input + attributes: + label: 使用的版本 + description: > + 请提供您正在使用的 小白羊 的版本。Please provide the version of 小白羊 you are using. For example, `3.11.7`. + validations: + required: true + - type: input + attributes: + label: 系统 System + description: > + 请提供您正在使用的系统。Please provide the version of the System you are using. For example, `macOS 11.2.3`. + validations: + required: true + - type: textarea + attributes: + label: 复现步骤 Reproduce step + description: > + 请提供完整且简明的复现步骤,以方便及时定位并解决问题。Please provide complete and concise reproduction steps to facilitate timely identification and resolution of the issue. + validations: + required: true + - type: textarea + attributes: + label: 你看到了什么错误?What errors do you see? + validations: + required: true + - type: textarea + attributes: + label: 你期望看到什么?What did you expect to see? + validations: + required: true + - type: textarea + attributes: + label: 还有其他的内容吗?Anything else? + - type: checkboxes + attributes: + label: 你是否愿意提交一份 PR 来修改这个错误?Are you willing to submit a PR? + description: > + 我们期待开发人员和用户的帮助,以解决在 小白羊 中发现的任何问题。 如果您愿意通过提交 PR 来解决此问题,请勾选。We eagerly anticipate developers' and users' support and collaboration in resolving any issues found in 小白羊. If you are willing to offer a solution by submitting a PR to fix this matter, kindly mark the checkbox provided. + options: + - label: 我愿意提供 PR! I'm willing to submit a PR! + - type: markdown + attributes: + value: '非常感谢您的反馈!Thank you very much for your feedback!' diff --git a/aliyunpan/.github/ISSUE_TEMPLATE/feature.yml b/aliyunpan/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml similarity index 100% rename from aliyunpan/.github/ISSUE_TEMPLATE/feature.yml rename to aliyunpan/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml diff --git a/aliyunpan/.github/ISSUE_TEMPLATE/bug-report.yml b/aliyunpan/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100644 index 33002c9d01..0000000000 --- a/aliyunpan/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Bug report -description: Problems with the software -title: '[Bug] {{请输入标题,不要留空 - Please enter a title, do not leave it blank.}}' -labels: ['bug'] -body: - - type: markdown - attributes: - value: | - **非常感谢您的反馈!Thank you very much for your feedback!** - - 有关讨论、建议或者咨询的内容请去往[讨论区](https://github.com/gaozhangmin/aliyunpan/discussions)。 - - For suggestions or help, please consider using [Github Discussion](https://github.com/gaozhangmin/aliyunpan/discussions) instead. - - - type: checkboxes - attributes: - label: Please search before asking - description: | - 辛苦提 Bug 前,检索一下 [问题](https://github.com/gaozhangmin/aliyunpan/issues?q=) 列表是否已经存在类似问题。麻烦提供系统版本、录屏或者截图、复现步骤,有助于更好的解决问题。 - - 非 Bug 相关,烦请移步 [讨论区](https://github.com/gaozhangmin/aliyunpan/discussions) 找寻有关讨论。 - - Please search [issues](https://github.com/gaozhangmin/aliyunpan/issues) to check if your issue has already been reported. - - Not related to bugs, please go to the [discussion area](https://github.com/gaozhangmin/aliyunpan/discussions) for relevant discussions. - - options: - - label: > - I searched in the [issues](https://github.com/gaozhangmin/aliyunpan/issues) and found nothing similar. - required: true - - type: checkboxes - attributes: - label: Please read README - description: | - 辛苦提 Bug 前,请仔细阅读一下 README 中的 [Troubleshooting](https://github.com/gaozhangmin/aliyunpan/tree/main#troubleshooting) 是否已经给出相关解决方案 - - Before reporting bugs (especially for issues such as missing icons in the desktop application, permission pop-ups, and damaged report files), please carefully read the [Troubleshooting](https://github.com/gaozhangmin/aliyunpan/tree/main#troubleshooting) section in README to see if relevant solutions have already been provided. - - options: - - label: I have read the troubleshooting section in the README in detail. - required: true - - - type: input - attributes: - label: 使用的版本 - description: > - 请提供您正在使用的 小白羊 的版本。Please provide the version of 小白羊 you are using. For example, `3.11.7`. - validations: - required: true - - type: input - attributes: - label: 系统 System - description: > - 请提供您正在使用的系统。Please provide the version of the System you are using. For example, `macOS 11.2.3`. - validations: - required: true - - type: textarea - attributes: - label: 复现步骤 Reproduce step - description: > - 请提供完整且简明的复现步骤,以方便及时定位并解决问题。Please provide complete and concise reproduction steps to facilitate timely identification and resolution of the issue. - validations: - required: true - - type: textarea - attributes: - label: 你看到了什么错误?What errors do you see? - validations: - required: true - - type: textarea - attributes: - label: 你期望看到什么?What did you expect to see? - validations: - required: true - - type: textarea - attributes: - label: 还有其他的内容吗?Anything else? - - type: checkboxes - attributes: - label: 你是否愿意提交一份 PR 来修改这个错误?Are you willing to submit a PR? - description: > - 我们期待开发人员和用户的帮助,以解决在 小白羊 中发现的任何问题。 如果您愿意通过提交 PR 来解决此问题,请勾选。We eagerly anticipate developers' and users' support and collaboration in resolving any issues found in 小白羊. If you are willing to offer a solution by submitting a PR to fix this matter, kindly mark the checkbox provided. - options: - - label: 我愿意提供 PR! I'm willing to submit a PR! - - type: markdown - attributes: - value: '非常感谢您的反馈!Thank you very much for your feedback!' \ No newline at end of file diff --git a/aliyunpan/.github/ISSUE_TEMPLATE/config.yml b/aliyunpan/.github/ISSUE_TEMPLATE/config.yml index 8d7bb4b274..1569e85530 100644 --- a/aliyunpan/.github/ISSUE_TEMPLATE/config.yml +++ b/aliyunpan/.github/ISSUE_TEMPLATE/config.yml @@ -2,4 +2,4 @@ blank_issues_enabled: false contact_links: - name: Ask a question or get support url: https://github.com/gaozhangmin/aliyunpan/discussions/categories/q-a - about: Ask a question or request support for XiaoBaiYang \ No newline at end of file + about: Ask a question or request support for XiaoBaiYang diff --git a/aliyunpan/.github/workflows/build.yml b/aliyunpan/.github/workflows/build.yml deleted file mode 100644 index f4b0d06c8f..0000000000 --- a/aliyunpan/.github/workflows/build.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: Build - -on: - push: - tags: - - '*.*.*' - -jobs: - create-release: - permissions: - contents: write - runs-on: ubuntu-20.04 - outputs: - release_id: ${{ steps.create-release.outputs.id }} - release_upload_url: ${{ steps.create-release.outputs.upload_url }} - - steps: - - uses: actions/checkout@v3 - - - name: Get version - id: get_version - uses: battila7/get-version-action@v2 - - - name: Create Release - id: create-release - uses: ncipollo/release-action@v1 - with: - draft: true - name: ${{ steps.get_version.outputs.version }} - tag: ${{ steps.get_version.outputs.version }} - body: "${{ steps.tag.outputs.message }}" - release: - needs: create-release - name: build and release - runs-on: ${{ matrix.os }} - - strategy: - fail-fast: false - matrix: - os: [windows-latest, macos-latest, ubuntu-latest] - - permissions: - contents: write - - steps: - - name: Checkout Git repository - uses: actions/checkout@v3 - - - name: Install Node - uses: actions/setup-node@v3 - with: - node-version: 16 - registry-url: https://registry.npmjs.org/ - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v3 - id: cache-yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - uses: actions/cache@v3 - id: cache-node-modules - with: - path: node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- - - - name: Install Dependencies - if: | - steps.cache-yarn-cache.outputs.cache-hit != 'true' || - steps.cache-node-modules.outputs.cache-hit != 'true' - run: | - npm install -g yarn - yarn install - - - name: Make Aria2c Executable - if: matrix.os != 'windows-latest' - run: | - chmod -R 777 ./static/engine/ - - - name: Build Electron app - uses: samuelmeuli/action-electron-builder@v1.6.0 - with: - release: ${{ startsWith(github.ref, 'refs/tags') }} - github_token: ${{ secrets.github_token }} diff --git a/aliyunpan/.github/workflows/closeissue.yml b/aliyunpan/.github/workflows/closeissue.yml new file mode 100644 index 0000000000..faac7cbdfc --- /dev/null +++ b/aliyunpan/.github/workflows/closeissue.yml @@ -0,0 +1,23 @@ +# https://github.com/actions/stale +name: autoCloseIssue + +on: + schedule: + # 每5天北京时间9点 + - cron: '30 1 1/5 * *' + workflow_dispatch: + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: '由于该问题长期没有更新,将于5天后自动关闭。如有需要可重新打开。' + days-before-stale: 30 + days-before-close: 5 + operations-per-run: 100 \ No newline at end of file diff --git a/aliyunpan/.github/workflows/release.yml b/aliyunpan/.github/workflows/release.yml new file mode 100644 index 0000000000..085806d0f3 --- /dev/null +++ b/aliyunpan/.github/workflows/release.yml @@ -0,0 +1,101 @@ +name: Build Release + +on: + push: + branches: + - main + paths: + - 'package.json' + +jobs: + release: + name: Build Release + strategy: + fail-fast: false + matrix: + os: [ windows-latest, macos-latest, ubuntu-latest ] + runs-on: ${{ matrix.os }} + + permissions: + contents: write + + steps: + - name: Checkout Git repository + uses: actions/checkout@v4 + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 18 + registry-url: https://registry.npmjs.org/ + + - uses: pnpm/action-setup@v3 + name: Install pnpm + id: pnpm-install + with: + version: 8 + run_install: false + + - name: Get Pnpm Store Directory + shell: bash + run: | + echo "PNPM_STORE_PATH=$(pnpm store path)" >> $GITHUB_ENV + + - name: Get Electron Store Directory + shell: bash + run: | + if [[ "${{ runner.os }}" == "Windows" ]]; then + echo "ELECTRON_STORE_PATH=$LOCALAPPDATA\\electron\\cache" >> $GITHUB_ENV + echo "ELECTRON_BUILDER_STORE_PATH=$LOCALAPPDATA\\electron-builder\\cache" >> $GITHUB_ENV + elif [[ "${{ runner.os }}" == "macOS" ]]; then + echo "ELECTRON_STORE_PATH=$HOME/Library/Caches/electron" >> $GITHUB_ENV + else + echo "ELECTRON_STORE_PATH=$HOME/.cache/electron" >> $GITHUB_ENV + echo "ELECTRON_BUILDER_STORE_PATH=$HOME/.cache/electron-builder" >> $GITHUB_ENV + fi + + - name: Setup Pnpm Cache + uses: actions/cache@v4 + with: + path: ${{ env.PNPM_STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Setup Cache Electron + uses: actions/cache@v4 + with: + path: ${{ env.ELECTRON_STORE_PATH }} + key: ${{ runner.os }}-electron-cache-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-electron-cache- + + - name: Setup Cache Electron Builder + uses: actions/cache@v4 + if: matrix.os != 'macos-latest' + with: + path: ${{ env.ELECTRON_BUILDER_STORE_PATH }} + key: ${{ runner.os }}-electron-builder-cache-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-electron-builder-cache- + + - name: Install Dependencies + run: | + pnpm install + + - name: Install Lib For Pacman Build + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install libarchive-tools + + - name: Make Aria2c Executable + if: matrix.os != 'windows-latest' + run: | + chmod -R 777 ./static/engine/ + + - name: Build Electron App + uses: paneron/action-electron-builder@v1.8.1 + with: + package_manager: pnpm + release: true + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/aliyunpan/.gitignore b/aliyunpan/.gitignore index c314163426..463a10f202 100644 --- a/aliyunpan/.gitignore +++ b/aliyunpan/.gitignore @@ -9,6 +9,5 @@ tmp release .idea -yarn.lock -localVersion +localVersion diff --git a/aliyunpan/LICENSE b/aliyunpan/LICENSE index af62d4018c..f288702d2f 100644 --- a/aliyunpan/LICENSE +++ b/aliyunpan/LICENSE @@ -1,21 +1,674 @@ -MIT License + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Copyright (c) 2023 Zhangao + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Preamble -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/aliyunpan/README.md b/aliyunpan/README.md index 9140d28dc4..2a80d18dfb 100644 --- a/aliyunpan/README.md +++ b/aliyunpan/README.md @@ -1,5 +1,5 @@

- NebulaGraph Data Intelligence Suite(ngdi) + NebulaGraph Data Intelligence Suite(ngdi)


English | 中文 @@ -44,12 +44,11 @@

- [![](https://img.shields.io/badge/-%E5%8A%9F%E8%83%BD-blue)](#功能-) [![](https://img.shields.io/badge/-%E7%95%8C%E9%9D%A2-blue)](#界面-) [![](https://img.shields.io/badge/-%E5%AE%89%E8%A3%85-blue)](#安装-) [![](https://img.shields.io/badge/-%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7-blue)](#小白羊公众号-) [![](https://img.shields.io/badge/-%E4%BA%A4%E6%B5%81%E7%A4%BE%E5%8C%BA-blue)](#交流社区-) [![](https://img.shields.io/badge/-%E9%B8%A3%E8%B0%A2-blue)](#鸣谢-) [![](https://img.shields.io/badge/-%E5%A3%B0%E6%98%8E-blue)](#免责声明-) # 功能 [![](https://img.shields.io/badge/-%E5%8A%9F%E8%83%BD-blue)](#功能-) -1.根据阿里云盘Open平台api开发的网盘客户端,支持win7-11,macOS,linux
+1.基于阿里云盘Open平台api开发的网盘客户端,支持win7-11,macOS,linux
2.支持同时登录多个账号管理。
@@ -69,13 +68,21 @@ 10.支持一次性上传/下载百万级的文件/文件夹。
+11. 支持M3u8下载。
+ +12. 影院模式支持自动刮削视频信息。
+ +13. 支持展示视频观看进度。
+ +更多功能见小白羊官网:https://xbysite.pages.dev/ + # # 界面 [![](https://img.shields.io/badge/-%E7%95%8C%E9%9D%A2-blue)](#界面-) - - + + # @@ -123,17 +130,17 @@

- - 点击 `Cancel` 按钮,然后去 `设置` -> `隐私与安全性` 页面,点击 `仍要打开` 按钮,然后在弹出窗口里点击 `打开` 按钮即可,以后就再也不会有任何弹窗告警了 🎉 + - 点击 `Cancel` 按钮,然后去 `设置` -> `隐私与安全性` 页面,点击 `仍要打开` 按钮,然后在弹出窗口里点击 `打开` 按钮即可,以后就再也不会有任何弹窗告警了 🎉 -

- -

+

+ +

- - 如果在 `隐私与安全性` 中找不到以上选项,或启动时提示文件损坏(Apple Silicon版本)。打开 `Terminal.app`,并输入以下命令(中途可能需要输入密码),然后重启 `小白羊云盘` 即可: + - 如果在 `隐私与安全性` 中找不到以上选项,或启动时提示文件损坏(Apple Silicon版本)。打开 `Terminal.app`,并输入以下命令(中途可能需要输入密码),然后重启 `小白羊云盘` 即可: - ```sh - sudo xattr -d com.apple.quarantine /Applications/小白羊云盘.app - ``` + ```sh + sudo xattr -d com.apple.quarantine /Applications/小白羊云盘.app + ``` # @@ -185,7 +192,7 @@ 4.在使用本程序之前,你应了解并承担相应的风险,包括但不限于账号被ban,下载限速等,与本程序无关; -5.如有侵权,请通过邮件与我联系,会及时处理。 +5.如有侵权,请通过邮件zhangao456@proton.me与我联系,会及时处理。 # - \ No newline at end of file + diff --git a/aliyunpan/changelog.txt b/aliyunpan/changelog.txt deleted file mode 100644 index 27de4cc6e9..0000000000 --- a/aliyunpan/changelog.txt +++ /dev/null @@ -1,329 +0,0 @@ -#### 2023/2/24 -1.修复UserDeviceIllegality问题 -2.修复下载功能 -3.修复打包问题 - -#### 2022/10/24 - -注:因上传下载功能尚未做完,本次仅同步代码不发布安装包 -0. 恢复文件上传功能,支持单次上传百万个文件/文件夹,优化上传任务的数据库文件体积过大的问题 -1. 优化sha1并发计算逻辑,增加文件sha1的缓存,提升上传大文件的性能 -2. Add 上传前弹窗,设置重名冲突(删除/覆盖/自动重命名/不上传) -3. Add 单独的总上传速度限制设置 -4. Add 上传文件时只上传可以秒传的文件的设置 -5. Add 单独的上传文件时的并发执行数设置(最大50文件同时上传) -6. Add 左侧文件树文件夹对拖放上传的支持 -7. Add 上传中列表里文件夹视图 -8. Add 优先上传小文件(100M)的支持 -9. Fix 按住Ctrl时,点击CheckBox和点击空白处,选中结果不一致的BUG -10. Fix 右键菜单-打开文件位置,没有选中和滚动到指定文件的BUG -11. Fix 断网时,重试上传任务,可能导致上传任务的断点续传进度丢失的BUG -#### 2022/09/18 - -注:因上传下载功能尚未做完,本次仅同步代码不发布安装包 - -1. Fix v3.5.23alpha中的20余处小BUG -2. Fix 登录时遇到二次短信验证时不能继续登录的BUG -3. 完善批量重命名功能 -4. 完善颜色标记功能 -5. 优化文件列表加载显示逻辑,现在很优雅了 -6. 优化文件名排序,支持中文数字排序,按win习惯英文在前中文在后 -7. 优化文件夹树性能(全部文件夹列出速度加快3倍,20万文件夹不卡顿,树内存占用减少60%) -8. 恢复显示文件夹体积(可按大小排序),优化计算文件夹体积的逻辑(速度加快计算量减少) -9. 底部增加网盘空间信息和文件夹内文件总数量 -10. 增加复制文件名和复制目录树的功能 -11. 适配更新vite3.1.2,更新全部package到最新版 - - -#### 2022/05/23 - -v3.5.23alpha 开发人员测试版本 - -1. Fix 偶发的登录后显示空白窗口的 BUG -2. Fix 登录太多账号时切换账号弹窗不能正确显示的 BUG -3. 优化长时间不用后需要重新登录的问题 -4. Fix 偶发的下载中显示空白列表的 BUG -5. Fix 多次弹出升级提示窗口的 BUG -6. Fix 目录下超过 200 个文件夹不能显示的 BUG -7. 重制 APP 设置页面,分组设置,配置保存到 setting.config -8. 优化快捷键(现在很多操作都支持键盘快捷键) - --- - -v3.05.23.alpha 新增加的功能 - -文件恢复,放映室,网页版播放器,彻底删除菜单,文件属性菜单,所有列表支持右键菜单, -整盘高级搜索,文件快速筛选,右侧文件拖放移动,文件标记,文件夹快捷方式,我订阅的公众号, -重复文件清理,扫描大文件,扫描重复文件,扫描违规文件,扫描空文件夹,网盘相册间复制, -批量重命名 - - - -#### 2022/04/14 - - -1. 修正因阿里云盘官网升级导致的无法使用 -2. 优化访问频次问题 -3. 修正违禁视频详情BUG - - -#### 2021/12/05 - - -1. 修正网盘内文件路径过长时下载失败 BUG -2. 优化快捷键功能(帮助文档里有完整的快捷键说明) -3. 增加收藏夹的搜索功能 -4. 优化后退按钮(以前是返回父文件夹,现在是后退,最多后退 20 步) -5. 增加优先下载小文件选项 -6. 增加雪碧图里视频信息的显示和保存雪碧图按钮 -7. 网盘页顶部路径默认隐藏需要在设置里勾选显示 -8. 修正m3u8播放链接15分钟后失效的 BUG (现在4小时) -9. 修正之前部分版本代理设置被覆盖的 BUG -10. 增加自定义缓存位置功能 -11. 增加帮助文档 - - -#### 2021/11/29 - -1. 修正 2.11.28 下载显示出错的 BUG -1. 修正登录时遇到二次验证导致点击登录按钮无反应的 BUG -2. 修正自 v2.11.07 开始不能上传体积为 0 的文件的 BUG -3. 修正 v2.11.16 上传文件时占用内存过多的问题 -4. 11.16 里 60 文件同时上传会占用大量内存,11.28 里优化为只占用 400MB 以内的内存 - -5. 修正传输完自动关机触发时机不准确的 BUG -6. 修正上传时遇到没有访问权限的文件/文件夹时上传中队列卡住的 BUG -7. 增加视频文件洗码功能 -8. 增加右侧文件拖动到左侧文件夹树(移动文件)功能 -9. 优化远程 Aria2 下载功能并修复断线后自动重连 -10. 优化本地 Aria2,无法连接时会尝试自动重启一次 Aria2 进程 -11. 增加任务栏的下载中上传中进度提示(win/mac) -12. 底部状态栏显示总传输的预估剩余时间 -13. 增加上传时跳过同名文件的设置项 -14. 优化盘内文件搜索支持选择分类 -15. 一些细节优化 - - - -#### 2021/11/14 -1. 增加创建分享、编辑分享、管理分享功能 -2. 完善导入分享功能,支持部分导入和完整导入 -3. 增加网盘内文件搜索功能 -4. 增加下载时自动过滤违规文件的设置 -5. 增加对禁止分享的文件的图标 -6. 增加文件/文件夹置顶功能 -7. 增加主题跟随系统 -8. 增加下载上传完成后自动关机设置 -9. 完善 mac 和 linux 下自定义播放软件功能 -10. 恢复视频文件雪碧图,增加复制 M3U8 链接和原始视频链接功能 -11. 修正 linux 下上传时会自动过滤软链接文件 - - - -#### 2021/11/07 - -1. 优化一次性上传或下载大量文件时的界面卡顿(参阅 挑战一百万.md) -2. 创建日期文件夹支持编号了 -3. 新增定时清理回收站功能 -4. 新增我创建的分享列表功能(没做完) -5. 增加文件列表显示限制(减少加载中) -6. 恢复导入阿里云盘分享链接功能(仅全部导入) -7. 恢复新建文件功能 -8. 恢复版本升级提示功能 -9. 修正 windows 下载位置不能选择根目录的 BUG -10. 修正不能上传大于 97.6GB 的文件的问题 -11. 修正上传速度显示不准确的 BUG -12. 修正不能同时下载同一个文件(下载到不同的位置)的 BUG -13. 优化部分文件格式图标 - - - -#### 2021/10/31 - -1. 增加文件列表的缩略图模式 -2. 修正v2.10.17和v2.10.19版本里一个严重的BUG(删除文件时可能会错误选中父文件夹一起删除) -3. 去除彻底删除按钮 -4. 回收站增加清理回收站按钮(一键删除回收站内全部文件) -5. 增加上传和下载的文件过滤功能(自动跳过特定格式的文件) -6. 修正左侧文件夹树和右侧文件列表的互动关联 -7. 恢复重命名功能和移动复制功能 - - -#### 2021/10/19 - -1. 优化文件列出逻辑,节省一半的等待加载中时间 - -2. 优化v2.9一个文件夹直接包含大量子文件夹时的卡顿和内存剧增问题 - 一个文件夹里直接包含 17000 个子文件夹时,打开文件夹 v2.9 需要占用 700MB 内存, v2.10 需要占用 190MB 内存 - v2.9 当一个文件夹里包含 3000 个以上的子文件夹时,小白羊文件夹树会卡顿 - v2.10 无所谓多少个子文件夹,不会卡顿 - -3. 优化v2.9网盘包含有巨量文件夹时,启动后前几秒会卡顿的问题 - -4. 优化v2.9上下传记录的本地数据库体积 - 长期大量上传下载会产生较大的本地记录数据,现在会自动清理 - -5. 修正v2.9统计文件夹体积功能的本地缓存和运行时CPU内存占用 - 开启统计文件夹体积功能后,我网盘里有 2 万个文件夹和 31 万个文件,v2.9 会产生 167MB 的缓存,v2.10 会产生 8MB 的缓存 - 修正了一个会导致 CPU 和内存占用高的 BUG(用户账号 token 失效时,会因为定时统计功能导致 CPU 和内存占用很高) - -6. 优化在线预览视频现在支持很多种播放器了 - 当前只适配了 windows (测试了 MPV,Potplayer,VLC media player,KMPlayer,恒星播放器,SMPlayer) - macos 和 linux 稍后会适配,现在仍旧只能用 mpv 播放器 - -7. 增加在线预览 word/excel/ppt/pdf 文件的功能 - 当前,大部分音视频格式,大部分图片格式,word/excel/ppt/pdf/txt,200 余种常见文本格式 都可以在线预览了 - -8. 修正在文件夹里搜索后,拖动搜索结果里的文件上传,上传文件名错误的 BUG - -9. 优化上传前的 sha1 校验速度,提升上传速度 - -10. 美化了一下界面 -11. 增加是否按照完整网盘路径保存的设置 -12. 增加关闭窗口立即退出的设置 -13. 增加双击才打开文件、文件夹的设置 -14. 增加清理缓存的设置 -15. 增加运行日志的设置 -16. 文件夹树的宽度可以拖动调整了 -17. 图片、Office、文本预览现在是单独窗口了 -18. 修正一些文件格式识别不准确的 BUG - -#### 2021/09/24 - -1. 修正上传 20GB 以上的文件时,断点续传时进度不准确的 BUG -2. 取消文件列表的加载中状态提示,快速展现文件列表 -3. 更新文件列表缓存方式,数据库文件体积减少 73% (14 万个文件从 240MB 降低为 60MB) -4. 增加是否统计文件夹体积的设置开关,减少网盘内文件过多时的白屏问题 -5. 同步 v2.9.24 源码到 github - -#### 2021/09/19 - -1. 删除秒传相关功能 -2. 修正 v2.8.30 里 aria2 远程模式连接失败的 BUG -3. 修正偶发文件列表只显示占位符不显示文件名的 BUG -4. 修正移动文件后选中文件数显示错误的 BUG -5. 修正批量重命名取消勾选文件夹时子文件名计算错误的 BUG -6. 修正批量重命名点击刷新后因一直加载,不能关闭的 BUG -7. 增加对文件名结尾的点和空格的清理,修正这些文件下载失败的 BUG -8. 修正闲置长时间后上传文件可能出现获取上传地址失败的 BUG -9. 减少因并发数太高容易出现的操作失败 BUG -10. 修正等宽图片预览时,切换下一张后滚动条没有自动回到顶部的 BUG -11. 增加文件列表(F5 键刷新文件,Back 键返回上级文件夹),等宽图片预览(← 上一张,→ 下一张)的快捷键 -12. 增加点击头像图片时自动刷新网盘空间用量 -13. 增加文件夹独立排序选项 -14. 增加直接彻底删除文件的右键菜单 - -15. 升级数据库架构,提升了加载文件列表的性能,本周重点就是此项,性能提升涉及方方面面的细节,大部分以前加载慢的功能都有了极明显的提升,例如一次性上传包含 10 万个文件的文件夹,不会出现任何卡顿了 - -16. 修正 v2.9.15 里长时间后上传文件时出现获取上传地址失败的 BUG -17. 增加上传/下载任务出错后等 1 分钟自动重试功能,可以放心挂机下载、挂机上传了 - -#### 2021/08/30 -1. Fix 修正 v1.6.29 大量上传下载后会生成大体积的 数据库 的 BUG -2. Fix 修正 v1.6.29 导入阿里云盘分享链接失败的 BUG -3. Fix 修正 v1.6.29 上传途中重启程序后,重新上传不会断点续传的 BUG -4. Fix 修正部分违规视频不能播放的 BUG,现在可以使用"优先播放转码视频"模式播放了 - -# - -1. Add 增加阿里云盘官方登录接口(手机短信、账号密码、APP 扫码登录) -2. Add 增加多个账号同时登录、切换功能 -3. Add 增加 Aria 远程连接设置,可以把文件直接下载到远程电脑/VPS/NAS/Docker -4. Add 增加文件名颜色标记,批量标记功能,观看视频自动标记功能 -5. Add 增加文件、文件夹详情功能(文件夹大小,包含文件数),视频文件的雪碧图 -6. Add 增加新的图片预览模式,可以放大/缩小/旋转/幻灯片播放 -7. Add 增加代码高亮/ json 格式化显示 / txt 在线预览功能 -8. Add 增加快速创建日期格式的文件夹 -9. Add 增加可选择文件夹是否和文件一起排序了 -10. Add 增加所有文件夹体积的显示,可以按照体积排序文件夹了 -11. Add windows 上支持 Potplayer 播放器了 -12. Add 顶部快捷路径跳转和区间选择功能 - -# - -1. Pro 优化文件复制功能,可极速复制 TB 级/上万文件 到网盘的其他位置 -2. Pro 优化导入分享功能,在导入时可以选择网盘里的保存位置,可以勾选要保存的 文件/文件夹 -3. Pro 优化上传功能,现在部分不能秒传的大文件,上传前不再需要计算 sha1 了(减少上传时间) -4. Pro 优化 sha1 计算逻辑和性能,同时最多 3 个文件计算 sha1,机械硬盘不会掉速,CPU 不会爆满 -5. Pro 现在 windows/macos/linux 都支持拖拽文件、文件夹上传了 -6. Pro 优化批量重命名功能,支持勾选文件,支持重命名多级子文件夹,支持 替换/删除/增加/序号/随机字符 等方式 -7. Pro 优化在线解压功能,支持全部解压和勾选文件解压,支持有密码的压缩包 - -#### 2021/06/29 -Fix 优化重命名、搜索输入框大小 -Fix 修正下载中、上传中页面因进度条动画导致的GPU占用过高的BUG -Fix 中文名导致偶有macos启动失败的BUG -Add 导入阿里云盘分享链接的功能 -Add 导入115网盘分享链接的功能 - - -#### 2021/06/21 -Fix 显示用户昵称和头像 -Fix 完善对字体的支持(可以随意更换自己喜欢的字体了),完善文字大小设置功能 -Fix 完善在线预览图片功能(支持旋转,文件夹内全部图片上一张下一张查看) -Fix 文件名是.(点)时导致的创建下载任务失败的BUG -Fix 创建文件夹太快偶发点击文件夹名不能进入的BUG -Add Windows上支持批量拖拽文件/文件夹上传 -Add 选择文件/文件夹计算秒传信息保存到网盘内txt文件的功能 -Add 导入txt文件类型的秒传链接(支持文件夹嵌套) -Add 新增相册功能(文件可以在相册和网盘之间移动复制) - - -#### 2021/06/13 -Fix 在下载大文件时Aria在某些系统上强制分配硬盘BUG导致下载进度卡死 -Fix 优化Aria的连接性,减少出错崩溃 -Fix Mac版输出大量无用日志的BUG -Fix 一堆UI细节上的完善 -Add 复制文件到...的功能(官方只有移动到...) -Add 批量重命名功能(替换/删除字符,增加前缀,正则表达式替换) -Add 聚合搜索功能(当前搜不到什么,要等以后大家主动分享) -Add 初步支持在线解压缩(zip,rar) -Ver 更新到Flutter2.2.1 - - -#### 2021/06/10 -Fix 因阿里云盘API升级导致的无法加载文件列表的BUG -Fix 选择上传文件夹时 可能 需要长时间等待的BUG -Fix 批量下载大量子文件夹时 可能 需要长时间等待的BUG -Fix 修正回收站内文件无法在线播放的BUG -Fix 增加一些在线播放视频格式的支持(m2ts/hevc....) -Add 增加对违规文件的标识 -Add 增加深色模式 -Add 下载失败时的一些错误提示 -Del 去除创建秒传链接的功能,仅支持导入秒传链接(115:// 、aliyun://) - -#### 2021/06/06 -1. Fix 批量下载时只解析了第一个选中的文件夹的严重BUG -2. Fix 大量操作更新为异步操作,极大的减少了操作等待时间 -3. Fix 因阿里云盘升级导致扫码登陆失败的BUG -4. Add 支持导入李恒道版本秒传链接 - - - -#### 2021/06/05 -1. Add 秒传短链接功能(创建秒传链接、导入别人分享的短链接、短链接本地历史记录) -2. Add 增加115链接批量导入功能(靠运气) -3. Add 增加在线预览文本文件功能 - -4. Fix 因阿里云盘升级导致扫码登陆失败的BUG -5. Fix 阿里云盘对单次批量操作最多限制100条的限制 -6. Fix 在线预览图片时图片大小缩放BUG -7. Fix 按文件名排序时不准确的BUG - - -#### 2021/05/31 -1. Add 上传文件、文件夹功能 -2. Add 在线预览图片 -3. Add 移动文件、文件夹功能 -4. Fix 优化启动时启动后台提示 -5. Fix 延长下载链接时效(15分钟->4小时) -6. Fix 文件夹内包含大量文件时多次操作可能回重复拉取的BUG - - - -#### 2021/05/25 -1. 上传第一个开发中版本仅供测试 -2. 支持 扫码登录/Cookie登录 -3. 支持 阿里云盘基本功能 -4. 支持 在线预览全格式原画视频(非转码) -5. 支持 批量下载文件/文件夹,只要阿里云不限速,就是满速下载 diff --git a/aliyunpan/electron-builder.json b/aliyunpan/electron-builder.json index 904948784b..ee0a30ab69 100644 --- a/aliyunpan/electron-builder.json +++ b/aliyunpan/electron-builder.json @@ -22,8 +22,9 @@ "hardenedRuntime": true, "category": "public.app-category.utilities", "extraResources": [ - { "from": "./static/engine/darwin/${arch}", "to": "./engine" }, - { "from": "./static/images/icon.icns", "to": "./images/icon.icns"} + { "from": "./static/images/icon.icns", "to": "./images/icon.icns"}, + { "from": "./static/images/icon_24x24.png", "to": "./images/icon_24x24.png"}, + { "from": "./static/engine/darwin/${arch}", "to": "./engine"} ], "target": [ { "target": "dmg", "arch": [ "x64", "arm64" ] } @@ -34,11 +35,15 @@ "category": "Network", "artifactName": "XBYDriver-${version}-linux-${arch}.${ext}", "extraResources": [ + { "from": "./static/images/icon_24x24.png", "to": "./images/icon_24x24.png"}, + { "from": "./static/images/icon_64x64.png", "to": "./images/icon_64x64.png"}, + { "from": "./static/images/icon_256x256.png", "to": "./images/icon_256x256.png"}, { "from": "./static/engine/linux/${arch}", "to": "./engine"} ], "target": [ { "target": "AppImage", "arch": [ "x64", "arm64", "armv7l" ] }, - { "target": "deb", "arch": [ "x64", "arm64", "armv7l" ] } + { "target": "deb", "arch": [ "x64", "arm64", "armv7l" ] }, + { "target": "pacman", "arch": [ "x64", "arm64", "armv7l" ] } ] }, "win": { @@ -46,11 +51,12 @@ "artifactName": "XBYDriver-${version}-win-${arch}.${ext}", "requestedExecutionLevel": "asInvoker", "extraResources": [ - { "from": "./static/engine/win32/${arch}", "to": "./engine"}, - { "from": "./static/images/icon_256x256.ico", "to": "./images/icon_256x256.ico"} + { "from": "./static/images/icon_64x64.png", "to": "./images/icon_64x64.png"}, + { "from": "./static/images/icon_256x256.ico", "to": "./images/icon_256x256.ico"}, + { "from": "./static/engine/win32/${arch}", "to": "./engine"} ], "target": [ - { "target": "nsis", "arch": [ "x64", "ia32", "arm64"] } + { "target": "nsis", "arch": [ "x64", "ia32", "arm64" ] } ] }, "dmg": { @@ -69,7 +75,10 @@ }, "publish": [ { + "owner": "gaozhangmin", + "repo": "aliyunpan", "provider": "github", + "publishAutoUpdate": false, "releaseType": "draft" } ] diff --git a/aliyunpan/electron/main/core/ipcEvent.ts b/aliyunpan/electron/main/core/ipcEvent.ts index babb399a55..a4888fb06c 100644 --- a/aliyunpan/electron/main/core/ipcEvent.ts +++ b/aliyunpan/electron/main/core/ipcEvent.ts @@ -1,14 +1,14 @@ import { AppWindow, createElectronWindow, Referer, ua } from './window' import path from 'path' import is from 'electron-is' -import { app, BrowserWindow, dialog, ipcMain, session, shell } from 'electron' +import { app, BrowserWindow, dialog, ipcMain, Menu, powerSaveBlocker, session, shell } from 'electron' import { existsSync, writeFileSync } from 'fs' import { exec, execFile, spawn, SpawnOptions } from 'child_process' import { ShowError } from './dialog' -// @ts-ignore -import {getResourcesPath, getStaticPath, getUserDataPath} from '../utils/mainfile' +import { getStaticPath, getUserDataPath } from '../utils/mainfile' import { portIsOccupied } from '../utils' +let psbId: any export default class ipcEvent { private constructor() { } @@ -16,6 +16,7 @@ export default class ipcEvent { static handleEvents() { this.handleWebToElectron() this.handleWebToElectronCB() + this.handleShowContextMenu() this.handleWebShowOpenDialogSync() this.handleWebShowSaveDialogSync() this.handleWebShowItemInFolder() @@ -44,13 +45,23 @@ export default class ipcEvent { let mainWindow = AppWindow.mainWindow if (data.cmd && data.cmd === 'close') { if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide() + } else if (data.cmd && data.cmd === 'relaunch') { + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.destroy() + mainWindow = undefined + } + try { + app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) }) + app.exit(0) + } catch { + } } else if (data.cmd && data.cmd === 'exit') { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.destroy() mainWindow = undefined } try { - app.exit() + app.exit(0) } catch { } } else if (data.cmd && data.cmd === 'minsize') { @@ -63,6 +74,26 @@ export default class ipcEvent { mainWindow.maximize() } } + } else if (data.cmd && data.cmd === 'minsizeAudio') { + if (AppWindow.auidoWindow && !AppWindow.auidoWindow.isDestroyed()) AppWindow.auidoWindow.minimize() + } else if (data.cmd && data.cmd === 'maxsizeAudio') { + if (AppWindow.auidoWindow && !AppWindow.auidoWindow.isDestroyed()) { + if (AppWindow.auidoWindow.isMaximized()) { + AppWindow.auidoWindow.unmaximize() + } else { + AppWindow.auidoWindow.maximize() + } + } + } else if (data.cmd && data.cmd === 'minsizeVideo') { + if (AppWindow.videoWindow && !AppWindow.videoWindow.isDestroyed()) AppWindow.videoWindow.minimize() + } else if (data.cmd && data.cmd === 'maxsizeVideo') { + if (AppWindow.videoWindow && !AppWindow.videoWindow.isDestroyed()) { + if (AppWindow.videoWindow.isMaximized()) { + AppWindow.videoWindow.unmaximize() + } else { + AppWindow.videoWindow.maximize() + } + } } else if (data.cmd && (Object.hasOwn(data.cmd, 'launchStart') || Object.hasOwn(data.cmd, 'launchStartShow'))) { const launchStart = data.cmd.launchStart @@ -84,6 +115,19 @@ export default class ipcEvent { !launchStartShow && settings.args.push('--openAsHidden') } app.setLoginItemSettings(settings) + } else if (data.cmd && data.cmd === 'preventSleep') { + if (data.flag) { + if (psbId && powerSaveBlocker.isStarted(psbId)) { + return + } + psbId = powerSaveBlocker.start('prevent-app-suspension') + } else { + if (typeof psbId === 'undefined' || !powerSaveBlocker.isStarted(psbId)) { + return + } + powerSaveBlocker.stop(psbId) + psbId = undefined + } } else { event.sender.send('ElectronToWeb', 'mainsenddata') } @@ -109,6 +153,25 @@ export default class ipcEvent { }) } + private static handleShowContextMenu() { + ipcMain.on('show-context-menu', (event, params) => { + const { showCut, showCopy, showPaste } = params + const window = BrowserWindow.fromWebContents(event.sender) + // 制作右键菜单 + let template: Array = [ + // 设置选项是否可见 + { role: 'selectAll', label: '全选' }, + { role: 'copy', label: '复制', visible: showCopy }, + { role: 'cut', label: '剪切', visible: showCut }, + { role: 'paste', label: '粘贴', visible: showPaste }, + { role: 'undo', label: '撤销' } + ] + // 显示菜单 + const contextMenu = Menu.buildFromTemplate(template) + contextMenu.popup({ window }) + }) + } + private static handleWebShowOpenDialogSync() { ipcMain.on('WebShowOpenDialogSync', (event, config) => { dialog.showOpenDialog(AppWindow.mainWindow!, config).then((result) => { @@ -174,11 +237,10 @@ export default class ipcEvent { command = `${argsToStr(data.command)}` } const subProcess = spawn(command, data.args, options) - const isRunning = process.kill(subProcess.pid, 0) subProcess.unref() event.returnValue = { pid: subProcess.pid, - isRunning: isRunning, + subProcess: subProcess, execCmd: data, options: options, exitCode: subProcess.exitCode @@ -292,9 +354,11 @@ export default class ipcEvent { windowsHide: false, windowsVerbatimArguments: true } + const fileAllocation = is.macOS() ? 'none' : (is.windows() ? 'falloc' : 'trunc') const args = [ `--stop-with-process=${argsToStr(process.pid)}`, `--conf-path=${argsToStr(confPath)}`, + `--file-allocation=${argsToStr(fileAllocation)}`, `--rpc-listen-port=${argsToStr(listenPort)}`, '-D' ] @@ -455,13 +519,20 @@ export default class ipcEvent { } private static handleWebOpenWindow() { + // let winWidth = AppWindow.winWidth + // if (winWidth < 1080) winWidth = 1080 ipcMain.on('WebOpenWindow', (event, data) => { - const win = createElectronWindow(AppWindow.winWidth, AppWindow.winHeight, true, 'main2', data.theme) + const win = createElectronWindow(data.width || AppWindow.winWidth, data.height || AppWindow.winHeight, true, 'main2', data.theme) win.on('ready-to-show', function() { win.webContents.send('setPage', data) win.setTitle('预览窗口') win.show() }) + if (data.page === 'PageAudio') { + AppWindow.auidoWindow = win + } else if (data.page === 'PageVideo') { + AppWindow.videoWindow = win + } }) } diff --git a/aliyunpan/electron/main/core/window.ts b/aliyunpan/electron/main/core/window.ts index ddd5dc018f..c77b25297f 100644 --- a/aliyunpan/electron/main/core/window.ts +++ b/aliyunpan/electron/main/core/window.ts @@ -1,17 +1,18 @@ -import { app, BrowserWindow, Menu, MenuItem, MessageChannelMain, nativeTheme, screen, session, Tray } from 'electron' -// @ts-ignore -import { getAsarPath, getResourcesPath, getStaticPath, getUserDataPath } from '../utils/mainfile' -import fs, { existsSync, readFileSync, writeFileSync } from 'fs' +import { app, BrowserWindow, ipcMain, Menu, MessageChannelMain, nativeTheme, screen, shell, Tray } from 'electron' +import { getAsarPath, getStaticPath, getUserDataPath } from '../utils/mainfile' +import { existsSync, readFileSync, writeFileSync } from 'fs' import is from 'electron-is' import { ShowErrorAndRelaunch } from './dialog' - -export const ua = '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' -export const Referer = 'https://www.aliyundrive.com/' +const DEBUGGING = !app.isPackaged +export const ua = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) aDrive/4.12.0 Chrome/108.0.5359.215 Electron/22.3.24 Safari/537.36' +export const Referer = 'https://www.alipan.com/' export const AppWindow: { mainWindow: BrowserWindow | undefined uploadWindow: BrowserWindow | undefined downloadWindow: BrowserWindow | undefined + videoWindow: BrowserWindow | undefined + auidoWindow: BrowserWindow | undefined appTray: Tray | undefined winWidth: number winHeight: number @@ -20,18 +21,13 @@ export const AppWindow: { mainWindow: undefined, uploadWindow: undefined, downloadWindow: undefined, + videoWindow: undefined, + auidoWindow: undefined, appTray: undefined, winWidth: 0, winHeight: 0, winTheme: '' } -export const AppMenu: { - menuEdit: Electron.Menu | undefined - menuCopy: Electron.Menu | undefined -} = { - menuEdit: undefined, - menuCopy: undefined -} let timerUpload: NodeJS.Timeout | undefined const debounceUpload = (fn: any, wait: number) => { @@ -107,7 +103,10 @@ export function createMainWindow() { AppWindow.mainWindow.on('resize', () => { debounceResize(function() { try { - if (AppWindow.mainWindow && AppWindow.mainWindow.isMaximized() == false && AppWindow.mainWindow.isMinimized() == false && AppWindow.mainWindow.isFullScreen() == false) { + if (AppWindow.mainWindow + && !AppWindow.mainWindow.isMaximized() + && !AppWindow.mainWindow.isMinimized() + && !AppWindow.mainWindow.isFullScreen()) { const s = AppWindow.mainWindow!.getSize() const configJson = getUserDataPath('config.json') writeFileSync(configJson, `{"width":${s[0].toString()},"height": ${s[1].toString()}}`, 'utf-8') @@ -133,6 +132,7 @@ export function createMainWindow() { AppWindow.mainWindow.on('ready-to-show', function() { AppWindow.mainWindow!.webContents.send('setPage', { page: 'PageMain' }) AppWindow.mainWindow!.webContents.send('setTheme', { dark: nativeTheme.shouldUseDarkColors }) + AppWindow.mainWindow.maximize() AppWindow.mainWindow!.setTitle('阿里云盘小白羊') if (is.windows() && process.argv && process.argv.join(' ').indexOf('--openAsHidden') < 0) { AppWindow.mainWindow!.show() @@ -156,24 +156,11 @@ export function createMainWindow() { createDownload() } -export function createMenu() { - AppMenu.menuEdit = new Menu() - AppMenu.menuEdit.append(new MenuItem({ label: '剪切', role: 'cut' })) - AppMenu.menuEdit.append(new MenuItem({ label: '复制', role: 'copy' })) - AppMenu.menuEdit.append(new MenuItem({ label: '粘贴', role: 'paste' })) - AppMenu.menuEdit.append(new MenuItem({ label: '删除', role: 'delete' })) - AppMenu.menuEdit.append(new MenuItem({ label: '全选', role: 'selectAll' })) - AppMenu.menuCopy = new Menu() - AppMenu.menuCopy.append(new MenuItem({ label: '复制', role: 'copy' })) - AppMenu.menuCopy.append(new MenuItem({ label: '全选', role: 'selectAll' })) -} - - export function createTray() { const trayMenuTemplate = [ { label: '显示主界面', - click: function () { + click: function() { if (AppWindow.mainWindow && AppWindow.mainWindow.isDestroyed() == false) { if (AppWindow.mainWindow.isMinimized()) AppWindow.mainWindow.restore() AppWindow.mainWindow.show() @@ -185,7 +172,7 @@ export function createTray() { }, { label: '彻底退出并停止下载', - click: function () { + click: function() { if (AppWindow.mainWindow) { AppWindow.mainWindow.destroy() AppWindow.mainWindow = undefined @@ -242,12 +229,9 @@ export function createElectronWindow(width: number, height: number, center: bool preload: getAsarPath('dist/electron/preload/index.js') } }) - - win.removeMenu() - if (is.dev()) { - const url = `http://localhost:${process.env.VITE_DEV_SERVER_PORT}` - win.loadURL(url, { userAgent: ua, httpReferrer: Referer }) + if (DEBUGGING) { + win.loadURL(process.env.VITE_DEV_SERVER_URL, { userAgent: ua, httpReferrer: Referer }) } else { win.loadURL('file://' + getAsarPath('dist/' + page + '.html'), { userAgent: ua, @@ -255,8 +239,10 @@ export function createElectronWindow(width: number, height: number, center: bool }) } - if (is.dev() && devTools) { - if (width < 100) win.setSize(800, 600) + if (DEBUGGING && devTools) { + if (width < 100) { + win.setSize(800, 680) + } win.show() win.webContents.openDevTools({ mode: 'bottom' }) } else { @@ -266,11 +252,14 @@ export function createElectronWindow(width: number, height: number, center: bool } }) } - win.webContents.on('before-input-event', (_, input: Electron.Input) => { - if (input.type === 'keyDown' && input.control && input.shift && input.key === 'F12') { - win.webContents.isDevToolsOpened() - ? win.webContents.closeDevTools() - : win.webContents.openDevTools({ mode: 'undocked' }) + if (page == 'main2') { + handleWinCmd(win) + } + handleWebView(win) + win.webContents.on('will-navigate', (e, url) => { + e.preventDefault() + if (!url.includes(process.env.VITE_DEV_SERVER_URL)) { + shell.openExternal(url) } }) win.webContents.on('did-create-window', (childWindow) => { @@ -281,8 +270,83 @@ export function createElectronWindow(width: number, height: number, center: bool return win } +function handleWebView(win: BrowserWindow) { + // 处理DevTools + win.webContents.on('before-input-event', (_, input: Electron.Input) => { + if (input.type === 'keyDown' && input.control && input.shift && input.key === 'F12') { + win.webContents.isDevToolsOpened() + ? win.webContents.closeDevTools() + : win.webContents.openDevTools({ mode: 'undocked' }) + } + }) + // 处理webview跳转 + win.webContents.addListener('did-attach-webview', (event, webContent) => { + webContent.on('before-input-event', (_, input: Electron.Input) => { + if (input.type === 'keyDown' && input.control && input.shift && input.key === 'F12') { + webContent.isDevToolsOpened() + ? webContent.closeDevTools() + : webContent.openDevTools({ mode: 'undocked' }) + } + }) + // 不允许的网址则阻止页面跳转并拉取浏览器展示页面 + webContent.setWindowOpenHandler((details) => { + let url = details.url + if (!/(aliyundrive|alipan).com\/s\/[0-9a-zA-Z_]{11,}/.test(url)) { + webContent.loadURL(url) + } else { + win.webContents.send('webview-new-window', webContent.id, details) + } + return { action: 'deny' } + }) + webContent.on('will-redirect', (e, url) => { + if (!/(aliyundrive|alipan).com\/s\/[0-9a-zA-Z_]{11,}/.test(url)) { + webContent.loadURL(url) + } else { + win.webContents.send('webview-redirect', webContent.id, url) + } + e.preventDefault() + }) + // 拦截链接跳转 + webContent.on('will-navigate', (e, url) => { + if (/(aliyundrive|alipan).com\/s\/[0-9a-zA-Z_]{11,}/.test(url)) { + e.preventDefault() + } + }) + }) +} + +function handleWinCmd(win: BrowserWindow) { + ipcMain.on('WebToWindow', (event, data) => { + if (data.cmd && data.cmd === 'close') { + if (win && !win.isDestroyed()) win.close() + } else if (data.cmd && data.cmd === 'minsize') { + if (win && !win.isDestroyed()) win.minimize() + } else if (data.cmd && data.cmd === 'top') { + if (win && !win.isDestroyed()) { + if (win.isAlwaysOnTop()) { + event.returnValue = 'untop' + win.setAlwaysOnTop(false) + } else { + event.returnValue = 'top' + win.setAlwaysOnTop(true, 'status') + } + } + } else if (data.cmd && data.cmd === 'maxsize') { + if (win && !win.isDestroyed()) { + if (win.isMaximized()) { + event.returnValue = 'unmaximize' + win.unmaximize() + } else { + event.returnValue = 'maximize' + win.maximize() + } + } + } + }) +} + function creatUploadPort() { - debounceUpload(function () { + debounceUpload(function() { if (AppWindow.mainWindow && AppWindow.uploadWindow && AppWindow.uploadWindow.isDestroyed() == false) { const { port1, port2 } = new MessageChannelMain() AppWindow.mainWindow.webContents.postMessage('setUploadPort', undefined, [port1]) @@ -322,6 +386,7 @@ function createUpload() { createUpload() } }) + // AppWindow.uploadWindow.webContents.openDevTools({ mode: 'undocked' }) AppWindow.uploadWindow.hide() } @@ -348,4 +413,4 @@ function createDownload() { AppWindow.downloadWindow.webContents.closeDevTools() AppWindow.downloadWindow.hide() -} \ No newline at end of file +} diff --git a/aliyunpan/electron/main/index.ts b/aliyunpan/electron/main/index.ts index 6f9601b0a2..5a82c64661 100644 --- a/aliyunpan/electron/main/index.ts +++ b/aliyunpan/electron/main/index.ts @@ -10,4 +10,4 @@ app.setAboutPanelOptions({ applicationVersion: '30' }) -const appLaunch = new launch() \ No newline at end of file +const appLaunch = new launch() diff --git a/aliyunpan/electron/main/launch.ts b/aliyunpan/electron/main/launch.ts index 6141e9f7a8..d10f9559c2 100644 --- a/aliyunpan/electron/main/launch.ts +++ b/aliyunpan/electron/main/launch.ts @@ -1,4 +1,4 @@ -import { AppWindow, createMainWindow, createMenu, createTray } from './core/window' +import { AppWindow, createMainWindow, createTray } from './core/window' import { app, ipcMain, session } from 'electron' import is from 'electron-is' import fixPath from 'fix-path' @@ -8,10 +8,11 @@ import { existsSync, readFileSync, writeFileSync } from 'fs' import { EventEmitter } from 'node:events' import exception from './core/exception' import ipcEvent from './core/ipcEvent' +import path from 'path' type UserToken = { access_token: string; - access_token_v2: string; + open_api_access_token: string; user_id: string; refresh: boolean } @@ -19,7 +20,7 @@ type UserToken = { export default class launch extends EventEmitter { private userToken: UserToken = { access_token: '', - access_token_v2: '', + open_api_access_token: '', user_id: '', refresh: false } @@ -53,6 +54,7 @@ export default class launch extends EventEmitter { start() { exception.handler() this.setInitArgv() + this.loadUserData() this.handleEvents() this.handleAppReady() } @@ -64,16 +66,22 @@ export default class launch extends EventEmitter { } process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' + process.env.DIST = path.join(__dirname, '../dist') + process.env.VITE_PUBLIC = app.isPackaged + ? process.env.DIST + : path.join(process.env.DIST, '../public') app.commandLine.appendSwitch('no-sandbox') app.commandLine.appendSwitch('disable-web-security') app.commandLine.appendSwitch('disable-renderer-backgrounding') app.commandLine.appendSwitch('disable-site-isolation-trials') - app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors,SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure') - app.commandLine.appendSwitch('ignore-connections-limit', 'bj29.cn-beijing.data.alicloudccp.com,alicloudccp.com,api.aliyundrive.com,aliyundrive.com') + app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors,SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure,BlockInsecurePrivateNetworkRequests') + app.commandLine.appendSwitch('ignore-connections-limit', 'bj29-enet.cn-beijing.data.alicloudccp.com,bj29-hz.cn-hangzhou.data.alicloudccp.com,bj29.cn-beijing.data.alicloudccp.com,alicloudccp.com,api.aliyundrive.com,aliyundrive.com,api.alipan.com,alipan.com') app.commandLine.appendSwitch('ignore-certificate-errors') - app.commandLine.appendSwitch('proxy-bypass-list', '') + app.commandLine.appendSwitch('proxy-bypass-list', '*') app.commandLine.appendSwitch('wm-window-animations-disabled') + app.commandLine.appendSwitch('enable-features', 'PlatformHEVCDecoderSupport') + app.commandLine.appendSwitch('force_high_performance_gpu') app.name = 'alixby3' if (is.windows()) { @@ -124,45 +132,51 @@ export default class launch extends EventEmitter { } catch (err) { } session.defaultSession.webRequest.onBeforeSendHeaders((details, cb) => { - const should115Referer = details.url.indexOf('.115.com') > 0 const shouldGieeReferer = details.url.indexOf('gitee.com') > 0 - const shouldAliOrigin = details.url.indexOf('.aliyundrive.com') > 0 - const shouldAliReferer = !should115Referer && !shouldGieeReferer && (!details.referrer || details.referrer.trim() === '' || /(\/localhost:)|(^file:\/\/)|(\/127.0.0.1:)/.exec(details.referrer) !== null) - const shouldToken = details.url.includes('aliyundrive') && details.url.includes('download') - const shouldOpenApiToken = details.url.includes('adrive/v1.0') + const shouldBiliBili = details.url.indexOf('bilibili.com') > 0 + const shouldQQTv = details.url.indexOf('v.qq.com') > 0 || details.url.indexOf('video.qq.com') > 0 + const shouldAliPanOrigin = details.url.indexOf('.aliyundrive.com') > 0 || details.url.indexOf('.alipan.com') > 0 + const shouldAliReferer = !shouldQQTv && !shouldBiliBili && !shouldGieeReferer && (!details.referrer || details.referrer.trim() === '' || /(\/localhost:)|(^file:\/\/)|(\/127.0.0.1:)/.exec(details.referrer) !== null) + const shouldToken = details.url.includes('alipan') && details.url.includes('download') + const shouldOpenApiToken = details.url.includes('adrive/v1.0') || details.url.includes('adrive/v1.1') cb({ cancel: false, requestHeaders: { ...details.requestHeaders, - ...(should115Referer && { - Referer: 'http://115.com/s/swn4bs33z88', - Origin: 'http://115.com' - }), ...(shouldGieeReferer && { Referer: 'https://gitee.com/' }), - ...(shouldAliOrigin && { - Origin: 'https://www.aliyundrive.com' + ...(shouldAliPanOrigin && { + Origin: 'https://www.alipan.com' }), ...(shouldAliReferer && { - Referer: 'https://www.aliyundrive.com/' + Referer: 'https://www.alipan.com/' + }), + ...(shouldBiliBili && { + Referer: 'https://www.bilibili.com/', + Cookie: 'buvid_fp=4e5ab1b80f684b94efbf0d2f4721913e;buvid3=0679D9AB-1548-ED1E-B283-E0114517315E63379infoc;buvid4=990C4544-0943-1FBF-F13C-4C42A4EA97AA63379-024020214-83%2BAINcbQP917Ye0PjtrCg%3D%3D;', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0' + }), + ...(shouldQQTv && { + Referer: 'https://m.v.qq.com/', + Origin: 'https://m.v.qq.com', + 'user-agent': 'Mozilla/5.0 (Linux; Android 13; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36 Edg/121.0.0.0' }), ...(shouldToken && { Authorization: this.userToken.access_token }), - // ...(shouldOpenApiToken && { - // Authorization: this.userToken.access_token_v2 - // }), - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) aDrive/4.1.0 Chrome/108.0.5359.215 Electron/22.3.1 Safari/537.36', - 'X-Canary': 'client=windows,app=adrive,version=v4.1.0', + ...(shouldOpenApiToken && { + Authorization: this.userToken.open_api_access_token + }), + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) aDrive/4.12.0 Chrome/108.0.5359.215 Electron/22.3.24 Safari/537.36', + 'X-Canary': 'client=windows,app=adrive,version=v4.12.0', 'Accept-Language': 'zh-CN,zh;q=0.9' } }) }) session.defaultSession.loadExtension(getStaticPath('crx'), { allowFileAccess: true }).then(() => { createMainWindow() - createMenu() createTray() }) }) @@ -186,9 +200,8 @@ export default class launch extends EventEmitter { handleAppActivate() { app.on('activate', () => { - if (!AppWindow.mainWindow || AppWindow.mainWindow.isDestroyed()){ - createMainWindow() - } else { + if (!AppWindow.mainWindow || AppWindow.mainWindow.isDestroyed()) createMainWindow() + else { if (AppWindow.mainWindow.isMinimized()) AppWindow.mainWindow.restore() AppWindow.mainWindow.show() AppWindow.mainWindow.focus() @@ -218,4 +231,4 @@ export default class launch extends EventEmitter { } }) } -} \ No newline at end of file +} diff --git a/aliyunpan/electron/preload/index.ts b/aliyunpan/electron/preload/index.ts index ebbd767507..418816e572 100644 --- a/aliyunpan/electron/preload/index.ts +++ b/aliyunpan/electron/preload/index.ts @@ -4,96 +4,116 @@ window.Electron = Electron process.noAsar = true window.platform = process.platform -window.WebToElectron = function (data: any) { +window.WebToElectron = function(data: any) { try { ipcRenderer.send('WebToElectron', data) - } catch {} + } catch { + } } -window.WebToElectronCB = function (data: any, callback: any) { +window.WebToWindow = function(data: any, callback: any) { + try { + const backData = ipcRenderer.sendSync('WebToWindow', data) + callback && callback(backData) + } catch { + } +} + +window.WebToElectronCB = function(data: any, callback: any) { try { const backData = ipcRenderer.sendSync('WebToElectronCB', data) - callback(backData) - } catch {} + callback(backData) + } catch { + } } -ipcRenderer.on('ElectronToWeb', function (event, arg) { - -}) -ipcRenderer.on('MainSendToken', function (event, arg) { +ipcRenderer.on('MainSendToken', function(event, arg) { try { window.postMessage(arg) - } catch {} + } catch { + } }) -window.WebSpawnSync = function (data: any, callback: any) { +window.WebSpawnSync = function(data: any, callback: any) { try { - const backData = ipcRenderer.sendSync('WebSpawnSync', data) - callback(backData) - } catch {} + const backData = ipcRenderer.sendSync('WebSpawnSync', data) + callback(backData) + } catch { + } } -window.WebExecSync = function (data: any, callback: any) { +window.WebExecSync = function(data: any, callback: any) { try { - const backData = ipcRenderer.sendSync('WebExecSync', data) - callback(backData) - } catch {} + const backData = ipcRenderer.sendSync('WebExecSync', data) + callback(backData) + } catch { + } } -window.WebShowOpenDialogSync = function (config: any, callback: any) { +window.WebShowOpenDialogSync = function(config: any, callback: any) { try { const backData = ipcRenderer.sendSync('WebShowOpenDialogSync', config) - callback(backData) - } catch {} + callback(backData) + } catch { + } } -window.WebShowSaveDialogSync = function (config: any, callback: any) { +window.WebShowSaveDialogSync = function(config: any, callback: any) { try { const backData = ipcRenderer.sendSync('WebShowSaveDialogSync', config) - callback(backData) - } catch {} + callback(backData) + } catch { + } } -window.WebShowItemInFolder = function (fullPath: string) { +window.WebShowItemInFolder = function(fullPath: string) { try { ipcRenderer.send('WebShowItemInFolder', fullPath) - } catch {} + } catch { + } } -window.WebPlatformSync = function (callback: any) { +window.WebPlatformSync = function(callback: any) { try { const backData = ipcRenderer.sendSync('WebPlatformSync') - callback(backData) - } catch {} + callback(backData) + } catch { + } } -window.WebClearCookies = function (data: any) { +window.WebClearCookies = function(data: any) { try { ipcRenderer.send('WebClearCookies', data) - } catch {} + } catch { + } } -window.WebClearCache = function (data: any) { +window.WebClearCache = function(data: any) { try { ipcRenderer.send('WebClearCache', data) - } catch {} + } catch { + } } -window.WebUserToken = function (data: any) { +window.WebUserToken = function(data: any) { try { ipcRenderer.send('WebUserToken', data) - } catch {} + } catch { + } } -window.WebSaveTheme = function (data: any) { +window.WebSaveTheme = function(data: any) { try { ipcRenderer.send('WebSaveTheme', data) - } catch {} + } catch { + } } -window.WebReload = function (data: any) { +window.WebReload = function(data: any) { try { ipcRenderer.send('WebReload', data) - } catch {} + } catch { + } } -window.WebRelaunch = function (data: any) { +window.WebRelaunch = function(data: any) { try { ipcRenderer.send('WebRelaunch', data) - } catch {} + } catch { + } } window.WebRelaunchAria = async function() { try { @@ -116,74 +136,95 @@ window.WebResetAlistPwd = async function(data: any) { return 0 } } -window.WebSetProgressBar = function (data: any) { +window.WebSetProgressBar = function(data: any) { try { - ipcRenderer.send('WebSetProgressBar', data) - } catch {} + ipcRenderer.send('WebSetProgressBar', data) + } catch { + } } -window.WebGetCookies = async function (data: any) { +window.WebGetCookies = async function(data: any) { try { return await ipcRenderer.invoke('WebGetCookies', data) - } catch {} + } catch { + } } -window.WebSetCookies = function (cookies: any) { +window.WebSetCookies = function(cookies: any) { try { - ipcRenderer.send('WebSetCookies', cookies) - } catch {} + ipcRenderer.send('WebSetCookies', cookies) + } catch { + } } -window.WebOpenWindow = function (data: any) { +window.WebOpenWindow = function(data: any) { try { ipcRenderer.send('WebOpenWindow', data) - } catch {} + } catch { + } } -window.WebOpenUrl = function (data: any) { +window.WebOpenUrl = function(data: any) { try { ipcRenderer.send('WebOpenUrl', data) - } catch {} + } catch { + } } -window.WebShutDown = function (data: any) { +window.WebShutDown = function(data: any) { try { - ipcRenderer.send('WebShutDown', data) - } catch {} + ipcRenderer.send('WebShutDown', data) + } catch { + } } -window.WebSetProxy = function (data: { proxyUrl: string }) { +window.WebSetProxy = function(data: { proxyUrl: string }) { try { ipcRenderer.send('WebSetProxy', data) - } catch {} + } catch { + } } function createRightMenu() { - window.addEventListener( - 'contextmenu', - (e) => { - try { - if (e) e.preventDefault() - if (isEleEditable(e.target)) { - ipcRenderer.send('WebToElectron', { cmd: 'menuedit' }) - } else { - - const selectText = window.getSelection()?.toString() - if (selectText) ipcRenderer.send('WebToElectron', { cmd: 'menucopy' }) - } - } catch {} - }, - false + window.addEventListener('contextmenu', (e) => { + if (e) e.preventDefault() + const target = e.target as HTMLElement + // 检查页面是否是有选择的文本 这里显示复制和剪切选项是否可见 + const selectText = !!window.getSelection().toString() + if (selectText || isEleEditable(target)) { + // 读取剪切板是否有文本 这里传递粘贴选项是否可见 + const showPaste = !!navigator.clipboard.readText() + // 判断ReadOnly + const isReadOnly = target.hasAttribute('readonly') + // 发送给主进程让它显示菜单 + ipcRenderer.send('show-context-menu', { + showPaste: !isReadOnly && showPaste, + showCopy: selectText, + showCut: !isReadOnly && selectText + }) + } + } ) } function isEleEditable(e: any): boolean { - if (!e) { - return false - } - - if ((e.tagName === 'INPUT' && e.type !== 'checkbox') || e.tagName === 'TEXTAREA' || e.contentEditable == 'true') { + if (!e) return false + if (e.tagName === 'TEXTAREA' + || (e.tagName === 'INPUT' && e.type !== 'checkbox') + || e.contentEditable == 'true') { return true } else { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return isEleEditable(e.parentNode) } } createRightMenu() + +// fix: new-windows event +ipcRenderer.on('webview-new-window', (e, webContentsId, details) => { + const webview = document.getElementById('webview') as any + const evt = new Event('new-window', { bubbles: true, cancelable: false }) + webview.dispatchEvent(Object.assign(evt, details)) +}) + +ipcRenderer.on('webview-redirect', (e, webContentsId, url) => { + const webview = document.getElementById('webview') as any + const evt = new Event('will-redirect', { bubbles: true, cancelable: false }) + webview.dispatchEvent(Object.assign(evt, { url })) +}) diff --git a/aliyunpan/electron/preload/preload-env.d.ts b/aliyunpan/electron/preload/preload-env.d.ts index e2a2a4c0b6..78a4a166cd 100644 --- a/aliyunpan/electron/preload/preload-env.d.ts +++ b/aliyunpan/electron/preload/preload-env.d.ts @@ -2,6 +2,8 @@ declare namespace NodeJS { interface ProcessEnv { NODE_ENV: 'development' | 'production' + DIST: string + VITE_PUBLIC: string readonly VITE_DEV_SERVER_HOST: string readonly VITE_DEV_SERVER_PORT: string } @@ -11,6 +13,7 @@ declare interface Window { platform: any WinMsg: any WebToElectron: any + WebToWindow: any WebToElectronCB: any WebSpawnSync: any WebExecSync: any diff --git a/aliyunpan/index.html b/aliyunpan/index.html index 37786e7699..6ffeb85c4b 100644 --- a/aliyunpan/index.html +++ b/aliyunpan/index.html @@ -1,15 +1,6 @@ - - - 小白羊云盘 diff --git a/aliyunpan/nano-staged.mjs b/aliyunpan/nano-staged.mjs index c6ce9aa3bb..8221c288ed 100644 --- a/aliyunpan/nano-staged.mjs +++ b/aliyunpan/nano-staged.mjs @@ -4,5 +4,5 @@ export default { '*.{vue}': ['stylelint --fix', 'prettier --write', 'eslint --cache --fix'], '*.{less,css}': ['stylelint --fix', 'prettier --write'], // typecheck - 'packages/renderer/**/{*.ts,*.tsx,*.vue,tsconfig.json}': ({ filenames }) => 'npm run typecheck' + 'src/**/{*.ts,*.tsx,*.vue,tsconfig.json}': ({ filenames }) => 'npm run typecheck' } diff --git a/aliyunpan/package.json b/aliyunpan/package.json index 1f11cf59c2..d811080006 100644 --- a/aliyunpan/package.json +++ b/aliyunpan/package.json @@ -1,78 +1,103 @@ { "name": "xbyyunpan", - "description": "小白羊云盘", - "version": "3.11.21", - "license": "MIT", + "description": "阿里云盘小白羊", + "version": "3.13.1", "main": "dist/electron/main/index.js", "author": { - "name": "gaozhangmin", + "name": "gavingaozhangmin", "email": "gaozhangmin@gmail.com" }, + "homepage": "https://github.com/gaozhangmin/aliyunpan", + "license": "MIT", "scripts": { "dev": "vite", - "build": "vue-tsc --noEmit && vite build && electron-builder -wml" + "build": "vue-tsc --noEmit && vite build && electron-builder -wml", + "build:version": "node version.mjs", + "build:electron": "pnpm run build && electron-builder", + "build:test": "pnpm run build && electron-builder --dir" }, "engines": { - "node": ">=16.0.0" - }, - "dependencies": { - + "node": ">=18.0.0" }, "devDependencies": { - "@arco-design/web-vue": "^2.45.3", - "@electron/remote": "^2.0.9", - "@types/crypto-js": "^4.1.1", - "@types/fast-levenshtein": "^0.0.2", - "@types/howler": "^2.2.7", - "@types/lodash": "^4.14.184", - "@types/node": "^17.0.45", - "@types/secp256k1": "^4.0.3", - "@types/uuid": "^9.0.1", - "@vitejs/plugin-vue": "^3.1.0", - "@vitejs/plugin-vue-jsx": "^2.0.1", - "ant-design-vue": "^3.2.20", - "aria2-lib": "1.0.1", - "artplayer": "^5.0.9", - "axios": "^1.4.0", - "consola": "^3.1.0", - "crypto-js": "^4.1.1", - "dayjs": "^1.11.7", - "dexie": "^3.2.4", - "digest-fetch": "^3.0.4", - "dom-to-image": "^2.6.0", - "electron": "^21.4.4", - "electron-builder": "^23.6.0", - "electron-is": "^3.0.0", - "electron-log": "^4.4.8", - "fast-levenshtein": "^3.0.0", - "fix-path": "^4.0.0", + "@arco-design/web-vue": "2.55.0", + "@arco-themes/vue-gi-demo": "^0.0.48", + "@types/crypto-js": "^4.2.2", + "@types/fast-levenshtein": "^0.0.4", + "@types/howler": "^2.2.11", + "@types/lodash": "^4.17.0", + "@types/markdown-it": "^13.0.7", + "@types/mime-types": "^2.1.4", + "@types/node": "^20.11.30", + "@types/node-ssdp": "^4.0.4", + "@types/secp256k1": "^4.0.6", + "@types/thunky": "^1.1.2", + "@types/uuid": "^9.0.8", + "@vitejs/plugin-vue": "^5.0.4", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "@vue/runtime-core": "3.4.21", + "@vue/runtime-dom": "3.4.21", "ass-html5": "^0.3.5", - "fuzzysort": "^2.0.1", - "hls.js": "^1.4.3", - "howler": "^2.2.3", + "autoprefixer": "^10.4.16", + "ant-design-vue": "4.1.2", + "aria2-lib": "1.0.1", + "artplayer": "^5.1.1", + "axios": "^1.6.8", + "chinese-simple2traditional": "^1.2.0", + "consola": "^3.2.3", + "cookie": "^0.6.0", + "crypto-js": "^4.2.0", + "dayjs": "^1.11.10", + "dexie": "^3.2.7", + "dom-to-image": "^2.6.0", + "electron": "21.4.4", + "electron-builder": "^24.13.3", + "electron-is": "^3.0.0", + "fast-levenshtein": "^3.0.0", + "fast-xml-parser": "^4.3.6", + "fix-path": "^4.0.0", + "fuzzysort": "^2.0.4", + "hls.js": "^1.5.7", + "howler": "^2.2.4", "isomorphic-fetch": "^3.0.0", + "jassub": "^1.7.15", "jschardet": "^3.0.0", + "less": "^4.2.0", "lodash": "^4.17.21", - "pinia": "^2.0.35", + "markdown-it": "^14.1.0", + "mime-types": "^2.1.35", + "node-ssdp": "^4.0.1", + "pako": "^2.1.0", + "path-to-regexp": "^6.2.1", + "perf_hooks": "^0.0.1", + "pinia": "^2.1.7", "secp256k1": "^5.0.0", - "socks-proxy-agent": "^7.0.0", - "sudo-prompt": "^9.2.1", - "terser": "^5.15.0", - "typescript": "^4.8.2", - "uuid": "^9.0.0", + "semver": "^7.6.0", + "socks-proxy-agent": "^8.0.2", + "terser": "^5.29.2", + "thunky": "^1.1.0", + "tree-kill": "^1.2.2", + "tailwind-scrollbar": "^2.1.0", + "tailwindcss": "^3.3.5", + "typescript": "^5.4.2", + "upnp-client-ts": "^1.1.1", + "uuid": "^9.0.1", "uuid-by-string": "^4.0.0", - "viewerjs": "^1.10.5", - "vite": "3.2.7", - "vite-plugin-electron": "^0.9.2", - "vite-plugin-resolve": "^2.1.2", - "vue": "^3.2.47", - "vue-tsc": "^1.6.4" + "viewerjs": "^1.11.6", + "vite": "5.2.2", + "vite-plugin-electron": "0.15.6", + "vite-plugin-electron-renderer": "0.14.5", + "vue": "3.4.21", + "vue-tsc": "^2.0.7", + "webdav-server": "^2.6.2", + "whacko": "^0.19.1", + "digest-fetch": "^3.0.4" }, "debug": { "env": { "VITE_DEV_SERVER_HOSTNAME": "127.0.0.1", - "VITE_DEV_SERVER_PORT": 3344, - "VITE_DEV_SERVER_URL": "http://127.0.0.1:3344" + "VITE_DEV_SERVER_PORT": 5173, + "VITE_DEV_SERVER_URL": "http://127.0.0.1:5173" } }, "keywords": [ diff --git a/aliyunpan/pnpm-lock.yaml b/aliyunpan/pnpm-lock.yaml new file mode 100644 index 0000000000..22ffd5423f --- /dev/null +++ b/aliyunpan/pnpm-lock.yaml @@ -0,0 +1,4359 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@arco-design/web-vue': + specifier: 2.55.0 + version: 2.55.0(vue@3.4.21) + '@arco-themes/vue-gi-demo': + specifier: ^0.0.48 + version: 0.0.48(@arco-design/web-vue@2.55.0) + '@types/crypto-js': + specifier: ^4.2.2 + version: 4.2.2 + '@types/fast-levenshtein': + specifier: ^0.0.4 + version: 0.0.4 + '@types/howler': + specifier: ^2.2.11 + version: 2.2.11 + '@types/lodash': + specifier: ^4.17.0 + version: 4.17.0 + '@types/markdown-it': + specifier: ^13.0.7 + version: 13.0.7 + '@types/mime-types': + specifier: ^2.1.4 + version: 2.1.4 + '@types/node': + specifier: ^20.11.30 + version: 20.11.30 + '@types/node-ssdp': + specifier: ^4.0.4 + version: 4.0.4 + '@types/secp256k1': + specifier: ^4.0.6 + version: 4.0.6 + '@types/thunky': + specifier: ^1.1.2 + version: 1.1.2 + '@types/uuid': + specifier: ^9.0.8 + version: 9.0.8 + '@vitejs/plugin-vue': + specifier: ^5.0.4 + version: 5.0.4(vite@5.2.2)(vue@3.4.21) + '@vitejs/plugin-vue-jsx': + specifier: ^3.1.0 + version: 3.1.0(vite@5.2.2)(vue@3.4.21) + '@vue/runtime-core': + specifier: 3.4.21 + version: 3.4.21 + '@vue/runtime-dom': + specifier: 3.4.21 + version: 3.4.21 + ant-design-vue: + specifier: 4.1.2 + version: 4.1.2(vue@3.4.21) + aria2-lib: + specifier: 1.0.1 + version: 1.0.1 + artplayer: + specifier: ^5.1.1 + version: 5.1.1 + axios: + specifier: ^1.6.8 + version: 1.6.8 + chinese-simple2traditional: + specifier: ^1.2.0 + version: 1.2.0 + consola: + specifier: ^3.2.3 + version: 3.2.3 + cookie: + specifier: ^0.6.0 + version: 0.6.0 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.10 + dexie: + specifier: ^3.2.7 + version: 3.2.7 + dom-to-image: + specifier: ^2.6.0 + version: 2.6.0 + electron: + specifier: 21.4.4 + version: 21.4.4 + electron-builder: + specifier: ^24.13.3 + version: 24.13.3(electron-builder-squirrel-windows@24.13.3) + electron-is: + specifier: ^3.0.0 + version: 3.0.0 + fast-levenshtein: + specifier: ^3.0.0 + version: 3.0.0 + fast-xml-parser: + specifier: ^4.3.6 + version: 4.3.6 + fix-path: + specifier: ^4.0.0 + version: 4.0.0 + fuzzysort: + specifier: ^2.0.4 + version: 2.0.4 + hls.js: + specifier: ^1.5.7 + version: 1.5.7 + howler: + specifier: ^2.2.4 + version: 2.2.4 + isomorphic-fetch: + specifier: ^3.0.0 + version: 3.0.0 + jassub: + specifier: ^1.7.15 + version: 1.7.15 + jschardet: + specifier: ^3.0.0 + version: 3.0.0 + less: + specifier: ^4.2.0 + version: 4.2.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + markdown-it: + specifier: ^14.1.0 + version: 14.1.0 + mime-types: + specifier: ^2.1.35 + version: 2.1.35 + node-ssdp: + specifier: ^4.0.1 + version: 4.0.1 + pako: + specifier: ^2.1.0 + version: 2.1.0 + path-to-regexp: + specifier: ^6.2.1 + version: 6.2.1 + perf_hooks: + specifier: ^0.0.1 + version: 0.0.1 + pinia: + specifier: ^2.1.7 + version: 2.1.7(typescript@5.4.3)(vue@3.4.21) + secp256k1: + specifier: ^5.0.0 + version: 5.0.0 + semver: + specifier: ^7.6.0 + version: 7.6.0 + socks-proxy-agent: + specifier: ^8.0.2 + version: 8.0.2 + terser: + specifier: ^5.29.2 + version: 5.29.2 + thunky: + specifier: ^1.1.0 + version: 1.1.0 + tree-kill: + specifier: ^1.2.2 + version: 1.2.2 + typescript: + specifier: ^5.4.3 + version: 5.4.3 + upnp-client-ts: + specifier: ^1.1.1 + version: 1.1.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + uuid-by-string: + specifier: ^4.0.0 + version: 4.0.0 + viewerjs: + specifier: ^1.11.6 + version: 1.11.6 + vite: + specifier: 5.2.2 + version: 5.2.2(@types/node@20.11.30)(less@4.2.0)(terser@5.29.2) + vite-plugin-electron: + specifier: 0.15.6 + version: 0.15.6(tree-kill@1.2.2)(vite-plugin-electron-renderer@0.14.5) + vite-plugin-electron-renderer: + specifier: 0.14.5 + version: 0.14.5 + vue: + specifier: 3.4.21 + version: 3.4.21(typescript@5.4.3) + vue-tsc: + specifier: ^2.0.7 + version: 2.0.7(typescript@5.4.3) + webdav-server: + specifier: ^2.6.2 + version: 2.6.2 + whacko: + specifier: ^0.19.1 + version: 0.19.1 + +packages: + + /7zip-bin@5.2.0: + resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@ant-design/colors@6.0.0: + resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==} + dependencies: + '@ctrl/tinycolor': 3.6.1 + dev: true + + /@ant-design/icons-svg@4.3.1: + resolution: {integrity: sha512-4QBZg8ccyC6LPIRii7A0bZUk3+lEDCLnhB+FVsflGdcWPPmV+j3fire4AwwoqHV/BibgvBmR9ZIo4s867smv+g==} + dev: true + + /@ant-design/icons-vue@7.0.1(vue@3.4.21): + resolution: {integrity: sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==} + peerDependencies: + vue: '>=3.0.3' + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-svg': 4.3.1 + vue: 3.4.21(typescript@5.4.3) + dev: true + + /@arco-design/color@0.4.0: + resolution: {integrity: sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==} + dependencies: + color: 3.2.1 + dev: true + + /@arco-design/web-vue@2.55.0(vue@3.4.21): + resolution: {integrity: sha512-aMfg9dHiDsiJsxU2Mpa0V4WKjtdAdETBBrrVEHj1E7rJYF+PmrfZcSgIvAJSbdJZC/euqSCHyYOQezi3esSzxA==} + peerDependencies: + vue: ^3.1.0 + dependencies: + '@arco-design/color': 0.4.0 + b-tween: 0.3.3 + b-validate: 1.5.3 + compute-scroll-into-view: 1.0.20 + dayjs: 1.11.10 + number-precision: 1.6.0 + resize-observer-polyfill: 1.5.1 + scroll-into-view-if-needed: 2.2.31 + vue: 3.4.21(typescript@5.4.3) + dev: true + + /@arco-themes/vue-gi-demo@0.0.48(@arco-design/web-vue@2.55.0): + resolution: {integrity: sha512-HUdIvGcKgHtmqMk1aMmRC1klDP2Ki1La/f+TUB4CpqkWNfiKtMmqpGcMyQeX7iNCE04m86DbCJL5v/BqGxcPuw==} + peerDependencies: + '@arco-design/web-vue': ^2.53.2 + dependencies: + '@arco-design/web-vue': 2.55.0(vue@3.4.21) + dev: true + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.5: + resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) + '@babel/helpers': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.5: + resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.22.2 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5): + resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.5: + resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.5: + resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/parser@7.23.9: + resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.5 + dev: true + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-transform-typescript@7.23.5(@babel/core@7.23.5): + resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.5) + dev: true + + /@babel/runtime@7.23.5: + resolution: {integrity: sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + dev: true + + /@babel/traverse@7.23.5: + resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.5: + resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@ctrl/tinycolor@3.6.1: + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + dev: true + + /@develar/schema-utils@2.6.5: + resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} + engines: {node: '>= 8.9.0'} + dependencies: + ajv: 6.12.6 + ajv-keywords: 3.5.2(ajv@6.12.6) + dev: true + + /@electron/asar@3.2.8: + resolution: {integrity: sha512-cmskk5M06ewHMZAplSiF4AlME3IrnnZhKnWbtwKVLRkdJkKyUVjMLhDIiPIx/+6zQWVlKX/LtmK9xDme7540Sg==} + engines: {node: '>=10.12.0'} + hasBin: true + dependencies: + commander: 5.1.0 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /@electron/get@1.14.1: + resolution: {integrity: sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==} + engines: {node: '>=8.6'} + dependencies: + debug: 4.3.4 + env-paths: 2.2.1 + fs-extra: 8.1.0 + got: 9.6.0 + progress: 2.0.3 + semver: 6.3.1 + sumchecker: 3.0.1 + optionalDependencies: + global-agent: 3.0.0 + global-tunnel-ng: 2.7.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@electron/notarize@2.2.1: + resolution: {integrity: sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==} + engines: {node: '>= 10.0.0'} + dependencies: + debug: 4.3.4 + fs-extra: 9.1.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@electron/osx-sign@1.0.5: + resolution: {integrity: sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + compare-version: 0.1.2 + debug: 4.3.4 + fs-extra: 10.1.0 + isbinaryfile: 4.0.10 + minimist: 1.2.8 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@electron/universal@1.5.1: + resolution: {integrity: sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==} + engines: {node: '>=8.6'} + dependencies: + '@electron/asar': 3.2.8 + '@malept/cross-spawn-promise': 1.1.1 + debug: 4.3.4 + dir-compare: 3.3.0 + fs-extra: 9.1.0 + minimatch: 3.1.2 + plist: 3.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@emotion/hash@0.9.1: + resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} + dev: true + + /@emotion/unitless@0.8.1: + resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + dev: true + + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map@0.3.5: + resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@malept/cross-spawn-promise@1.1.1: + resolution: {integrity: sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==} + engines: {node: '>= 10'} + dependencies: + cross-spawn: 7.0.3 + dev: true + + /@malept/flatpak-bundler@0.4.0: + resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==} + engines: {node: '>= 10.0.0'} + dependencies: + debug: 4.3.4 + fs-extra: 9.1.0 + lodash: 4.17.21 + tmp-promise: 3.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} + cpu: [arm64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} + cpu: [x64] + os: [linux] + libc: [glibc] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} + cpu: [x64] + os: [linux] + libc: [musl] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@simonwep/pickr@1.8.2: + resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==} + dependencies: + core-js: 3.33.3 + nanopop: 2.3.0 + dev: true + + /@sindresorhus/is@0.14.0: + resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} + engines: {node: '>=6'} + dev: true + + /@szmarczak/http-timer@1.1.2: + resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} + engines: {node: '>=6'} + dependencies: + defer-to-connect: 1.1.3 + dev: true + + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + + /@types/crypto-js@4.2.2: + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + dev: true + + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + dependencies: + '@types/ms': 0.7.34 + dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + + /@types/fast-levenshtein@0.0.4: + resolution: {integrity: sha512-tkDveuitddQCxut1Db8eEFfMahTjOumTJGPHmT9E7KUH+DkVq9WTpVvlfenf3S+uCBeu8j5FP2xik/KfxOEjeA==} + dev: true + + /@types/fs-extra@9.0.13: + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + dependencies: + '@types/node': 20.11.30 + dev: true + + /@types/howler@2.2.11: + resolution: {integrity: sha512-7aBoUL6RbSIrqKnpEgfa1wSNUBK06mn08siP2QI0zYk7MXfEJAaORc4tohamQYqCqVESoDyRWSdQn2BOKWj2Qw==} + dev: true + + /@types/keyv@3.1.4: + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + dependencies: + '@types/node': 20.11.30 + dev: true + + /@types/linkify-it@3.0.5: + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + dev: true + + /@types/lodash@4.17.0: + resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==} + dev: true + + /@types/markdown-it@13.0.7: + resolution: {integrity: sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==} + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + dev: true + + /@types/mdurl@1.0.5: + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + dev: true + + /@types/mime-types@2.1.4: + resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} + dev: true + + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + dev: true + + /@types/node-ssdp@4.0.4: + resolution: {integrity: sha512-k5gWpZYrNAZ1alW8RoAPaYlk6IYPad92FyeyOk1n2RIdwouNJZ4Ft8Fc2YxFUwty+bBqY4C/fWFEOxdso4jSoA==} + dependencies: + '@types/node': 20.11.30 + dev: true + + /@types/node@16.18.82: + resolution: {integrity: sha512-pcDZtkx9z8XYV+ius2P3Ot2VVrcYOfXffBQUBuiszrlUzKSmoDYqo+mV+IoL8iIiIjjtOMvNSmH1hwJ+Q+f96Q==} + dev: true + + /@types/node@20.11.30: + resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/plist@3.0.5: + resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} + requiresBuild: true + dependencies: + '@types/node': 20.11.30 + xmlbuilder: 15.1.1 + dev: true + optional: true + + /@types/responselike@1.0.3: + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + dependencies: + '@types/node': 20.11.30 + dev: true + + /@types/secp256k1@4.0.6: + resolution: {integrity: sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==} + dependencies: + '@types/node': 20.11.30 + dev: true + + /@types/thunky@1.1.2: + resolution: {integrity: sha512-VI0UCE4siVMkLBLZBOls+ohv2uXcBYQvwD6IkseB4WAIXktRuMPE7FKZ13B5UlN3Q7/fLFDSgZ1/mkFEXpDY1g==} + dev: true + + /@types/uuid@9.0.8: + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + dev: true + + /@types/verror@1.10.9: + resolution: {integrity: sha512-MLx9Z+9lGzwEuW16ubGeNkpBDE84RpB/NyGgg6z2BTpWzKkGU451cAY3UkUzZEp72RHF585oJ3V8JVNqIplcAQ==} + requiresBuild: true + dev: true + optional: true + + /@types/yauzl@2.10.3: + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + requiresBuild: true + dependencies: + '@types/node': 20.11.30 + dev: true + optional: true + + /@vitejs/plugin-vue-jsx@3.1.0(vite@5.2.2)(vue@3.4.21): + resolution: {integrity: sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.0.0 || ^5.0.0 + vue: ^3.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.23.5) + '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.23.5) + vite: 5.2.2(@types/node@20.11.30)(less@4.2.0)(terser@5.29.2) + vue: 3.4.21(typescript@5.4.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitejs/plugin-vue@5.0.4(vite@5.2.2)(vue@3.4.21): + resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + dependencies: + vite: 5.2.2(@types/node@20.11.30)(less@4.2.0)(terser@5.29.2) + vue: 3.4.21(typescript@5.4.3) + dev: true + + /@volar/language-core@2.1.4: + resolution: {integrity: sha512-ROfPepDxZ5Eq+Unbx3M9QcHT7MoE9tYdbkuzLTtxG5rfkEi5RwsDPncjANMOq/gHhIIDlWgqWwS2nXWMGsuj4w==} + dependencies: + '@volar/source-map': 2.1.4 + dev: true + + /@volar/source-map@2.1.4: + resolution: {integrity: sha512-mCg8IiPZmHZVzqL4Owg+BzQ5ZTG1cVwATxrkrFPZpcAin97Xa3MbchxVhHtHTWTT8ER8bJh5xVjeVxsSN++FUA==} + dependencies: + muggle-string: 0.4.1 + dev: true + + /@volar/typescript@2.1.4: + resolution: {integrity: sha512-Mt7wOLPkomFnUfVpb5IHlPhSpD7FJAn+FHSsovePmqFNQzFLz16wrpHjAkorPiAnP0847w71NL5fIJyWbAsR8Q==} + dependencies: + '@volar/language-core': 2.1.4 + path-browserify: 1.0.1 + dev: true + + /@vue/babel-helper-vue-transform-on@1.1.5: + resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==} + dev: true + + /@vue/babel-plugin-jsx@1.1.5(@babel/core@7.23.5): + resolution: {integrity: sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-module-imports': 7.22.15 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + '@vue/babel-helper-vue-transform-on': 1.1.5 + camelcase: 6.3.0 + html-tags: 3.3.1 + svg-tags: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@vue/compiler-core@3.4.21: + resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/shared': 3.4.21 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-dom@3.4.21: + resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} + dependencies: + '@vue/compiler-core': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + + /@vue/compiler-sfc@3.4.21: + resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} + dependencies: + '@babel/parser': 7.23.9 + '@vue/compiler-core': 3.4.21 + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + estree-walker: 2.0.2 + magic-string: 0.30.7 + postcss: 8.4.35 + source-map-js: 1.0.2 + dev: true + + /@vue/compiler-ssr@3.4.21: + resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} + dependencies: + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + + /@vue/devtools-api@6.5.1: + resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==} + dev: true + + /@vue/language-core@2.0.7(typescript@5.4.3): + resolution: {integrity: sha512-Vh1yZX3XmYjn9yYLkjU8DN6L0ceBtEcapqiyclHne8guG84IaTzqtvizZB1Yfxm3h6m7EIvjerLO5fvOZO6IIQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@volar/language-core': 2.1.4 + '@vue/compiler-dom': 3.4.21 + '@vue/shared': 3.4.21 + computeds: 0.0.1 + minimatch: 9.0.3 + path-browserify: 1.0.1 + typescript: 5.4.3 + vue-template-compiler: 2.7.15 + dev: true + + /@vue/reactivity@3.4.21: + resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==} + dependencies: + '@vue/shared': 3.4.21 + dev: true + + /@vue/runtime-core@3.4.21: + resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==} + dependencies: + '@vue/reactivity': 3.4.21 + '@vue/shared': 3.4.21 + dev: true + + /@vue/runtime-dom@3.4.21: + resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==} + dependencies: + '@vue/runtime-core': 3.4.21 + '@vue/shared': 3.4.21 + csstype: 3.1.3 + dev: true + + /@vue/server-renderer@3.4.21(vue@3.4.21): + resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==} + peerDependencies: + vue: 3.4.21 + dependencies: + '@vue/compiler-ssr': 3.4.21 + '@vue/shared': 3.4.21 + vue: 3.4.21(typescript@5.4.3) + dev: true + + /@vue/shared@3.4.21: + resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + dev: true + + /@xmldom/xmldom@0.8.10: + resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /agent-base@7.1.0: + resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} + engines: {node: '>= 14'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /ajv-keywords@3.5.2(ajv@6.12.6): + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + dependencies: + ajv: 6.12.6 + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ant-design-vue@4.1.2(vue@3.4.21): + resolution: {integrity: sha512-ynFkDJLlHgumeK6Hr1UZ7PvQNZ1uBcri/pmejBdS3kRqHeA5VRsxneYDwa8YxA+uYB5YfT2jpYsSHsiMiCjRGg==} + engines: {node: '>=12.22.0'} + peerDependencies: + vue: '>=3.2.0' + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-vue': 7.0.1(vue@3.4.21) + '@babel/runtime': 7.23.5 + '@ctrl/tinycolor': 3.6.1 + '@emotion/hash': 0.9.1 + '@emotion/unitless': 0.8.1 + '@simonwep/pickr': 1.8.2 + array-tree-filter: 2.1.0 + async-validator: 4.2.5 + csstype: 3.1.3 + dayjs: 1.11.10 + dom-align: 1.12.4 + dom-scroll-into-view: 2.0.1 + lodash: 4.17.21 + lodash-es: 4.17.21 + resize-observer-polyfill: 1.5.1 + scroll-into-view-if-needed: 2.2.31 + shallow-equal: 1.2.1 + stylis: 4.3.1 + throttle-debounce: 5.0.0 + vue: 3.4.21(typescript@5.4.3) + vue-types: 3.0.2(vue@3.4.21) + warning: 4.0.3 + dev: true + + /app-builder-bin@4.0.0: + resolution: {integrity: sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==} + dev: true + + /app-builder-lib@24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3): + resolution: {integrity: sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==} + engines: {node: '>=14.0.0'} + peerDependencies: + dmg-builder: 24.13.3 + electron-builder-squirrel-windows: 24.13.3 + dependencies: + '@develar/schema-utils': 2.6.5 + '@electron/notarize': 2.2.1 + '@electron/osx-sign': 1.0.5 + '@electron/universal': 1.5.1 + '@malept/flatpak-bundler': 0.4.0 + '@types/fs-extra': 9.0.13 + async-exit-hook: 2.0.1 + bluebird-lst: 1.0.9 + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + chromium-pickle-js: 0.2.0 + debug: 4.3.4 + dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) + ejs: 3.1.9 + electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3) + electron-publish: 24.13.1 + form-data: 4.0.0 + fs-extra: 10.1.0 + hosted-git-info: 4.1.0 + is-ci: 3.0.1 + isbinaryfile: 5.0.0 + js-yaml: 4.1.0 + lazy-val: 1.0.5 + minimatch: 5.1.6 + read-config-file: 6.3.2 + sanitize-filename: 1.6.3 + semver: 7.6.0 + tar: 6.2.0 + temp-file: 3.4.0 + transitivePeerDependencies: + - supports-color + dev: true + + /archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + dev: true + + /archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + + /archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + dependencies: + archiver-utils: 2.1.0 + async: 3.2.5 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /aria2-lib@1.0.1: + resolution: {integrity: sha512-+kzXnMUEm1qANHkTmMjn4sWv9Bpc+DGSPbUHpAKFVdVgfZcT5kLsh2SGF+6j7NsXwkRfH8NDhkFtNQpZ4qSYZQ==} + dev: true + + /array-tree-filter@2.1.0: + resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==} + dev: true + + /artplayer@5.1.1: + resolution: {integrity: sha512-pZ2/lB+Oo3g0CVLqkFuB86bMBPMtZaABaq5e1LPKubYDBW46+mzin2wzdVmk3tvFyfQoVnb+MBFU7nJYvI3qNg==} + dependencies: + option-validator: 2.0.6 + dev: true + + /assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} + requiresBuild: true + dev: true + optional: true + + /astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + requiresBuild: true + dev: true + optional: true + + /async-exit-hook@2.0.1: + resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} + engines: {node: '>=0.12.0'} + dev: true + + /async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + dev: true + + /async@2.6.4: + resolution: {integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==} + dependencies: + lodash: 4.17.21 + dev: true + + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + dev: true + + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true + + /at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + dev: true + + /axios@1.6.8: + resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: true + + /b-tween@0.3.3: + resolution: {integrity: sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==} + dev: true + + /b-validate@1.5.3: + resolution: {integrity: sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA==} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + requiresBuild: true + dev: true + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /bluebird-lst@1.0.9: + resolution: {integrity: sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==} + dependencies: + bluebird: 3.7.2 + dev: true + + /bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + dev: true + + /bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + dev: true + + /boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /boolean@3.2.0: + resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + requiresBuild: true + dev: true + optional: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + dev: true + + /browserslist@4.22.2: + resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001566 + electron-to-chromium: 1.4.601 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.22.2) + dev: true + + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + + /buffer-equal@1.0.1: + resolution: {integrity: sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==} + engines: {node: '>=0.4'} + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + requiresBuild: true + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /builder-util-runtime@9.2.4: + resolution: {integrity: sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==} + engines: {node: '>=12.0.0'} + dependencies: + debug: 4.3.4 + sax: 1.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /builder-util@24.13.1: + resolution: {integrity: sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==} + dependencies: + 7zip-bin: 5.2.0 + '@types/debug': 4.1.12 + app-builder-bin: 4.0.0 + bluebird-lst: 1.0.9 + builder-util-runtime: 9.2.4 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + fs-extra: 10.1.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-ci: 3.0.1 + js-yaml: 4.1.0 + source-map-support: 0.5.21 + stat-mode: 1.0.0 + temp-file: 3.4.0 + transitivePeerDependencies: + - supports-color + dev: true + + /cacheable-request@6.1.0: + resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} + engines: {node: '>=8'} + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 3.1.0 + lowercase-keys: 2.0.0 + normalize-url: 4.5.1 + responselike: 1.0.2 + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001566: + resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chinese-simple2traditional@1.2.0: + resolution: {integrity: sha512-0DbXpBBOZzQBklJb/6ykZzsw0ykay95JK3byoQG+YAlFzJn+G6aIPNPQk3/T+whxSJm5J384xLsvQY77km6inA==} + dev: true + + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: true + + /chromium-pickle-js@0.2.0: + resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + + /cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + dev: true + optional: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + dependencies: + mimic-response: 1.0.1 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: true + + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: true + + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: true + + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + dev: true + + /compare-version@0.1.2: + resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} + engines: {node: '>=0.10.0'} + dev: true + + /compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + + /compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + dev: true + + /computeds@0.0.1: + resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + requiresBuild: true + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + optional: true + + /config-file-ts@0.2.4: + resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==} + dependencies: + glob: 7.2.3 + typescript: 4.9.5 + dev: true + + /consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + dev: true + + /copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + dependencies: + is-what: 3.14.1 + dev: true + + /core-js@3.33.3: + resolution: {integrity: sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==} + requiresBuild: true + dev: true + + /core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + requiresBuild: true + dev: true + + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: true + + /crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + dev: true + + /crc@3.8.0: + resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} + requiresBuild: true + dependencies: + buffer: 5.7.1 + dev: true + optional: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + dev: true + + /css-select@1.2.0: + resolution: {integrity: sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==} + dependencies: + boolbase: 1.0.0 + css-what: 2.1.3 + domutils: 1.5.1 + nth-check: 1.0.2 + dev: true + + /css-what@2.1.3: + resolution: {integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==} + dev: true + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true + + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: true + + /de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + requiresBuild: true + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decompress-response@3.3.0: + resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} + engines: {node: '>=4'} + dependencies: + mimic-response: 1.0.1 + dev: true + + /default-shell@2.2.0: + resolution: {integrity: sha512-sPpMZcVhRQ0nEMDtuMJ+RtCxt7iHPAMBU+I4tAlo5dU1sjRpNax0crj6nR3qKpvVnckaQ9U38enXcwW9nZJeCw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /defer-to-connect@1.1.3: + resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} + dev: true + + /define-data-property@1.1.2: + resolution: {integrity: sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==} + engines: {node: '>= 0.4'} + requiresBuild: true + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + optional: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + requiresBuild: true + dependencies: + define-data-property: 1.1.2 + has-property-descriptors: 1.0.1 + object-keys: 1.1.1 + dev: true + optional: true + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: true + + /detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + requiresBuild: true + dev: true + optional: true + + /dexie@3.2.7: + resolution: {integrity: sha512-2a+BXvVhY5op+smDRLxeBAivE7YcYaneXJ1la3HOkUfX9zKkE/AJ8CNgjiXbtXepFyFmJNGSbmjOwqbT749r/w==} + engines: {node: '>=6.0'} + dev: true + + /dir-compare@3.3.0: + resolution: {integrity: sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==} + dependencies: + buffer-equal: 1.0.1 + minimatch: 3.1.2 + dev: true + + /dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3): + resolution: {integrity: sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==} + dependencies: + app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + fs-extra: 10.1.0 + iconv-lite: 0.6.3 + js-yaml: 4.1.0 + optionalDependencies: + dmg-license: 1.0.11 + transitivePeerDependencies: + - electron-builder-squirrel-windows + - supports-color + dev: true + + /dmg-license@1.0.11: + resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==} + engines: {node: '>=8'} + os: [darwin] + hasBin: true + requiresBuild: true + dependencies: + '@types/plist': 3.0.5 + '@types/verror': 1.10.9 + ajv: 6.12.6 + crc: 3.8.0 + iconv-corefoundation: 1.1.7 + plist: 3.1.0 + smart-buffer: 4.2.0 + verror: 1.10.1 + dev: true + optional: true + + /dom-align@1.12.4: + resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==} + dev: true + + /dom-scroll-into-view@2.0.1: + resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==} + dev: true + + /dom-serializer@0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + dev: true + + /dom-to-image@2.6.0: + resolution: {integrity: sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==} + dev: true + + /domelementtype@1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + dev: true + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: true + + /domutils@1.5.1: + resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==} + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + dev: true + + /dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + dev: true + + /dotenv@9.0.2: + resolution: {integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==} + engines: {node: '>=10'} + dev: true + + /duplexer3@0.1.5: + resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} + dev: true + + /ejs@3.1.9: + resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + jake: 10.8.7 + dev: true + + /electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3): + resolution: {integrity: sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==} + dependencies: + app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) + archiver: 5.3.2 + builder-util: 24.13.1 + fs-extra: 10.1.0 + transitivePeerDependencies: + - dmg-builder + - supports-color + dev: true + + /electron-builder@24.13.3(electron-builder-squirrel-windows@24.13.3): + resolution: {integrity: sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + chalk: 4.1.2 + dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) + fs-extra: 10.1.0 + is-ci: 3.0.1 + lazy-val: 1.0.5 + read-config-file: 6.3.2 + simple-update-notifier: 2.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - electron-builder-squirrel-windows + - supports-color + dev: true + + /electron-is-dev@0.3.0: + resolution: {integrity: sha512-jLttuuq8QK67n3mXmIe9pkrO7IH3LGIk12xJkhTmc852U2sCJaRAOpRGPSh+1Xnzck5v9escd9YXzuze9nGejg==} + dev: true + + /electron-is@3.0.0: + resolution: {integrity: sha512-aQv1y3WrDZ+mtO8acbhiiip/8fa0Et7cvZyvlqJm2H7fih4hiJWEFRyYxzLgDG2kmiLdF8l3y5tbek5JFOPQkQ==} + dependencies: + electron-is-dev: 0.3.0 + semver: 5.7.2 + dev: true + + /electron-publish@24.13.1: + resolution: {integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==} + dependencies: + '@types/fs-extra': 9.0.13 + builder-util: 24.13.1 + builder-util-runtime: 9.2.4 + chalk: 4.1.2 + fs-extra: 10.1.0 + lazy-val: 1.0.5 + mime: 2.6.0 + transitivePeerDependencies: + - supports-color + dev: true + + /electron-to-chromium@1.4.601: + resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==} + dev: true + + /electron@21.4.4: + resolution: {integrity: sha512-N5O7y7Gtt7mDgkJLkW49ETiT8M3myZ9tNIEvGTKhpBduX4WdgMj6c3hYeYBD6XW7SvbRkWEQaTl25RNday8Xpw==} + engines: {node: '>= 10.17.0'} + hasBin: true + requiresBuild: true + dependencies: + '@electron/get': 1.14.1 + '@types/node': 16.18.82 + extract-zip: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /elementtree@0.1.7: + resolution: {integrity: sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==} + engines: {node: '>= 0.4.0'} + dependencies: + sax: 1.1.4 + dev: true + + /elliptic@6.5.4: + resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + requiresBuild: true + dev: true + optional: true + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + + /entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: true + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: true + + /env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: true + + /err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + dev: true + + /errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + requiresBuild: true + dependencies: + prr: 1.0.1 + dev: true + optional: true + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + requiresBuild: true + dev: true + optional: true + + /es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + requiresBuild: true + dev: true + optional: true + + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + requiresBuild: true + dev: true + optional: true + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: true + + /extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + dependencies: + debug: 4.3.4 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + dev: true + + /extsprintf@1.4.1: + resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} + engines: {'0': node >=0.6.0} + requiresBuild: true + dev: true + optional: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + requiresBuild: true + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + requiresBuild: true + dev: true + + /fast-levenshtein@3.0.0: + resolution: {integrity: sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==} + dependencies: + fastest-levenshtein: 1.0.16 + dev: true + + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: true + + /fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + dev: true + + /fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + dependencies: + pend: 1.2.0 + dev: true + + /filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + dependencies: + minimatch: 5.1.6 + dev: true + + /fix-path@4.0.0: + resolution: {integrity: sha512-g31GX207Tt+psI53ZSaB1egprYbEN0ZYl90aKcO22A2LmCNnFsSq3b5YpoKp3E/QEiWByTXGJOkFQG4S07Bc1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + shell-path: 3.0.0 + dev: true + + /follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: true + + /form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: true + + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: true + + /fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + + /fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: true + + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + requiresBuild: true + dev: true + optional: true + + /fuzzysort@2.0.4: + resolution: {integrity: sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + requiresBuild: true + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: true + optional: true + + /get-stream@4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + dependencies: + pump: 3.0.0 + dev: true + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /global-agent@3.0.0: + resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} + engines: {node: '>=10.0'} + requiresBuild: true + dependencies: + boolean: 3.2.0 + es6-error: 4.1.1 + matcher: 3.0.0 + roarr: 2.15.4 + semver: 7.6.0 + serialize-error: 7.0.1 + dev: true + optional: true + + /global-tunnel-ng@2.7.1: + resolution: {integrity: sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==} + engines: {node: '>=0.10'} + requiresBuild: true + dependencies: + encodeurl: 1.0.2 + lodash: 4.17.21 + npm-conf: 1.1.3 + tunnel: 0.0.6 + dev: true + optional: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + requiresBuild: true + dependencies: + define-properties: 1.2.1 + dev: true + optional: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + requiresBuild: true + dependencies: + get-intrinsic: 1.2.4 + dev: true + optional: true + + /got@9.6.0: + resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} + engines: {node: '>=8.6'} + dependencies: + '@sindresorhus/is': 0.14.0 + '@szmarczak/http-timer': 1.1.2 + '@types/keyv': 3.1.4 + '@types/responselike': 1.0.3 + cacheable-request: 6.1.0 + decompress-response: 3.3.0 + duplexer3: 0.1.5 + get-stream: 4.1.0 + lowercase-keys: 1.0.1 + mimic-response: 1.0.1 + p-cancelable: 1.1.0 + to-readable-stream: 1.0.0 + url-parse-lax: 3.0.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + requiresBuild: true + dependencies: + get-intrinsic: 1.2.4 + dev: true + optional: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + requiresBuild: true + dev: true + optional: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + requiresBuild: true + dev: true + optional: true + + /hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: true + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + requiresBuild: true + dependencies: + function-bind: 1.1.2 + dev: true + optional: true + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + + /hls.js@1.5.7: + resolution: {integrity: sha512-Hnyf7ojTBtXHeOW1/t6wCBJSiK1WpoKF9yg7juxldDx8u3iswrkPt2wbOA/1NiwU4j27DSIVoIEJRAhcdMef/A==} + dev: true + + /hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: true + + /hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + + /howler@2.2.4: + resolution: {integrity: sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==} + dev: true + + /html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + dev: true + + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true + + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /iconv-corefoundation@1.1.7: + resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} + engines: {node: ^8.11.2 || >=10} + os: [darwin] + requiresBuild: true + dependencies: + cli-truncate: 2.1.0 + node-addon-api: 1.7.2 + dev: true + optional: true + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + requiresBuild: true + dev: true + + /image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + requiresBuild: true + dev: true + optional: true + + /ip@1.1.9: + resolution: {integrity: sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==} + dev: true + + /ip@2.0.1: + resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} + dev: true + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: true + + /is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + dependencies: + ci-info: 3.9.0 + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-plain-object@3.0.1: + resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==} + engines: {node: '>=0.10.0'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + dev: true + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /isbinaryfile@4.0.10: + resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} + engines: {node: '>= 8.0.0'} + dev: true + + /isbinaryfile@5.0.0: + resolution: {integrity: sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==} + engines: {node: '>= 14.0.0'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + dependencies: + node-fetch: 2.7.0 + whatwg-fetch: 3.6.19 + transitivePeerDependencies: + - encoding + dev: true + + /jake@10.8.7: + resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} + engines: {node: '>=10'} + hasBin: true + dependencies: + async: 3.2.5 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + dev: true + + /jassub@1.7.15: + resolution: {integrity: sha512-8yKAJc++Y1gNfATOPRo3APk0JUhshKl5l7bRkT6WkJ8XP4RvYfVPb6ieH6WDxsMq523exwGzNvjjPEEWT+Z1nQ==} + dependencies: + rvfc-polyfill: 1.0.7 + dev: true + + /js-md5@0.7.3: + resolution: {integrity: sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==} + dev: true + + /js-sha1@0.6.0: + resolution: {integrity: sha512-01gwBFreYydzmU9BmZxpVk6svJJHrVxEN3IOiGl6VO93bVKYETJ0sIth6DASI6mIFdt7NmfX9UiByRzsYHGU9w==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jschardet@3.0.0: + resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==} + engines: {node: '>=0.1.90'} + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.0: + resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + requiresBuild: true + dev: true + + /json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + requiresBuild: true + dev: true + optional: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + + /keyv@3.1.0: + resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} + dependencies: + json-buffer: 3.0.0 + dev: true + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /lazy-val@1.0.5: + resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} + dev: true + + /lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + dependencies: + readable-stream: 2.3.8 + dev: true + + /less@4.2.0: + resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} + engines: {node: '>=6'} + hasBin: true + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.6.2 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + dependencies: + uc.micro: 2.1.0 + dev: true + + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: true + + /lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + dev: true + + /lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + dev: true + + /lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + dev: true + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + + /lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + dev: true + + /lodash@3.10.1: + resolution: {integrity: sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /lowercase-keys@1.0.1: + resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} + engines: {node: '>=0.10.0'} + dev: true + + /lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string@0.30.7: + resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + pify: 4.0.1 + semver: 5.7.2 + dev: true + optional: true + + /markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + dev: true + + /matcher@3.0.0: + resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + escape-string-regexp: 4.0.0 + dev: true + optional: true + + /mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: true + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: true + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + dev: true + + /minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + dev: true + + /minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: true + + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true + + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: true + + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + requiresBuild: true + dev: true + + /muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + dev: true + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /nanopop@2.3.0: + resolution: {integrity: sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==} + dev: true + + /needle@3.2.0: + resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==} + engines: {node: '>= 4.4.x'} + hasBin: true + requiresBuild: true + dependencies: + debug: 3.2.7 + iconv-lite: 0.6.3 + sax: 1.3.0 + transitivePeerDependencies: + - supports-color + dev: true + optional: true + + /node-addon-api@1.7.2: + resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + requiresBuild: true + dev: true + optional: true + + /node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + dev: true + + /node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + + /node-gyp-build@4.7.1: + resolution: {integrity: sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==} + hasBin: true + dev: true + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true + + /node-ssdp@4.0.1: + resolution: {integrity: sha512-uJXkLZVuyaMg1qNbMbGQ6YzNzyOD+NLxYyxIJocPTKTVECPDokOiCZA686jTLXHMUnV34uY/lcUSJ+/5fhY43A==} + engines: {node: '>=0.10.0'} + dependencies: + async: 2.6.4 + bluebird: 3.7.2 + debug: 3.2.7 + extend: 3.0.2 + ip: 1.1.9 + transitivePeerDependencies: + - supports-color + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-url@4.5.1: + resolution: {integrity: sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==} + engines: {node: '>=8'} + dev: true + + /npm-conf@1.1.3: + resolution: {integrity: sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + config-chain: 1.1.13 + pify: 3.0.0 + dev: true + optional: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /nth-check@1.0.2: + resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} + dependencies: + boolbase: 1.0.0 + dev: true + + /number-precision@1.6.0: + resolution: {integrity: sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + requiresBuild: true + dev: true + optional: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /option-validator@2.0.6: + resolution: {integrity: sha512-tmZDan2LRIRQyhUGvkff68/O0R8UmF+Btmiiz0SmSw2ng3CfPZB9wJlIjHpe/MKUZqyIZkVIXCrwr1tIN+0Dzg==} + dependencies: + kind-of: 6.0.3 + dev: true + + /p-cancelable@1.1.0: + resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} + engines: {node: '>=6'} + dev: true + + /pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + dev: true + + /parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + dev: true + + /parse5@1.5.1: + resolution: {integrity: sha512-w2jx/0tJzvgKwZa58sj2vAYq/S/K1QJfIB3cWYea/Iu1scFPDQQ3IQiVZTHWtRBwAjv2Yd7S/xeZf3XqLDb3bA==} + dev: true + + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-to-regexp@6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: true + + /pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + dev: true + + /perf_hooks@0.0.1: + resolution: {integrity: sha512-qG/D9iA4KDme+KF4vCObJy6Bouu3BlQnmJ8jPydVPm32NJBD9ZK1ZNgXSYaZKHkVC1sKSqUiLgFvAZPUiIEnBw==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + requiresBuild: true + dev: true + optional: true + + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + requiresBuild: true + dev: true + optional: true + + /pinia@2.1.7(typescript@5.4.3)(vue@3.4.21): + resolution: {integrity: sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==} + peerDependencies: + '@vue/composition-api': ^1.4.0 + typescript: '>=4.4.4' + vue: ^2.6.14 || ^3.3.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + typescript: + optional: true + dependencies: + '@vue/devtools-api': 6.5.1 + typescript: 5.4.3 + vue: 3.4.21(typescript@5.4.3) + vue-demi: 0.14.6(vue@3.4.21) + dev: true + + /plist@3.1.0: + resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} + engines: {node: '>=10.4.0'} + dependencies: + '@xmldom/xmldom': 0.8.10 + base64-js: 1.5.1 + xmlbuilder: 15.1.1 + dev: true + + /postcss@8.4.35: + resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: true + + /prepend-http@2.0.0: + resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} + engines: {node: '>=4'} + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true + + /promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + dev: true + + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + requiresBuild: true + dev: true + optional: true + + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: true + + /prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + requiresBuild: true + dev: true + optional: true + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + + /punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + requiresBuild: true + dev: true + + /read-config-file@6.3.2: + resolution: {integrity: sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==} + engines: {node: '>=12.0.0'} + dependencies: + config-file-ts: 0.2.4 + dotenv: 9.0.2 + dotenv-expand: 5.1.0 + js-yaml: 4.1.0 + json5: 2.2.3 + lazy-val: 1.0.5 + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.2 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + dependencies: + minimatch: 5.1.6 + dev: true + + /regenerator-runtime@0.14.0: + resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + dev: true + + /responselike@1.0.2: + resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} + dependencies: + lowercase-keys: 1.0.1 + dev: true + + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /roarr@2.15.4: + resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} + engines: {node: '>=8.0'} + requiresBuild: true + dependencies: + boolean: 3.2.0 + detect-node: 2.1.0 + globalthis: 1.0.3 + json-stringify-safe: 5.0.1 + semver-compare: 1.0.0 + sprintf-js: 1.1.3 + dev: true + optional: true + + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 + fsevents: 2.3.3 + dev: true + + /rvfc-polyfill@1.0.7: + resolution: {integrity: sha512-seBl7J1J3/k0LuzW2T9fG6JIOpni5AbU+/87LA+zTYKgTVhsfShmS8K/yOo1eeEjGJHnAdkVAUUM+PEjN9Mpkw==} + dev: true + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /sanitize-filename@1.6.3: + resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + dependencies: + truncate-utf8-bytes: 1.0.2 + dev: true + + /sax@1.1.4: + resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==} + dev: true + + /sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + dev: true + + /scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + dependencies: + compute-scroll-into-view: 1.0.20 + dev: true + + /secp256k1@5.0.0: + resolution: {integrity: sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==} + engines: {node: '>=14.0.0'} + requiresBuild: true + dependencies: + elliptic: 6.5.4 + node-addon-api: 5.1.0 + node-gyp-build: 4.7.1 + dev: true + + /semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + requiresBuild: true + dev: true + optional: true + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /serialize-error@7.0.1: + resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} + engines: {node: '>=10'} + requiresBuild: true + dependencies: + type-fest: 0.13.1 + dev: true + optional: true + + /shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shell-env@4.0.1: + resolution: {integrity: sha512-w3oeZ9qg/P6Lu6qqwavvMnB/bwfsz67gPB3WXmLd/n6zuh7TWQZtGa3iMEdmua0kj8rivkwl+vUjgLWlqZOMPw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + default-shell: 2.2.0 + execa: 5.1.1 + strip-ansi: 7.1.0 + dev: true + + /shell-path@3.0.0: + resolution: {integrity: sha512-HNIZ+W/3P0JuVTV03xjGqYKt3e3h0/Z4AH8TQWeth1LBtCusSjICgkdNdb3VZr6mI7ijE2AiFFpgkVMNKsALeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + shell-env: 4.0.1 + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: true + + /simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.0 + dev: true + + /slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + optional: true + + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: true + + /socks-proxy-agent@8.0.2: + resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + socks: 2.7.1 + transitivePeerDependencies: + - supports-color + dev: true + + /socks@2.7.1: + resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} + engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + dependencies: + ip: 2.0.1 + smart-buffer: 4.2.0 + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + requiresBuild: true + dev: true + optional: true + + /stat-mode@1.0.0: + resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} + engines: {node: '>= 6'} + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + dev: true + + /stylis@4.3.1: + resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==} + dev: true + + /sumchecker@3.0.1: + resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} + engines: {node: '>= 8.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + dev: true + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /tar@6.2.0: + resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} + engines: {node: '>=10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: true + + /temp-file@3.4.0: + resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} + dependencies: + async-exit-hook: 2.0.1 + fs-extra: 10.1.0 + dev: true + + /terser@5.29.2: + resolution: {integrity: sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.5 + acorn: 8.11.2 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /throttle-debounce@5.0.0: + resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==} + engines: {node: '>=12.22'} + dev: true + + /thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + dev: true + + /tmp-promise@3.0.3: + resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} + dependencies: + tmp: 0.2.1 + dev: true + + /tmp@0.2.1: + resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} + engines: {node: '>=8.17.0'} + dependencies: + rimraf: 3.0.2 + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-readable-stream@1.0.0: + resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==} + engines: {node: '>=6'} + dev: true + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: true + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /truncate-utf8-bytes@1.0.2: + resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} + dependencies: + utf8-byte-length: 1.0.4 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: true + + /tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + requiresBuild: true + dev: true + optional: true + + /type-fest@0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + requiresBuild: true + dev: true + optional: true + + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /typescript@5.4.3: + resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.2): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.2 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /upnp-client-ts@1.1.1: + resolution: {integrity: sha512-UTe5r/2sAth2g4lama6ETTJxYcIhcKRATZZmdb+BavPqPuY3GRSXPxLgDtbc64dqwvomKZecEEiwT+6vm8mWpw==} + engines: {node: '>=18.0.0'} + dependencies: + elementtree: 0.1.7 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + requiresBuild: true + dependencies: + punycode: 2.3.1 + dev: true + + /url-parse-lax@3.0.0: + resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} + engines: {node: '>=4'} + dependencies: + prepend-http: 2.0.0 + dev: true + + /utf8-byte-length@1.0.4: + resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==} + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /uuid-by-string@4.0.0: + resolution: {integrity: sha512-88ZSfcSkN04juiLqSsuyteqlSrXNFdsEPzSv3urnElDXNsZUXQN0smeTnh99x2DE15SCUQNgqKBfro54CuzHNQ==} + dependencies: + js-md5: 0.7.3 + js-sha1: 0.6.0 + dev: true + + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: true + + /verror@1.10.1: + resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} + engines: {node: '>=0.6.0'} + requiresBuild: true + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.4.1 + dev: true + optional: true + + /viewerjs@1.11.6: + resolution: {integrity: sha512-TlhdSp2oEOLFXvEp4psKaeTjR5zBjTRcM/sHUN8PkV1UWuY8HKC8n7GaVdW5Xqnwdr/F1OmzLik1QwDjI4w/nw==} + dev: true + + /vite-plugin-electron-renderer@0.14.5: + resolution: {integrity: sha512-EQ7ORuPp8vFPCqfuGnVo7d36fXS0IFH4/RUlKb1drseix3TQEPcgwEuFADdXBxRgqMp70njz/1m0kdf5lEsm8w==} + dev: true + + /vite-plugin-electron@0.15.6(tree-kill@1.2.2)(vite-plugin-electron-renderer@0.14.5): + resolution: {integrity: sha512-sbjzNiH8N6Was67oIUgglW6tefahkiCTItM3MsdOAQ0xhUMxizeQMF9DhSvEBwvLvUGKYboIdIYskSchV7Akrg==} + peerDependencies: + tree-kill: '*' + vite-plugin-electron-renderer: '*' + peerDependenciesMeta: + tree-kill: + optional: true + vite-plugin-electron-renderer: + optional: true + dependencies: + tree-kill: 1.2.2 + vite-plugin-electron-renderer: 0.14.5 + dev: true + + /vite@5.2.2(@types/node@20.11.30)(less@4.2.0)(terser@5.29.2): + resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.11.30 + esbuild: 0.20.2 + less: 4.2.0 + postcss: 8.4.38 + rollup: 4.13.0 + terser: 5.29.2 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vue-demi@0.14.6(vue@3.4.21): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.4.21(typescript@5.4.3) + dev: true + + /vue-template-compiler@2.7.15: + resolution: {integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==} + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + dev: true + + /vue-tsc@2.0.7(typescript@5.4.3): + resolution: {integrity: sha512-LYa0nInkfcDBB7y8jQ9FQ4riJTRNTdh98zK/hzt4gEpBZQmf30dPhP+odzCa+cedGz6B/guvJEd0BavZaRptjg==} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + '@volar/typescript': 2.1.4 + '@vue/language-core': 2.0.7(typescript@5.4.3) + semver: 7.6.0 + typescript: 5.4.3 + dev: true + + /vue-types@3.0.2(vue@3.4.21): + resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==} + engines: {node: '>=10.15.0'} + peerDependencies: + vue: ^3.0.0 + dependencies: + is-plain-object: 3.0.1 + vue: 3.4.21(typescript@5.4.3) + dev: true + + /vue@3.4.21(typescript@5.4.3): + resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@vue/compiler-dom': 3.4.21 + '@vue/compiler-sfc': 3.4.21 + '@vue/runtime-dom': 3.4.21 + '@vue/server-renderer': 3.4.21(vue@3.4.21) + '@vue/shared': 3.4.21 + typescript: 5.4.3 + dev: true + + /warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + dependencies: + loose-envify: 1.4.0 + dev: true + + /webdav-server@2.6.2: + resolution: {integrity: sha512-0iHdrOzlKGFD96bTvPF8IIEfxw9Q7jB5LqWqhjyBYsofD6T6mOYqWtAvR88VY9Mq0xeg8bCRHC2Vifc9iuTYuw==} + engines: {node: '>= 4'} + dependencies: + mime-types: 2.1.35 + xml-js-builder: 1.0.3 + dev: true + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true + + /whacko@0.19.1: + resolution: {integrity: sha512-OnO3ZX6wIC21GASyIl6ZcVYhu+BaIcaGNtads+O9U/gFqQ4dkUrpauAb3WMSWFK+2qzDfxEXnOxBqQnS1/NrXQ==} + engines: {node: '>= 0.6'} + dependencies: + css-select: 1.2.0 + domutils: 1.5.1 + lodash: 3.10.1 + parse5: 1.5.1 + dev: true + + /whatwg-fetch@3.6.19: + resolution: {integrity: sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==} + dev: true + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /xml-js-builder@1.0.3: + resolution: {integrity: sha512-BoLgG/glT45M0jK5PGh9h+iGrQxa8jJk9ofR63GroRifl2tbGB3/yYiVY3wQWHrZgWWfl9+7fhEB/VoD9mWnSg==} + engines: {node: '>= 4'} + dependencies: + xml-js: 1.6.11 + dev: true + + /xml-js@1.6.11: + resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} + hasBin: true + dependencies: + sax: 1.3.0 + dev: true + + /xmlbuilder@15.1.1: + resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} + engines: {node: '>=8.0'} + requiresBuild: true + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + dev: true + + /zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + dev: true diff --git a/aliyunpan/postcss.config.js b/aliyunpan/postcss.config.js new file mode 100644 index 0000000000..33ad091d26 --- /dev/null +++ b/aliyunpan/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/aliyunpan/public/audio/down_finished.mp3 b/aliyunpan/public/audio/download_finished.mp3 similarity index 100% rename from aliyunpan/public/audio/down_finished.mp3 rename to aliyunpan/public/audio/download_finished.mp3 diff --git a/aliyunpan/public/comlink.js b/aliyunpan/public/comlink.js deleted file mode 100644 index 4ba9504c53..0000000000 --- a/aliyunpan/public/comlink.js +++ /dev/null @@ -1,319 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.Comlink = {})); -}(this, (function (exports) { 'use strict'; - - /** - * Copyright 2019 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - const proxyMarker = Symbol("Comlink.proxy"); - const createEndpoint = Symbol("Comlink.endpoint"); - const releaseProxy = Symbol("Comlink.releaseProxy"); - const throwMarker = Symbol("Comlink.thrown"); - const isObject = (val) => (typeof val === "object" && val !== null) || typeof val === "function"; - /** - * Internal transfer handle to handle objects marked to proxy. - */ - const proxyTransferHandler = { - canHandle: (val) => isObject(val) && val[proxyMarker], - serialize(obj) { - const { port1, port2 } = new MessageChannel(); - expose(obj, port1); - return [port2, [port2]]; - }, - deserialize(port) { - port.start(); - return wrap(port); - }, - }; - /** - * Internal transfer handler to handle thrown exceptions. - */ - const throwTransferHandler = { - canHandle: (value) => isObject(value) && throwMarker in value, - serialize({ value }) { - let serialized; - if (value instanceof Error) { - serialized = { - isError: true, - value: { - message: value.message, - name: value.name, - stack: value.stack, - }, - }; - } - else { - serialized = { isError: false, value }; - } - return [serialized, []]; - }, - deserialize(serialized) { - if (serialized.isError) { - throw Object.assign(new Error(serialized.value.message), serialized.value); - } - throw serialized.value; - }, - }; - /** - * Allows customizing the serialization of certain values. - */ - const transferHandlers = new Map([ - ["proxy", proxyTransferHandler], - ["throw", throwTransferHandler], - ]); - function expose(obj, ep = self) { - ep.addEventListener("message", function callback(ev) { - if (!ev || !ev.data) { - return; - } - const { id, type, path } = Object.assign({ path: [] }, ev.data); - const argumentList = (ev.data.argumentList || []).map(fromWireValue); - let returnValue; - try { - const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj); - const rawValue = path.reduce((obj, prop) => obj[prop], obj); - switch (type) { - case "GET" /* GET */: - { - returnValue = rawValue; - } - break; - case "SET" /* SET */: - { - parent[path.slice(-1)[0]] = fromWireValue(ev.data.value); - returnValue = true; - } - break; - case "APPLY" /* APPLY */: - { - returnValue = rawValue.apply(parent, argumentList); - } - break; - case "CONSTRUCT" /* CONSTRUCT */: - { - const value = new rawValue(...argumentList); - returnValue = proxy(value); - } - break; - case "ENDPOINT" /* ENDPOINT */: - { - const { port1, port2 } = new MessageChannel(); - expose(obj, port2); - returnValue = transfer(port1, [port1]); - } - break; - case "RELEASE" /* RELEASE */: - { - returnValue = undefined; - } - break; - default: - return; - } - } - catch (value) { - returnValue = { value, [throwMarker]: 0 }; - } - Promise.resolve(returnValue) - .catch((value) => { - return { value, [throwMarker]: 0 }; - }) - .then((returnValue) => { - const [wireValue, transferables] = toWireValue(returnValue); - ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables); - if (type === "RELEASE" /* RELEASE */) { - // detach and deactive after sending release response above. - ep.removeEventListener("message", callback); - closeEndPoint(ep); - } - }); - }); - if (ep.start) { - ep.start(); - } - } - function isMessagePort(endpoint) { - return endpoint.constructor.name === "MessagePort"; - } - function closeEndPoint(endpoint) { - if (isMessagePort(endpoint)) - endpoint.close(); - } - function wrap(ep, target) { - return createProxy(ep, [], target); - } - function throwIfProxyReleased(isReleased) { - if (isReleased) { - throw new Error("Proxy has been released and is not useable"); - } - } - function createProxy(ep, path = [], target = function () { }) { - let isProxyReleased = false; - const proxy = new Proxy(target, { - get(_target, prop) { - throwIfProxyReleased(isProxyReleased); - if (prop === releaseProxy) { - return () => { - return requestResponseMessage(ep, { - type: "RELEASE" /* RELEASE */, - path: path.map((p) => p.toString()), - }).then(() => { - closeEndPoint(ep); - isProxyReleased = true; - }); - }; - } - if (prop === "then") { - if (path.length === 0) { - return { then: () => proxy }; - } - const r = requestResponseMessage(ep, { - type: "GET" /* GET */, - path: path.map((p) => p.toString()), - }).then(fromWireValue); - return r.then.bind(r); - } - return createProxy(ep, [...path, prop]); - }, - set(_target, prop, rawValue) { - throwIfProxyReleased(isProxyReleased); - // FIXME: ES6 Proxy Handler `set` methods are supposed to return a - // boolean. To show good will, we return true asynchronously ¯\_(ツ)_/¯ - const [value, transferables] = toWireValue(rawValue); - return requestResponseMessage(ep, { - type: "SET" /* SET */, - path: [...path, prop].map((p) => p.toString()), - value, - }, transferables).then(fromWireValue); - }, - apply(_target, _thisArg, rawArgumentList) { - throwIfProxyReleased(isProxyReleased); - const last = path[path.length - 1]; - if (last === createEndpoint) { - return requestResponseMessage(ep, { - type: "ENDPOINT" /* ENDPOINT */, - }).then(fromWireValue); - } - // We just pretend that `bind()` didn’t happen. - if (last === "bind") { - return createProxy(ep, path.slice(0, -1)); - } - const [argumentList, transferables] = processArguments(rawArgumentList); - return requestResponseMessage(ep, { - type: "APPLY" /* APPLY */, - path: path.map((p) => p.toString()), - argumentList, - }, transferables).then(fromWireValue); - }, - construct(_target, rawArgumentList) { - throwIfProxyReleased(isProxyReleased); - const [argumentList, transferables] = processArguments(rawArgumentList); - return requestResponseMessage(ep, { - type: "CONSTRUCT" /* CONSTRUCT */, - path: path.map((p) => p.toString()), - argumentList, - }, transferables).then(fromWireValue); - }, - }); - return proxy; - } - function myFlat(arr) { - return Array.prototype.concat.apply([], arr); - } - function processArguments(argumentList) { - const processed = argumentList.map(toWireValue); - return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))]; - } - const transferCache = new WeakMap(); - function transfer(obj, transfers) { - transferCache.set(obj, transfers); - return obj; - } - function proxy(obj) { - return Object.assign(obj, { [proxyMarker]: true }); - } - function windowEndpoint(w, context = self, targetOrigin = "*") { - return { - postMessage: (msg, transferables) => w.postMessage(msg, targetOrigin, transferables), - addEventListener: context.addEventListener.bind(context), - removeEventListener: context.removeEventListener.bind(context), - }; - } - function toWireValue(value) { - for (const [name, handler] of transferHandlers) { - if (handler.canHandle(value)) { - const [serializedValue, transferables] = handler.serialize(value); - return [ - { - type: "HANDLER" /* HANDLER */, - name, - value: serializedValue, - }, - transferables, - ]; - } - } - return [ - { - type: "RAW" /* RAW */, - value, - }, - transferCache.get(value) || [], - ]; - } - function fromWireValue(value) { - switch (value.type) { - case "HANDLER" /* HANDLER */: - return transferHandlers.get(value.name).deserialize(value.value); - case "RAW" /* RAW */: - return value.value; - } - } - function requestResponseMessage(ep, msg, transfers) { - return new Promise((resolve) => { - const id = generateUUID(); - ep.addEventListener("message", function l(ev) { - if (!ev.data || !ev.data.id || ev.data.id !== id) { - return; - } - ep.removeEventListener("message", l); - resolve(ev.data); - }); - if (ep.start) { - ep.start(); - } - ep.postMessage(Object.assign({ id }, msg), transfers); - }); - } - function generateUUID() { - return new Array(4) - .fill(0) - .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)) - .join("-"); - } - - exports.createEndpoint = createEndpoint; - exports.expose = expose; - exports.proxy = proxy; - exports.proxyMarker = proxyMarker; - exports.releaseProxy = releaseProxy; - exports.transfer = transfer; - exports.transferHandlers = transferHandlers; - exports.windowEndpoint = windowEndpoint; - exports.wrap = wrap; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); -//# sourceMappingURL=comlink.js.map \ No newline at end of file diff --git a/aliyunpan/public/favicon.png b/aliyunpan/public/favicon.png new file mode 100644 index 0000000000..5a9e9dfe6c Binary files /dev/null and b/aliyunpan/public/favicon.png differ diff --git a/aliyunpan/public/font/VideoJS.woff b/aliyunpan/public/font/VideoJS.woff deleted file mode 100644 index 4b846ae5e0..0000000000 Binary files a/aliyunpan/public/font/VideoJS.woff and /dev/null differ diff --git a/aliyunpan/public/iconfont.css b/aliyunpan/public/iconfont.css index 687803b7a7..a70e770ec3 100644 --- a/aliyunpan/public/iconfont.css +++ b/aliyunpan/public/iconfont.css @@ -4,9 +4,8 @@ } .iconfont { - font-family: "iconfont" !important; + font-family: "iconfont", serif !important; font-size: 16px; - color: #0078D7; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -805,10 +804,6 @@ content: "\e842"; } -.iconhistory:before { - content: "\e6bb"; -} - .iconset:before { content: "\e623"; } diff --git a/aliyunpan/public/images/alicode.png b/aliyunpan/public/images/alicode.png deleted file mode 100644 index f70a7a18a3..0000000000 Binary files a/aliyunpan/public/images/alicode.png and /dev/null differ diff --git a/aliyunpan/public/images/indicator.svg b/aliyunpan/public/images/indicator.svg new file mode 100644 index 0000000000..e0c134312f --- /dev/null +++ b/aliyunpan/public/images/indicator.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/aliyunpan/public/images/loading.svg b/aliyunpan/public/images/loading.svg new file mode 100644 index 0000000000..197d5bd20c --- /dev/null +++ b/aliyunpan/public/images/loading.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/aliyunpan/public/images/music_bg.jpg b/aliyunpan/public/images/music_bg.jpg new file mode 100644 index 0000000000..8d328fc77b Binary files /dev/null and b/aliyunpan/public/images/music_bg.jpg differ diff --git a/aliyunpan/public/images/pause.svg b/aliyunpan/public/images/pause.svg new file mode 100644 index 0000000000..752d72ed85 --- /dev/null +++ b/aliyunpan/public/images/pause.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/aliyunpan/public/images/ploading.gif b/aliyunpan/public/images/ploading.gif new file mode 100644 index 0000000000..1a8c97d044 Binary files /dev/null and b/aliyunpan/public/images/ploading.gif differ diff --git a/aliyunpan/public/images/sales.png b/aliyunpan/public/images/sales.png index d4d0322b88..d75c01b46e 100644 Binary files a/aliyunpan/public/images/sales.png and b/aliyunpan/public/images/sales.png differ diff --git a/aliyunpan/public/images/state.svg b/aliyunpan/public/images/state.svg new file mode 100644 index 0000000000..8c8ab30185 --- /dev/null +++ b/aliyunpan/public/images/state.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aliyunpan/public/imgerror.png b/aliyunpan/public/imgerror.png deleted file mode 100644 index d84bbd63b7..0000000000 Binary files a/aliyunpan/public/imgerror.png and /dev/null differ diff --git a/aliyunpan/public/loading.png b/aliyunpan/public/loading.png deleted file mode 100644 index 59e18d9ae2..0000000000 Binary files a/aliyunpan/public/loading.png and /dev/null differ diff --git a/aliyunpan/public/main.html b/aliyunpan/public/main.html index 1709205f94..023675c3fc 100644 --- a/aliyunpan/public/main.html +++ b/aliyunpan/public/main.html @@ -2,13 +2,13 @@ - 小白羊云盘 + 阿里云盘小白羊 - + - + - +
diff --git a/aliyunpan/public/main2.html b/aliyunpan/public/main2.html index 7e77259084..4820a7a297 100644 --- a/aliyunpan/public/main2.html +++ b/aliyunpan/public/main2.html @@ -2,16 +2,16 @@ - 小白羊云盘 + 阿里云盘小白羊 - - - - + + + + - +
diff --git a/aliyunpan/public/notify.wav b/aliyunpan/public/notify.wav deleted file mode 100644 index 6bf41a35d1..0000000000 Binary files a/aliyunpan/public/notify.wav and /dev/null differ diff --git a/aliyunpan/public/userface.png b/aliyunpan/public/userface.png deleted file mode 100644 index 0565d3db6f..0000000000 Binary files a/aliyunpan/public/userface.png and /dev/null differ diff --git a/aliyunpan/src/App.vue b/aliyunpan/src/App.vue index c7a627ac08..121d19fe27 100644 --- a/aliyunpan/src/App.vue +++ b/aliyunpan/src/App.vue @@ -10,7 +10,7 @@ import PageVideoXBTVue from './layout/PageVideoXBT.vue' import PageCode from './layout/PageCode.vue' import PageOffice from './layout/PageOffice.vue' import PageImage from './layout/PageImage.vue' -import PageHelp from './layout/PageHelp.vue' +import PageAudio from './layout/PageAudio.vue' import PageVideo from './layout/PageVideo.vue' import PageWorker from './layout/PageWorker.vue' @@ -20,12 +20,12 @@ export default { return () => { if (appStore.appPage == 'PageMain') return h(PageMain) - if (appStore.appPage == 'PageHelp') return h(PageHelp) if (appStore.appPage == 'PageOffice') return h(PageOffice) if (appStore.appPage == 'PageVideoXBT') return h(PageVideoXBTVue) if (appStore.appPage == 'PageCode') return h(PageCode) if (appStore.appPage == 'PageImage') return h(PageImage) if (appStore.appPage == 'PageVideo') return h(PageVideo) + if (appStore.appPage == 'PageAudio') return h(PageAudio) if (appStore.appPage == 'PageWorker') return h(PageWorker) return h(PageLoading) } diff --git a/aliyunpan/src/aliapi/album.ts b/aliyunpan/src/aliapi/album.ts index 77ccc7c54a..82bf879358 100644 --- a/aliyunpan/src/aliapi/album.ts +++ b/aliyunpan/src/aliapi/album.ts @@ -1,289 +1,455 @@ -import {AliAlbumFileInfo, IAliAlbumsList, IAliAlubmCreateInfo, IAliAlubmListInfo} from './alimodels' -import AliHttp, {IUrlRespData} from "./alihttp"; -import DebugLog from "../utils/debuglog"; -import {useSettingStore, useUserStore} from "../store"; -import {GetDriveID} from "./utils"; +import { + AliAlbumFileInfo, + IAliAlbumInfo, + IAliAlbumsList, + IAliAlubmCreateInfo, + IAliAlubmListInfo, + IAliGetDirModel +} from './alimodels' +import AliHttp, { IUrlRespData } from './alihttp' +import DebugLog from '../utils/debuglog' +import { HanToPin } from '../utils/utils' +import { GetDriveID } from './utils' +import { useSettingStore, useUserStore } from '../store' export default class AliAlbum { - static async ApiAlbumsList(): Promise { - const url = 'adrive/v1/album/list' - const albums: IAliAlbumsList[] = [] - if (!GetDriveID(useUserStore().user_id, 'pic')) { - return albums - } - const userId = useUserStore().user_id - let max: number = useSettingStore().debugFileListMax - let next_marker = '' - do { - const resp = await AliHttp.Post(url, {next_marker: next_marker}, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - const items = resp.body.items as IAliAlubmListInfo[] - if (items) { - items.forEach((item) => { - if (item.cover && item.cover.list - && item.cover.list.length > 0 - && item.cover.list[0].thumbnail) { - albums.push({ - name: item.album_id, - friendly_name: item.name, - preview: item.cover.list[0].thumbnail, - image_count: item.image_count - }) - } else { - albums.push({ - name: item.album_id, - friendly_name: item.name, - preview: '', - image_count: item.image_count - }) - } - - }) - next_marker = resp.body.next_marker - } else { - next_marker = '' - } - + static async ApiAlbumsList(): Promise { + const url = 'adrive/v1/album/list' + const albums: IAliAlbumsList[] = [] + if (!GetDriveID(useUserStore().user_id, 'pic')) { + return albums + } + const userId = useUserStore().user_id + let max: number = useSettingStore().debugFileListMax + let next_marker = '' + do { + const resp = await AliHttp.Post(url, {next_marker: next_marker}, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + const items = resp.body.items as IAliAlubmListInfo[] + if (items) { + items.forEach((item) => { + if (item.cover && item.cover.list + && item.cover.list.length > 0 + && item.cover.list[0].thumbnail) { + albums.push({ + name: item.album_id, + friendly_name: item.name, + preview: item.cover.list[0].thumbnail, + image_count: item.image_count + }) } else { - next_marker = '' - break + albums.push({ + name: item.album_id, + friendly_name: item.name, + preview: '', + image_count: item.image_count + }) } - if (albums.length >= max && max > 0) { - next_marker = '' - break - } - } while(next_marker) - return albums - } - static async ApiAlbumGet(album_id: string): Promise { - const userId = useUserStore().user_id - const url = 'adrive/v1/album/get' - const resp = await AliHttp.Post(url, {album_id}, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - return resp.body as IAliAlubmListInfo + }) + next_marker = resp.body.next_marker } else { - DebugLog.mSaveWarning('ApiAlbumsList err=' + (resp.code || '')) + next_marker = '' } - return undefined + + } else { + next_marker = '' + break + } + if (albums.length >= max && max > 0) { + next_marker = '' + break + } + } while(next_marker) + return albums + } + + static async ApiAlbumGet(album_id: string): Promise { + const userId = useUserStore().user_id + const url = 'adrive/v1/album/get' + const resp = await AliHttp.Post(url, {album_id}, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body as IAliAlubmListInfo + } else { + DebugLog.mSaveWarning('ApiAlbumsList err=' + (resp.code || '')) + } + return undefined + } + + static async ApiTotalPhotosNum(): Promise { + const driver_id = GetDriveID(useUserStore().user_id, 'pic') + const userId = useUserStore().user_id + const url = 'adrive/v2/file/search' + + if (!driver_id) { + return 0 + } + const postData = { + "drive_id": driver_id, + "query": "type = \"file\"", + "limit": 1, + "return_total_count": true, + "marker":"", + } + const resp = await AliHttp.Post(url, postData, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body.total_count + } + return 0 + } + + + static async ApiLimitedPhotos(marker="", limited=100): Promise { + const driver_id = GetDriveID(useUserStore().user_id, 'pic') + const userId = useUserStore().user_id + const url = 'adrive/v2/file/search' + let max: number = useSettingStore().debugFileListMax + + const results:AliAlbumFileInfo[] = [] + if (!driver_id) { + return results } - static async ApiTotalPhotosNum(): Promise { - const driver_id = GetDriveID(useUserStore().user_id, 'pic') - const userId = useUserStore().user_id - const url = 'adrive/v1.0/openFile/search' - - if (!driver_id) { - return 0 - } - const postData = { - "drive_id": driver_id, - "query": "type = \"file\"", - "limit": 1, - "return_total_count": true, - "marker":"", - } - const resp = await AliHttp.Post(url, postData, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - return resp.body.total_count - } - return 0 + const postData = { + "drive_id": driver_id, + "query": "type = \"file\"", + "image_thumbnail_process": "image/resize,w_400/format,jpeg", + "image_url_process": "image/resize,w_1920/format,jpeg", + "video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_1000", + "limit": limited, + "marker":marker, + "order_by": "created_at DESC" } + const resp = await AliHttp.Post(url, postData, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + const items = resp.body.items as AliAlbumFileInfo[] - - static async ApiLimitedPhotos(marker="", limited=100): Promise { - const driver_id = GetDriveID(useUserStore().user_id, 'pic') - const userId = useUserStore().user_id - const url = 'adrive/v1.0/openFile/search' - let max: number = useSettingStore().debugFileListMax - - const results:AliAlbumFileInfo[] = [] - if (!driver_id) { - return results - } - - const postData = { - "drive_id": driver_id, - "query": "type = \"file\"", - "image_thumbnail_process": "image/resize,w_400/format,jpeg", - "image_url_process": "image/resize,w_1920/format,jpeg", - "video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_1000", - "limit": limited, - "marker":marker, - "order_by": "created_at DESC" - } - const resp = await AliHttp.Post(url, postData, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - const items = resp.body.items as AliAlbumFileInfo[] - - if (items) { - items.forEach((item) => { - item.next_marker = resp.body.next_marker - }) - results.push(...items) - return results - } - } + if (items) { + items.forEach((item) => { + item.next_marker = resp.body.next_marker + }) + results.push(...items) return results + } + } + return results + } + + + static async ApiAllPhotos(): Promise { + const driver_id = GetDriveID(useUserStore().user_id, 'pic') + const userId = useUserStore().user_id + const url = 'adrive/v1.0/openFile/search' + let marker = ''; + let max: number = useSettingStore().debugFileListMax + + const results:AliAlbumFileInfo[] = [] + if (!driver_id) { + return results } - - static async ApiAllPhotos(): Promise { - const driver_id = GetDriveID(useUserStore().user_id, 'pic') - const userId = useUserStore().user_id - const url = 'adrive/v1.0/openFile/search' - let marker = ''; - let max: number = useSettingStore().debugFileListMax - - const results:AliAlbumFileInfo[] = [] - if (!driver_id) { - return results - } - - do { - const postData = { - drive_id: driver_id, - query: "type = \"file\"", - image_thumbnail_process: "image/resize,w_400/format,jpeg", - image_url_process: "image/resize,w_1920/format,jpeg", - video_thumbnail_process: "video/snapshot,t_0,f_jpg,ar_auto,w_1000", - marker:marker, - order_by: "created_at DESC" - } - const resp = await AliHttp.Post(url, postData, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - const items = resp.body.items as AliAlbumFileInfo[] - if (items) { - results.push(...items) - marker = resp.body.next_marker - } else { - marker = '' - } - } else { - marker = '' - break - } - if (max > 0 && results.length >= max) { - marker = '' - break - } - } while (marker) - return results - } - - static async ApiAlbumListFiles(album_id: string, marker="", limited=100): Promise { - const userId = useUserStore().user_id - const url = 'adrive/v1/album/list_files' - const postData = { - album_id, - image_thumbnail_process: "image/resize,w_400/format,jpeg", - video_thumbnail_process: "video/snapshot,t_0,f_jpg,ar_auto,w_1000", - image_url_process: "image/resize,w_1920/format,jpeg", - limit: limited, - marker:marker, - order_direction: "DESC" - } - const results:AliAlbumFileInfo[] = [] - const driver_id = GetDriveID(useUserStore().user_id, 'pic') - if (!driver_id) { - return results - } - const resp = await AliHttp.Post(url, postData, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - const items = resp.body.items as AliAlbumFileInfo[] - if (items) { - items.forEach((item) => { - item.next_marker = resp.body.next_marker - }) - results.push(...items) - return results - } - } - return results - } - - static async ApiAlbumCreate(name: string, description: string): Promise<{ album_id: string; error: string }> { - const userId = useUserStore().user_id - const url = 'adrive/v1/album/create' - const resp = await AliHttp.Post(url, {name, description}, userId, '') - if (AliHttp.IsSuccess(resp.code)) { - const item = resp.body as IAliAlubmCreateInfo - return {album_id: item.album_id, error: ''} + do { + const postData = { + drive_id: driver_id, + query: "type = \"file\"", + image_thumbnail_process: "image/resize,w_400/format,jpeg", + image_url_process: "image/resize,w_1920/format,jpeg", + video_thumbnail_process: "video/snapshot,t_0,f_jpg,ar_auto,w_1000", + marker:marker, + order_by: "created_at DESC" + } + const resp = await AliHttp.Post(url, postData, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + const items = resp.body.items as AliAlbumFileInfo[] + if (items) { + results.push(...items) + marker = resp.body.next_marker } else { - DebugLog.mSaveWarning('ApiAlbumCreate err=' + (resp.code || '')) + marker = '' } - return {album_id: '', error: resp.body?.code || '创建相册出错'} - } + } else { + marker = '' + break + } + if (max > 0 && results.length >= max) { + marker = '' + break + } + } while (marker) + return results + } - static async ApiAlbumUpdate(album_id:string, name: string, description: string): Promise { - const userId = useUserStore().user_id - // { "album_id": "cfe400000000478599575b69356c5a4962383669", "description": "ff", "name": "未命名" } - const url = 'adrive/v1/album/update' - return await AliHttp.Post(url, {name, album_id}, userId, '') + static async ApiAlbumListFiles(album_id: string, marker="", limited=100): Promise { + const userId = useUserStore().user_id + const url = 'adrive/v1/album/list_files' + const postData = { + album_id, + image_thumbnail_process: "image/resize,w_400/format,jpeg", + video_thumbnail_process: "video/snapshot,t_0,f_jpg,ar_auto,w_1000", + image_url_process: "image/resize,w_1920/format,jpeg", + limit: limited, + marker:marker, + order_direction: "DESC" } - - static async ApiAlbumFilesDelete(album_id:string, file_list:string[]): Promise { - const userId = useUserStore().user_id - const drive_id = GetDriveID(userId, 'pic') - const data:{drive_id:string, file_id:string}[] = [] - file_list.forEach((file_id) => { - data.push({drive_id, file_id}) + const results:AliAlbumFileInfo[] = [] + const driver_id = GetDriveID(useUserStore().user_id, 'pic') + if (!driver_id) { + return results + } + const resp = await AliHttp.Post(url, postData, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + const items = resp.body.items as AliAlbumFileInfo[] + if (items) { + items.forEach((item) => { + item.next_marker = resp.body.next_marker }) - console.log("ApiAlbumFilesDelete data=", data, album_id) - // { "album_id": "cfe400000000478599575b69356c5a4962383669", "drive_file_list": [{ "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" }] } - const url = 'adrive/v1/album/delete_files' - return await AliHttp.Post(url, {album_id, "drive_file_list": data}, userId, '') + results.push(...items) + return results + } } + return results + } - - static async ApiAlbumDelete(album_id:string): Promise { - const userId = useUserStore().user_id - const url = 'adrive/v1/album/delete' - return await AliHttp.Post(url, {album_id}, userId, '') + static async ApiAlbumCreate_1(name: string, description: string): Promise<{ album_id: string; error: string }> { + const userId = useUserStore().user_id + const url = 'adrive/v1/album/create' + const resp = await AliHttp.Post(url, {name, description}, userId, '') + if (AliHttp.IsSuccess(resp.code)) { + const item = resp.body as IAliAlubmCreateInfo + return {album_id: item.album_id, error: ''} + } else { + DebugLog.mSaveWarning('ApiAlbumCreate err=' + (resp.code || '')) } + return {album_id: '', error: resp.body?.code || '创建相册出错'} + } - static async ApiAlbumAddExistPic(album_id:string, file_list:string[]): Promise { - const userId = useUserStore().user_id - const drive_id = GetDriveID(userId, 'pic') - const data:{drive_id:string, file_id:string}[] = [] - file_list.forEach((file_id) => { - data.push({drive_id, file_id}) - }) - const postData = {album_id, "drive_file_list": data} - // { "album_id": "cfe400000000478599575b69356c5a4962383669", "drive_file_list": [{ "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" }] } - const url = 'adrive/v1/album/add_files' - return await AliHttp.Post(url, postData, userId, '') + static async ApiAlbumUpdat_1(album_id:string, name: string, description: string): Promise { + const userId = useUserStore().user_id + // { "album_id": "cfe400000000478599575b69356c5a4962383669", "description": "ff", "name": "未命名" } + const url = 'adrive/v1/album/update' + return await AliHttp.Post(url, {name, album_id}, userId, '') + } + + static async ApiAlbumFilesDelete(album_id:string, file_list:string[]): Promise { + const userId = useUserStore().user_id + const drive_id = GetDriveID(userId, 'pic') + const data:{drive_id:string, file_id:string}[] = [] + file_list.forEach((file_id) => { + data.push({drive_id, file_id}) + }) + console.log("ApiAlbumFilesDelete data=", data, album_id) + // { "album_id": "cfe400000000478599575b69356c5a4962383669", "drive_file_list": [{ "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" }] } + const url = 'adrive/v1/album/delete_files' + return await AliHttp.Post(url, {album_id, "drive_file_list": data}, userId, '') + } + + + static async ApiAlbumDelete_1(album_id:string): Promise { + const userId = useUserStore().user_id + const url = 'adrive/v1/album/delete' + return await AliHttp.Post(url, {album_id}, userId, '') + } + + static async ApiAlbumAddExistPic(album_id:string, file_list:string[]): Promise { + const userId = useUserStore().user_id + const drive_id = GetDriveID(userId, 'pic') + const data:{drive_id:string, file_id:string}[] = [] + file_list.forEach((file_id) => { + data.push({drive_id, file_id}) + }) + const postData = {album_id, "drive_file_list": data} + // { "album_id": "cfe400000000478599575b69356c5a4962383669", "drive_file_list": [{ "drive_id": "9600002", "file_id": "623b00000000d89ef21d4118838aed83de7575ba" }] } + const url = 'adrive/v1/album/add_files' + return await AliHttp.Post(url, postData, userId, '') + } + + static async trashPhotos(file_ids: string[]): Promise { + const user_id = useUserStore().user_id + const drive_id = GetDriveID(user_id, 'pic') + // @ts-ignore + const requestData = [] + file_ids.forEach((file_id) => { + requestData.push({ + "body": { + "drive_id": drive_id, + "file_id": file_id + }, + "headers": { + "Content-Type": "application/json" + }, + "id": file_id, + "method": "POST", + "url": "/recyclebin/trash" + }) + }) + const url = 'v2/batch' + // @ts-ignore + const postData = { requests: requestData, resource: "file" } + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return true + } else { + DebugLog.mSaveWarning('UploadFileDelete err=' + (resp.code || '')) + return false } + } - static async trashPhotos(file_ids: string[]): Promise { - const user_id = useUserStore().user_id - const drive_id = GetDriveID(user_id, 'pic') - // @ts-ignore - const requestData = [] - file_ids.forEach((file_id) => { - requestData.push({ - "body": { - "drive_id": drive_id, - "file_id": file_id - }, - "headers": { - "Content-Type": "application/json" - }, - "id": file_id, - "method": "POST", - "url": "/recyclebin/trash" - }) - }) - const url = 'v2/batch' - // @ts-ignore - const postData = { requests: requestData, resource: "file" } - const resp = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(resp.code)) { - return true - } else { - DebugLog.mSaveWarning('UploadFileDelete err=' + (resp.code || '')) - return false + /** + * 创建相册 + */ + static async ApiAlbumCreate(user_id: string, album_name: string, album_description: string): Promise { + if (!user_id || !album_name) return '创建相册出错' + const url = 'adrive/v1/album/create' + const postData = { name: album_name, description: album_description } + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + const result = resp.body as IAliAlbumInfo + if (result) return 'success' + else return '创建相册出错' + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumCreate err=' + (resp.code || '')) + } + return '创建相册出错' + } + + /** + * 获取相册路径 + */ + static async ApiAlbumGetPath(user_id: string, drive_id: string, album_id: string): Promise { + if (!user_id || !drive_id || !album_id) return [] + const url = 'adrive/v1/album/get' + const postData = { album_id: album_id } + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + const list: IAliGetDirModel[] = [] + list.push({ + __v_skip: true, + drive_id: drive_id, + file_id: 'mypic', + parent_file_id: '', + name: '我的相册', + namesearch: '', + size: 0, + time: 0, + description: '' + } as IAliGetDirModel) + if (resp.body.name.length > 0) { + list.push({ + __v_skip: true, + drive_id: drive_id, + file_id: resp.body.album_id, + album_id: resp.body.album_id, + parent_file_id: 'mypic', + name: resp.body.name, + namesearch: HanToPin(resp.body.name), + size: 0, + time: new Date(resp.body.updated_at).getTime(), + description: resp.body.description || '' + } as IAliGetDirModel) + } + return list + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumGetPath err=' + album_id + ' ' + (resp.code || ''), resp.body) + } + return [] + } + + /** + * 列出相册 + */ + static async ApiAlbumList(user_id: string, limit?: string, order_by?: string, order_direction?: string) { + if (!user_id) return [] + const url = 'adrive/v1/album/list' + const postData = { + limit: limit || 20, + order_by: order_by || 'updated_at', + order_direction: order_direction || 'DESC' + } + const data: IAliAlbumInfo[] = [] + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + const items = resp.body.items + if (items && items.length > 0) { + for (let item of items) { + let coverUrl = '' + if (item.cover && item.cover.list.length > 0) { + coverUrl = item.cover.list[0].download_url || '' + } + data.push({ ...item, coverUrl }) } + } + return data + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumList err=' + (resp.code || '')) } -} \ No newline at end of file + return [] + } + + /** + * 更新相册 + */ + static async ApiAlbumUpdate(user_id: string, album_id: string, name: string, description: string): Promise { + if (!user_id || !album_id) return undefined + const url = 'adrive/v1/album/update' + const postData = { name, album_id, description } + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body as IUrlRespData + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumUpdate err=' + (resp.code || '')) + } + return undefined + } + + /** + * 删除相册(不会删除文件) + */ + static async ApiAlbumDelete(user_id: string, album_id: string): Promise { + if (!user_id || !album_id) return undefined + const url = 'adrive/v1/album/delete' + const resp = await AliHttp.Post(url, { album_id }, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body as IUrlRespData + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumDelete err=' + (resp.code || '')) + } + return undefined + } + + /** + * 添加文件到相册 + */ + static async ApiAlbumAddFiles(user_id: string, album_id: string, drive_file_list: { + drive_id: string, + file_id: string + }[]): Promise { + if (!user_id || !album_id || !drive_file_list) return undefined + const url = 'adrive/v1/album/add_files' + const resp = await AliHttp.Post(url, { album_id, drive_file_list }, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body as IUrlRespData + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumAddFiles err=' + (resp.code || '')) + } + return undefined + } + + /** + * 文件移出相册(不会删除文件) + */ + static async ApiAlbumDeleteFiles(user_id: string, album_id: string, drive_file_list: { + drive_id: string, + file_id: string + }[]): Promise { + if (!user_id || !album_id || !drive_file_list) return undefined + const url = 'adrive/v1/album/delete_files' + const resp = await AliHttp.Post(url, { album_id, drive_file_list }, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body as IUrlRespData + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAlbumDeleteFiles err=' + (resp.code || '')) + } + return undefined + } +} diff --git a/aliyunpan/src/aliapi/alihttp.ts b/aliyunpan/src/aliapi/alihttp.ts index 15962ea867..15fbfe3063 100644 --- a/aliyunpan/src/aliapi/alihttp.ts +++ b/aliyunpan/src/aliapi/alihttp.ts @@ -19,7 +19,7 @@ function BlobToString(body: Blob, encoding: string): Promise { return new Promise((resolve) => { const reader = new FileReader() reader.readAsText(body, encoding) - reader.onload = function () { + reader.onload = function() { resolve((reader.result as string) || '') } }) @@ -29,7 +29,7 @@ function BlobToBuff(body: Blob): Promise { return new Promise((resolve) => { const reader = new FileReader() reader.readAsArrayBuffer(body) - reader.onload = function () { + reader.onload = function() { resolve(reader.result as ArrayBuffer) } }) @@ -50,8 +50,8 @@ function Sleep(msTime: number): Promise<{ success: true; time: number }> { const IsDebugHttp = false export default class AliHttp { - static baseApi = 'https://api.aliyundrive.com/' - static baseOpenApi = 'https://openapi.aliyundrive.com/' + static baseApi = 'https://api.alipan.com/' + static baseOpenApi = 'https://openapi.alipan.com/' static IsSuccess(code: number): Boolean { return code >= 200 && code <= 300 @@ -61,8 +61,7 @@ export default class AliHttp { if (code >= 200 && code <= 300) return true if (code == 400) return true // if (code == 401) return true - if (code >= 402 && code <= 428) return true - if (code == 404) return true + if (code > 402 && code <= 428) return true if (code == 409) return true return false } @@ -72,15 +71,16 @@ export default class AliHttp { if (IsDebugHttp) console.log('CALLURLError ', error) const errorMessage = error.display_message || error.message || '' if (error.response) { - let { code, status, config, data = undefined, headers = undefined} = error.response + let { code, status, config, data = undefined, headers = undefined } = error.response if (code == 'ERR_NETWORK' || (status == 0 && !headers)) { DebugLog.mSaveWarning('HttpError0 message=' + errorMessage) return { code: 600, header: '', body: 'NetError 网络无法连接' } as IUrlRespData } let isNeedLog = true - if (status == 429) isNeedLog = false + if (status == 429 || status == 504 || status == 500) isNeedLog = false if (data && data.code) { let errCode = [ + 'NotFound.File', 'InvalidParameter.Limit', 'ForbiddenFileInTheRecycleBin', 'PreHashMatched', @@ -94,29 +94,26 @@ export default class AliHttp { 'UserDeviceOffline', 'DeviceSessionSignatureInvalid', 'AccessTokenInvalid', - 'AccessTokenExpired', - 'I400JD', + 'AccessTokenExpired' ] if (errCode.includes(data.code)) isNeedLog = false // 自动刷新Token - if (data.code == 'AccessTokenInvalid' - || data.code == 'AccessTokenExpired' - || data.code == 'I400JD') { + if (data.code == 'AccessTokenInvalid' || data.code == 'AccessTokenExpired') { if (token) { - const isOpenApi = config.url.includes('adrive/v1.0') + const isOpenApi = config.url.includes('adrive/v1.0') || config.url.includes('adrive/v1.1') if (!isOpenApi) { - return await AliUser.ApiRefreshAccessTokenV1(token, true, true).then((isLogin: boolean) => { + return await AliUser.ApiTokenRefreshAccount(token, true, true).then((isLogin: boolean) => { if (isLogin) { return { code: 401, header: '', body: '' } as IUrlRespData } return { code: 403, header: '', body: 'NetError 账号需要重新登录' } as IUrlRespData }) } else { - return await AliUser.ApiRefreshAccessTokenV2(token, true, true).then((flag: boolean) => { + return await AliUser.OpenApiTokenRefreshAccount(token, true, true).then((flag: boolean) => { if (flag) { return { code: 401, header: '', body: '' } as IUrlRespData } - return { code: 403, header: '', body: '刷新OpenApiToken失败,请检查配置' } as IUrlRespData + return { code: 403, header: '', body: 'NetError 账号需要重新登录' } as IUrlRespData }) } } else { @@ -126,10 +123,10 @@ export default class AliHttp { // 自动刷新Session if (data.code == 'UserDeviceIllegality' - || data.code == 'UserDeviceOffline' - || data.code == 'DeviceSessionSignatureInvalid') { + || data.code == 'UserDeviceOffline' + || data.code == 'DeviceSessionSignatureInvalid') { if (token) { - return await AliUser.ApiSessionRefreshAccount(token, true).then((flag: boolean) => { + return await AliUser.ApiSessionRefreshAccount(token, true, true).then((flag: boolean) => { if (flag) { return { code: 401, header: '', body: '' } as IUrlRespData } @@ -176,10 +173,16 @@ export default class AliHttp { } } - static async Get(url: string, user_id: string): Promise { - if (!url.startsWith('http') && !url.startsWith('https')) url = AliHttp.baseApi + url + static async Get(url: string, user_id: string, params?: any): Promise { + if (!url.startsWith('http') && !url.startsWith('https')) { + if (url.includes('adrive/v1.0') || url.includes('adrive/v1.1')) { + url = AliHttp.baseOpenApi + url + } else { + url = AliHttp.baseApi + url + } + } for (let i = 0; i <= 5; i++) { - const resp = await AliHttp._Get(url, user_id) + const resp = await AliHttp._Get(url, user_id, params) if (AliHttp.HttpCodeBreak(resp.code)) return resp else if (i == 5) return resp else await Sleep(2000) @@ -187,17 +190,26 @@ export default class AliHttp { return { code: 607, header: '', body: 'NetError GetLost' } } - static _Get(url: string, user_id: string): Promise { + static _Get(url: string, user_id: string, params?: any): Promise { return UserDAL.GetUserTokenFromDB(user_id).then((token) => { const headers: any = {} if (token) { - headers['Authorization'] = token.token_type + ' ' + token.access_token - headers['x-request-id'] = v4().toString() - headers['x-device-id'] = token.device_id - headers['x-signature'] = token.signature + let token_type = token.token_type + let access_token = token.access_token + let need_open_api = url.includes('openapi') + if (need_open_api && token.open_api_access_token) { + token_type = token.open_api_token_type || 'Bearer' + access_token = token.open_api_access_token + } else { + headers['x-device-id'] = token.device_id + headers['x-signature'] = token.signature + headers['x-request-id'] = v4().toString() + } + headers['Authorization'] = token_type + ' ' + access_token } return axios .get(url, { + params: params, withCredentials: false, responseType: 'json', timeout: 30000, @@ -210,7 +222,7 @@ export default class AliHttp { body: response.data } as IUrlRespData }) - .catch(function (err: any) { + .catch(function(err: any) { return AliHttp.CatchError(err, token) }) }) @@ -218,7 +230,13 @@ export default class AliHttp { static async GetString(url: string, user_id: string, fileSize: number, maxSize: number): Promise { - if (!url.startsWith('http') && !url.startsWith('https')) url = AliHttp.baseApi + url + if (!url.startsWith('http') && !url.startsWith('https')) { + if (url.includes('adrive/v1.0') || url.includes('adrive/v1.1')) { + url = AliHttp.baseOpenApi + url + } else { + url = AliHttp.baseApi + url + } + } for (let i = 0; i <= 5; i++) { const resp = await AliHttp._GetString(url, user_id, fileSize, maxSize) if (AliHttp.HttpCodeBreak(resp.code)) return resp @@ -232,14 +250,21 @@ export default class AliHttp { return UserDAL.GetUserTokenFromDB(user_id).then((token) => { const headers: any = {} if (token) { - headers['Authorization'] = token.token_type + ' ' + token.access_token - headers['x-request-id'] = v4().toString() - headers['x-device-id'] = token.device_id - headers['x-signature'] = token.signature - } - if (maxSize > 0) { - headers.Range = 'bytes=0-' + (Math.min(fileSize, maxSize) - 1).toString() + let token_type = token.token_type + let access_token = token.access_token + let need_open_api = url.includes('openapi') + if (need_open_api && token.open_api_access_token) { + token_type = token.open_api_token_type || 'Bearer' + access_token = token.open_api_access_token + } else { + headers['x-device-id'] = token.device_id + headers['x-signature'] = token.signature + headers['x-request-id'] = v4().toString() + } + headers['Authorization'] = token_type + ' ' + access_token } + headers.Range = 'bytes=0-' + (Math.min(fileSize, maxSize) - 1).toString() + return axios .get(url, { withCredentials: false, @@ -293,12 +318,13 @@ export default class AliHttp { try { resp.body = JSON.stringify(JSON.parse(resp.body), undefined, 2) - } catch {} + } catch { + } } } return resp }) - .catch(function (err: any) { + .catch(function(err: any) { return AliHttp.CatchError(err, token) }) }) @@ -306,7 +332,13 @@ export default class AliHttp { static async GetBlob(url: string, user_id: string): Promise { - if (!url.startsWith('http') && !url.startsWith('https')) url = AliHttp.baseApi + url + if (!url.startsWith('http') && !url.startsWith('https')) { + if (url.includes('adrive/v1.0') || url.includes('adrive/v1.1')) { + url = AliHttp.baseOpenApi + url + } else { + url = AliHttp.baseApi + url + } + } for (let i = 0; i <= 5; i++) { const resp = await AliHttp._GetBlob(url, user_id) if (AliHttp.HttpCodeBreak(resp.code)) return resp @@ -320,10 +352,18 @@ export default class AliHttp { return UserDAL.GetUserTokenFromDB(user_id).then((token) => { const headers: any = {} if (token) { - headers['Authorization'] = token.token_type + ' ' + token.access_token - headers['x-request-id'] = v4().toString() - headers['x-device-id'] = token.device_id - headers['x-signature'] = token.signature + let token_type = token.token_type + let access_token = token.access_token + let need_open_api = url.includes('openapi') + if (need_open_api && token.open_api_access_token) { + token_type = token.open_api_token_type || 'Bearer' + access_token = token.open_api_access_token + } else { + headers['x-device-id'] = token.device_id + headers['x-signature'] = token.signature + headers['x-request-id'] = v4().toString() + } + headers['Authorization'] = token_type + ' ' + access_token } return axios .get(url, { @@ -339,7 +379,7 @@ export default class AliHttp { body: response.data } as IUrlRespData }) - .catch(function (err: any) { + .catch(function(err: any) { return AliHttp.CatchError(err, token) }) }) @@ -347,26 +387,65 @@ export default class AliHttp { static async Post(url: string, postData: any, user_id: string, share_token: string): Promise { if (!url.startsWith('http') && !url.startsWith('https')) { - url = (url.includes('adrive/v1.0') ? AliHttp.baseOpenApi : AliHttp.baseApi) + url + if (url.includes('adrive/v1.0') || url.includes('adrive/v1.1')) { + url = AliHttp.baseOpenApi + url + } else { + url = AliHttp.baseApi + url + } } for (let i = 0; i <= 5; i++) { const resp = await AliHttp._Post(url, postData, user_id, share_token) + if (resp.code == 429 + && resp.body.display_message + && !url.includes('getDownloadUrl') + && !url.includes('get_download_url')) { + return resp + } if (resp.code == 400 && - (url.includes('/file/search') + (url.includes('/file/search') || url.includes('/file/list') || url.includes('/file/walk') - || url.includes('/file/scan')) - && !resp.body?.code) await Sleep(2000) - else if (AliHttp.HttpCodeBreak(resp.code)) return resp + || url.includes('/file/scan'))) { + await Sleep(2000) + } else if (AliHttp.HttpCodeBreak(resp.code)) return resp else if (i == 5) return resp else await Sleep(2000) } return { code: 608, header: '', body: 'NetError PostLost' } as IUrlRespData } - static async GetWithOutUserId(url: string): Promise { - return axios - .get(url) + private static _Post(url: string, postData: any, user_id: string, share_token: string): Promise { + return UserDAL.GetUserTokenFromDB(user_id).then((token) => { + const headers: any = {} + if (url.includes('aliyundrive') || url.includes('alipan')) { + headers['Content-Type'] = 'application/json' + } + if (token) { + let token_type = token.token_type + let access_token = token.access_token + let need_open_api = url.includes('openapi') + if (need_open_api && token.open_api_access_token) { + token_type = token.open_api_token_type || 'Bearer' + access_token = token.open_api_access_token + } else { + headers['x-device-id'] = token.device_id + headers['x-signature'] = token.signature + headers['x-request-id'] = v4().toString() + } + headers['Authorization'] = token_type + ' ' + access_token + } + if (share_token) { + headers['x-share-token'] = share_token + } + let timeout = 30000 + if (url.includes('/batch')) timeout = 60000 + return axios + .post(url, postData, { + withCredentials: false, + responseType: 'json', + timeout, + headers + }) .then((response: AxiosResponse) => { return { code: response.status, @@ -374,9 +453,83 @@ export default class AliHttp { body: response.data } as IUrlRespData }) - .catch(function (err: any) { - return AliHttp.CatchError(err, undefined) + .catch(function(err: any) { + return AliHttp.CatchError(err, token) }) + }) + } + + static async PostString(url: string, postData: any, user_id: string, share_token: string): Promise { + if (!url.startsWith('http') && !url.startsWith('https')) { + if (url.includes('adrive/v1.0') || url.includes('adrive/v1.1')) { + url = AliHttp.baseOpenApi + url + } else { + url = AliHttp.baseApi + url + } + } + for (let i = 0; i <= 5; i++) { + const resp = await AliHttp._PostString(url, postData, user_id, share_token) + if (AliHttp.HttpCodeBreak(resp.code)) return resp + else if (i == 5) return resp + else await Sleep(2000) + } + return { code: 610, header: '', body: 'NetError PostStringLost' } as IUrlRespData + } + + private static _PostString(url: string, postData: any, user_id: string, share_token: string): Promise { + const headers: any = {} + return UserDAL.GetUserTokenFromDB(user_id).then((token) => { + if (token) { + let token_type = token.token_type + let access_token = token.access_token + let need_open_api = url.includes('openapi') + if (need_open_api && token.open_api_access_token) { + token_type = token.open_api_token_type || 'Bearer' + access_token = token.open_api_access_token + } else { + headers['x-device-id'] = token.device_id + headers['x-signature'] = token.signature + headers['x-request-id'] = v4().toString() + } + headers['Authorization'] = token_type + ' ' + access_token + } + if (share_token) { + headers['x-share-token'] = share_token + } + + return axios + .post(url, postData, { + withCredentials: false, + responseType: 'text', + timeout: 50000, + headers + }) + .then((response: AxiosResponse) => { + return { + code: response.status, + header: JSON.stringify(response.headers), + body: response.data + } as IUrlRespData + }) + .catch(function(err: any) { + return AliHttp.CatchError(err, token) + }) + }) + } + + static async GetWithOutUserId(url: string): Promise { + return axios + .get(url) + .then((response: AxiosResponse) => { + return { + code: response.status, + header: JSON.stringify(response.headers), + body: response.data + } as IUrlRespData + }) + .catch(function (err: any) { + return AliHttp.CatchError(err, undefined) + }) } static async PostWithOutUserId(url: string, postData: any): Promise { @@ -400,91 +553,4 @@ export default class AliHttp { return AliHttp.CatchError(err, undefined) }) } - - static _Post(url: string, postData: any, user_id: string, share_token: string): Promise { - return UserDAL.GetUserTokenFromDB(user_id).then((token) => { - const headers: any = {} - if (url.includes('aliyundrive')) { - headers['Content-Type'] = 'application/json' - } - if (token && url.startsWith(this.baseOpenApi)) { - headers['Authorization'] = token.token_type + ' ' + token.access_token_v2 - headers['x-request-id'] = v4().toString() - headers['x-device-id'] = token.device_id - headers['x-signature'] = token.signature - } else if (token) { - headers['Authorization'] = token.token_type + ' ' + token.access_token - headers['x-request-id'] = v4().toString() - headers['x-device-id'] = token.device_id - headers['x-signature'] = token.signature - } - if (share_token) { - headers['x-share-token'] = share_token - } - if (url.includes('ali')) headers['content-type'] = 'application/json;charset-utf-8' - let timeout = 30000 - if (url.includes('/batch')) timeout = 60000 - return axios - .post(url, postData, { - withCredentials: false, - responseType: 'json', - timeout, - headers - }) - .then((response: AxiosResponse) => { - return { - code: response.status, - header: JSON.stringify(response.headers), - body: response.data - } as IUrlRespData - }) - .catch(function (err: any) { - return AliHttp.CatchError(err, token) - }) - }) - } - - static async PostString(url: string, postData: any, user_id: string, share_token: string): Promise { - if (!url.startsWith('http') && !url.startsWith('https')) url = AliHttp.baseApi + url - for (let i = 0; i <= 5; i++) { - const resp = await AliHttp._PostString(url, postData, user_id, share_token) - if (AliHttp.HttpCodeBreak(resp.code)) return resp - else if (i == 5) return resp - else await Sleep(2000) - } - return { code: 610, header: '', body: 'NetError PostStringLost' } as IUrlRespData - } - - private static _PostString(url: string, postData: any, user_id: string, share_token: string): Promise { - const headers: any = {} - return UserDAL.GetUserTokenFromDB(user_id).then((token) => { - if (token) { - headers['Authorization'] = token.token_type + ' ' + token.access_token - headers['x-request-id'] = v4().toString() - headers['x-device-id'] = token.device_id - headers['x-signature'] = token.signature - } - if (share_token) { - headers['x-share-token'] = share_token - } - - return axios - .post(url, postData, { - withCredentials: false, - responseType: 'text', - timeout: 50000, - headers - }) - .then((response: AxiosResponse) => { - return { - code: response.status, - header: JSON.stringify(response.headers), - body: response.data - } as IUrlRespData - }) - .catch(function (err: any) { - return AliHttp.CatchError(err, token) - }) - }) - } } diff --git a/aliyunpan/src/aliapi/alimodels.ts b/aliyunpan/src/aliapi/alimodels.ts index ea9d4fcb62..bb58ef42cd 100644 --- a/aliyunpan/src/aliapi/alimodels.ts +++ b/aliyunpan/src/aliapi/alimodels.ts @@ -5,6 +5,7 @@ export interface IAliFileVideoMeta { duration?: string fps?: string } + export interface IAliFileAudioMeta { bit_rate?: string channel_layout?: string @@ -14,6 +15,372 @@ export interface IAliFileAudioMeta { sample_rate?: string } +export interface IAliAlbumInfo { + owner: string + name: string + description: string + coverUrl: string + album_id: string + file_count: number + image_count: number + video_count: number + created_at: number + updated_at: number, +} + +export interface IAliFileItem { + drive_id: string + domain_id: string + description?: string + file_id: string + album_id?: string + compilation_id?: string + name: string + type: string + video_type?: string + content_type: string + created_at: string + updated_at: string + last_played_at?: string + gmt_cleaned?: string + gmt_deleted?: string + file_extension?: string + hidden: boolean + file_count?: number + image_count?: number + video_count?: number + size: number + starred: boolean + status: string + upload_id: string + parent_file_id: string + crc64_hash: string + content_hash: string + content_hash_name: string + download_url: string + url: string + category: string + encrypt_mode: string + punish_flag: number + from_share_id?: string + thumbnail?: string + mime_extension: string + mime_type: string + play_cursor: string + duration: string + video_media_metadata?: { + duration?: string | number + height?: number + width?: number + time?: string + video_media_video_stream?: IAliFileVideoMeta[] | IAliFileVideoMeta + video_media_audio_stream?: IAliFileAudioMeta[] | IAliFileAudioMeta + } + + video_preview_metadata?: { + duration?: string | number + height?: number + width?: number + time?: string + audio_format?: string + bitrate?: string + frame_rate?: string + video_format?: string + template_list?: [{ template_id: string; status: string }] + audio_template_list?: [{ template_id: string; status: string }] + } + + image_media_metadata?: { + height?: number + width?: number + time?: string + exif?: string + } + + user_meta?: string +} + + +export interface IAliOtherFollowingModel { + avatar: string + description: string + is_following: boolean + nick_name: string + phone: string + user_id: string + follower_count: number +} + +interface IAliMyFollowingMessageModel { + action: string + content: { + file_id_list: string[] + share: { popularity: number; popularity_emoji: string; popularity_str: string; share_id: string; share_pwd: string } + } + created: number + createdstr: string + creator: IAliOtherFollowingModel + creator_id: string + display_action: string + sequence_id: number +} + + +export interface IAliMyFollowingModel { + avatar: string + description: string + has_unread_message: boolean + is_following: boolean + latest_messages: IAliMyFollowingMessageModel[] + nick_name: string + phone: string + user_id: string + SearchName: string +} + +export interface IAliShareBottleFish { + bottleId: string; + bottleName: string; + shareId: string; +} + +export interface IAliShareItem { + created_at: string + creator: string + description: string + display_name: string + display_label: string + download_count: number + drive_id: string + expiration: string + expired: boolean + file_id: string + file_id_list: string[] + icon: string + first_file?: IAliFileItem + preview_count: number + save_count: number + share_id: string + + share_msg: string + full_share_msg: string + share_name: string + share_policy: string + share_pwd: string + share_url: string + status: string + updated_at: string + + is_share_saved: boolean + share_saved: string +} + +export interface IAliShareRecentItem { + popularity: number; + browse_count: number; + share_id: string; + share_msg: string; + share_name: string; + share_url: string; + creator: string; + file_id_list: string[]; + preview_count: number; + save_count: number; + status: string; + share_subtitle: string; + gmt_created: string; + gmt_modified: string; + is_public: boolean; + file_list: { + name: string; + type: string; + category: string; + parent_file_id: string; + drive_id: string; + file_id: string; + created_at: string; + updated_at: string; + trashed_at: string | null; + }[]; + creator_name: string; + creator_uid: string; + file_count: number; + is_punished: boolean; + share_creator: { + userId: string; + avatar: string; + displayName: string; + }; + popularity_str: string; + popularity_emoji: string; + full_share_msg: string; + share_title: string; + display_name: string; +} + +export interface IAliShareBottleFishItem { + bottleId: string; + gmtCreate: number; + id: number; + name: string; + saved: boolean; + shareId: string; + gmt_created: string; + saved_msg: string; + share_name: string; + display_name: string; +} + +export interface IAliShareAnonymous { + shareinfo: { + share_id: string + creator_id: string + creator_name: string + creator_phone: string + display_name: string + expiration: string + file_count: number + share_name: string + created_at: string + updated_at: string + vip: string + is_photo_collection: boolean + album_id: string + } + shareinfojson: string + error: string +} + + +export interface IAliShareFileItem { + drive_id: string + // domain_id: string + file_id: string + name: string + type: string + created_at: string + updated_at: string + // hidden: boolean + // starred: boolean + // status: string + parent_file_id: string + // encrypt_mode: string + // revision_id: string + + file_extension?: string + mime_extension: string + mime_type: string + size: number + // content_hash: string + // content_hash_name: string + category: string + punish_flag: number + + + isDir: boolean + sizeStr: string + timeStr: string + icon: string +} + + +export interface IAliGetForderSizeModel { + size: number + folder_count: number + file_count: number + reach_limit?: boolean +} + + +export interface IAliGetDirModel { + __v_skip: true + drive_id: string + file_id: string + album_id?: string + album_type?: string + parent_file_id: string + name: string + namesearch: string + size: number + time: number + punish_flag?: number + description: string +} + + +export interface IAliGetFileModel { + __v_skip: true + drive_id: string + file_id: string + parent_file_id: string + name: string + namesearch: string + ext: string + mime_type: string + mime_extension: string + category: string + icon: string + file_count?: number + size: number + sizeStr: string + time: number + timeStr: string + starred: boolean + isDir: boolean + thumbnail: string + punish_flag?: number + from_share_id?: string + description: string + album_id?: string + compilation_id?: string + download_url?: string + media_width?: number + media_height?: number + media_duration?: string + media_play_cursor?: string + media_time?: string + user_meta?: string + duration?: number + play_cursor?: number + season_poster?:string + episode_name?:string + episode_poster?:string + season_num?:number + episode_num?:number + // tvSeason?:{ + // "air_date": string, + // "episode_count": number, + // "id": number, + // "name": string, + // "overview": string, + // "poster_path": string, + // "season_number": number, + // "vote_average": number + // }[] + minfo?: { + cached?: boolean + adult: boolean + backdrop_path: string + id: number + title: string + name: string + original_language: string + original_title: string + origin_country?: string[] + overview: string + poster_path: string + media_type: string + genre_ids: number[] + popularity: string + release_date?: string + video?: boolean + first_air_date?: string + vote_average: number + vote_count: number + } + m3u8_total_file_nums?:number + m3u8_parent_file_name?:string +} export interface AliAlbumFileInfo { album_name?:string @@ -63,6 +430,7 @@ export interface AliAlbumFileInfo { "next_marker"?: string } + // [{"name": "cutecy", "friendly_name": "\u53ef\u53ef\u7231\u7231\n", "preview": ".DS_Store"}] export interface IAliAlbumsList { name:string @@ -97,256 +465,3 @@ export interface IAliAlubmCreateInfo { "created_at": number, "updated_at": number, } - -export interface IAliFileItem { - trashed?: boolean - drive_id: string - domain_id: string - description?: string - file_id: string - compilation_id?: string - name: string - type: string - video_type?: string - content_type: string - created_at: string - updated_at: string - last_played_at?: string - gmt_cleaned?: string - gmt_deleted?: string - file_extension?: string - hidden: boolean - user_tags?: { - video_watch_progress?: string | number - } - labels?:[string] - size: number - starred: boolean - status: string - upload_id: string - parent_file_id: string - crc64_hash: string - content_hash: string - content_hash_name: string - download_url: string - url: string - category: string - encrypt_mode: string - punish_flag: number - thumbnail?: string - mime_extension: string - mime_type: string - play_cursor?: number - video_media_metadata?: { - duration?: string | number - height?: number - width?: number - time?: string - video_media_video_stream?: IAliFileVideoMeta[] | IAliFileVideoMeta - video_media_audio_stream?: IAliFileAudioMeta[] | IAliFileAudioMeta - } - - video_preview_metadata?: { - duration?: string | number - height?: number - width?: number - time?: string - audio_format?: string - bitrate?: string - frame_rate?: string - video_format?: string - template_list?: [{ template_id: string; status: string }] - audio_template_list?: [{ template_id: string; status: string }] - } - - image_media_metadata?: { - height?: number - width?: number - time?: string - exif?: string - } - cover_type?: string - cover_url?: string - grand_parent_file_id?: string - user_meta?: string - meta?: string - location?: string - deleted?: boolean - channel?: string - revision_id?: string - local_created_at?: string - local_modified_at?: string - trashed_at?: string -} - - -export interface IAliOtherFollowingModel { - avatar: string - description: string - is_following: boolean - nick_name: string - phone: string - user_id: string - follower_count: number -} - -interface IAliMyFollowingMessageModel { - action: string - content: { - file_id_list: string[] - share: { popularity: number; popularity_emoji: string; popularity_str: string; share_id: string; share_pwd: string } - } - created: number - createdstr: string - creator: IAliOtherFollowingModel - creator_id: string - display_action: string - sequence_id: number -} - - -export interface IAliMyFollowingModel { - avatar: string - description: string - has_unread_message: boolean - is_following: boolean - latest_messages: IAliMyFollowingMessageModel[] - nick_name: string - phone: string - user_id: string - SearchName: string -} - - -export interface IAliShareItem { - created_at: string - creator: string - description: string - display_name: string - display_label: string - download_count: number - drive_id: string - expiration: string - expired: boolean - file_id: string - file_id_list: string[] - icon: string - first_file?: IAliFileItem - preview_count: number - save_count: number - share_id: string - - share_msg: string - full_share_msg: string - share_name: string - share_policy: string - share_pwd: string - share_url: string - status: string - updated_at: string - - is_share_saved: boolean - share_saved: string -} - -export interface IAliShareAnonymous { - shareinfo: { - share_id: string - creator_id: string - creator_name: string - creator_phone: string - display_name: string - expiration: string - file_count: number - share_name: string - created_at: string - updated_at: string - vip: string - is_photo_collection: boolean - album_id: string - } - shareinfojson: string - error: string -} - - -export interface IAliShareFileItem { - drive_id: string - // domain_id: string - file_id: string - name: string - type: string - // created_at: string - // updated_at: string - // hidden: boolean - // starred: boolean - // status: string - parent_file_id: string - // encrypt_mode: string - // revision_id: string - - file_extension?: string - mime_extension: string - mime_type: string - size: number - // content_hash: string - // content_hash_name: string - category: string - punish_flag: number - - - isDir: boolean - sizeStr: string - icon: string -} - - -export interface IAliGetForderSizeModel { - size: number - folder_count: number - file_count: number - reach_limit?: boolean -} - - -export interface IAliGetDirModel { - __v_skip: true - drive_id: string - file_id: string - parent_file_id: string - name: string - namesearch: string - size: number - time: number - - description: string -} - - -export interface IAliGetFileModel { - __v_skip: true - drive_id: string - file_id: string - parent_file_id: string - name: string - namesearch: string - ext: string - category: string - icon: string - size: number - sizeStr: string - time: number - timeStr: string - starred: boolean - isDir: boolean - thumbnail: string - description: string - compilation_id?: string - download_url?: string - media_width?: number - media_height?: number - media_duration?: string - media_time?: string - m3u8_total_file_nums?:number - m3u8_parent_file_name?:string -} diff --git a/aliyunpan/src/aliapi/archive.ts b/aliyunpan/src/aliapi/archive.ts index c5cea7d890..21835a3bc0 100644 --- a/aliyunpan/src/aliapi/archive.ts +++ b/aliyunpan/src/aliapi/archive.ts @@ -139,6 +139,7 @@ export default class AliArchive { static async ApiArchiveUncompress(user_id: string, drive_id: string, file_id: string, domain_id: string, archive_type: string, target_drive_id: string, target_file_id: string, password: string, file_list: string[]): Promise { if (!user_id || !drive_id || !file_id || !target_drive_id || !target_file_id) return undefined + if (target_file_id.includes('root')) target_file_id = 'root' const url = 'v2/archive/uncompress' const postData: { drive_id: string diff --git a/aliyunpan/src/aliapi/dirfilelist.ts b/aliyunpan/src/aliapi/dirfilelist.ts index aa7a839be8..00dbd78525 100644 --- a/aliyunpan/src/aliapi/dirfilelist.ts +++ b/aliyunpan/src/aliapi/dirfilelist.ts @@ -1,4 +1,4 @@ -import { usePanFileStore, useResPanFileStore, useSettingStore } from '../store' +import { usePanFileStore, useServerStore, useSettingStore } from '../store' import TreeStore from '../store/treestore' import DebugLog from '../utils/debuglog' import { OrderDir, OrderFile } from '../utils/filenameorder' @@ -8,19 +8,24 @@ import { HanToPin, MapValueToArray } from '../utils/utils' import AliHttp, { IUrlRespData } from './alihttp' import { IAliFileItem, IAliGetFileModel } from './alimodels' import getFileIcon from './fileicon' +import { DecodeEncName, GetDriveID } from './utils' +import DB from '../utils/db' +import Config from '../config' + export interface IAliFileResp { items: IAliGetFileModel[] itemsKey: Set punished_file_count: number - + next_marker: string - m_user_id: string - m_drive_id: string - dirID: string - dirName: string - itemsTotal?: number + m_user_id: string + m_drive_id: string + dirID: string + albumID?: string + dirName: string + itemsTotal?: number } export function NewIAliFileResp(user_id: string, drive_id: string, dirID: string, dirName: string): IAliFileResp { @@ -39,94 +44,96 @@ export function NewIAliFileResp(user_id: string, drive_id: string, dirID: string } export default class AliDirFileList { - - static LimitMax = 100 - static ItemJsonmask = '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' - - static getFileInfo(item: IAliFileItem, downUrl: string): IAliGetFileModel { + static ItemJsonmask = 'category%2Ccreated_at%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription%2Cfrom_share_id' + + static getFileInfo(user_id: string, item: IAliFileItem, downUrl: string): IAliGetFileModel { const size = item.size ? item.size : 0 - const date = new Date(item.updated_at || item.gmt_deleted || item.last_played_at || '') - const y = date.getFullYear().toString() - let m: number | string = date.getMonth() + 1 - m = m < 10 ? '0' + m.toString() : m.toString() - let d: number | string = date.getDate() - d = d < 10 ? '0' + d.toString() : d.toString() - let h: number | string = date.getHours() - h = h < 10 ? '0' + h.toString() : h.toString() - let minute: number | string = date.getMinutes() - minute = minute < 10 ? '0' + minute.toString() : minute.toString() - let second: number | string = date.getSeconds() - second = second < 10 ? '0' + second.toString() : second.toString() - + const file_count = item.file_count || item.image_count || item.video_count || 0 + const time = new Date(item.updated_at || item.created_at || item.gmt_deleted || item.last_played_at || '') + const timeStr = humanDateTimeDateStr(item.updated_at || item.created_at || item.gmt_deleted || item.last_played_at || '') const isDir = item.type == 'folder' - + const { name, mine_type, ext } = DecodeEncName(user_id, item) const add: IAliGetFileModel = { __v_skip: true, drive_id: item.drive_id, file_id: item.file_id, parent_file_id: item.parent_file_id || '', - name: item.name, - namesearch: HanToPin(item.name), - ext: item.file_extension?.toLowerCase() || '', + name: name, + namesearch: HanToPin(name), + ext: ext || '', + mime_type: mine_type || '', + mime_extension: item.mime_extension, category: item.category || '', starred: item.starred || false, - time: date.getTime() , + time: time.getTime(), + file_count: file_count, size: size, sizeStr: humanSize(size), - timeStr: y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second , + timeStr: timeStr, icon: 'iconfile-folder', isDir: isDir, thumbnail: '', - description: item.description || '' + from_share_id: item.from_share_id, + punish_flag: item.punish_flag, + description: item.description || '', + user_meta: item.user_meta || '' } if (!isDir) { - const icon = getFileIcon(add.category, add.ext, item.mime_extension, item.mime_type, add.size) + const icon = getFileIcon(add.category, add.ext, add.ext || item.mime_extension, add.mime_type, add.size) add.category = icon[0] add.icon = icon[1] + if (downUrl) { if (downUrl == 'download_url') { add.download_url = item.download_url || '' } else if (add.category == 'image') { - add.thumbnail = item?.thumbnail || '' - // add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + '&image_thumbnail_process=image%2Fresize%2Cl_260%2Fformat%2Cjpg%2Fauto-orient%2C1' + add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + '&image_thumbnail_process=image%2Fresize%2Cl_260%2Fformat%2Cjpg%2Fauto-orient%2C1' } else if (add.category == 'image2') { - add.thumbnail = item?.thumbnail || '' - // add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id } else if (add.category.startsWith('video')) { - add.thumbnail = item?.thumbnail || '' - // add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + '&video_thumbnail_process=video%2Fsnapshot%2Ct_106000%2Cf_jpg%2Car_auto%2Cw_260%2Cm_fast' + add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + '&video_thumbnail_process=video%2Fsnapshot%2Ct_106000%2Cf_jpg%2Car_auto%2Cm_fast' } else if (add.category == 'doc' || add.category == 'doc2') { - add.thumbnail = item?.thumbnail || '' - // if (add.ext != 'txt' && add.ext != 'epub' && add.ext != 'azw' && add.ext != 'azw3') { - // add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + '&office_thumbnail_process=image%2Fresize%2Cl_260%2Fformat%2Cjpg%2Fauto-orient%2C1' - // } + if (add.ext != 'txt' && add.ext != 'epub' && add.ext != 'azw' && add.ext != 'azw3') { + add.thumbnail = downUrl + '&drive_id=' + add.drive_id + '&file_id=' + add.file_id + '&office_thumbnail_process=image%2Fresize%2Cl_260%2Fformat%2Cjpg%2Fauto-orient%2C1' + } } } - if (item.video_media_metadata) { + if (item.video_media_metadata && Object.keys(item.video_media_metadata).length > 0) { add.media_width = item.video_media_metadata.width || 0 add.media_height = item.video_media_metadata.height || 0 add.media_time = humanDateTimeDateStr(item.video_media_metadata.time) add.media_duration = humanTime(item.video_media_metadata.duration) - } else if (item.video_preview_metadata) { + } else if (item.video_preview_metadata && Object.keys(item.video_preview_metadata).length > 0) { add.media_width = item.video_preview_metadata.width || 0 add.media_height = item.video_preview_metadata.height || 0 - add.media_time = humanDateTimeDateStr(item.video_preview_metadata.time) add.media_duration = humanTime(item.video_preview_metadata.duration) - } else if (item.image_media_metadata) { + } else if (item.image_media_metadata && Object.keys(item.image_media_metadata).length > 0) { add.media_width = item.image_media_metadata.width || 0 add.media_height = item.image_media_metadata.height || 0 add.media_time = humanDateTimeDateStr(item.image_media_metadata.time) } + if (item.play_cursor) { + add.media_play_cursor = humanTime(item.play_cursor) + } else if (item.user_meta) { + const meta = JSON.parse(item.user_meta) + if (meta.play_cursor) { + add.media_play_cursor = humanTime(meta.play_cursor) + } + } + if (!add.media_duration && item.duration) { + add.media_duration = humanTime(item.duration) + } } + // 完全违规和部分违规 if (item.punish_flag == 2) add.icon = 'iconweifa' - else if (item.punish_flag > 0) add.icon = 'iconweixiang' - + else if (item.punish_flag == 103) add.icon = 'iconpartweifa' + else if (item.punish_flag > 0) add.icon = 'iconweixiang' return add } - - static async ApiDirFileList(user_id: string, drive_id: string, dirID: string, dirName: string, order: string, type: string = '', openApi = false): Promise { + + static async ApiDirFileList(user_id: string, drive_id: string, dirID: string, dirName: string, order: string, type: string = '', albumID?: string, refresh: boolean = true, openApi = false, search = true): Promise { const dir: IAliFileResp = { items: [], itemsKey: new Set(), @@ -135,23 +142,24 @@ export default class AliDirFileList { m_user_id: user_id, m_drive_id: drive_id, dirID: dirID, - dirName: dirName + dirName: dirName, + albumID: albumID } if (!user_id || !drive_id || !dirID) return dir - if (!order) order = 'updated_at asc' + if (dirID.includes('video')) order = 'updated_at desc' order = order.replace(' desc', ' DESC').replace(' asc', ' ASC') const orders = order.split(' ') let pageIndex = 0 - // if (dirID == 'video') { - // await AliDirFileList._ApiVideoListRecent(orders[0], orders[1], dir, pageIndex) - // pageIndex++ - // } let max: number = useSettingStore().debugFileListMax - if (dirID == 'favorite' || dirID.startsWith('color') || dirID.startsWith('search') || dirID.startsWith('video')) max = useSettingStore().debugFavorListMax + if (dirID == 'favorite' || dirID.startsWith('color') + || dirID.startsWith('search') || dirID == 'trash' + || dirID.startsWith('video') || dirID == 'recover') { + max = useSettingStore().debugFavorListMax + } let needTotal do { @@ -165,8 +173,7 @@ export default class AliDirFileList { isGet = await AliDirFileList._ApiFavorFileListOnePage(orders[0], orders[1], dir, pageIndex) } else if (dirID == 'trash') { isGet = await AliDirFileList._ApiTrashFileListOnePage(orders[0], orders[1], dir, pageIndex) - } - else if (dirID == 'recover') { + } else if (dirID == 'recover') { isGet = await AliDirFileList._ApiDeleteedFileListOnePage(orders[0], orders[1], dir, pageIndex) } else if (dirID.startsWith('color')) { if (!needTotal) { @@ -182,60 +189,249 @@ export default class AliDirFileList { }) } isGet = await AliDirFileList._ApiSearchFileListOnePage(orders[0], orders[1], dir, pageIndex) - } else if (dirID === 'video') { - isGet = await AliDirFileList._ApiVideoListOnePage(orders[0], orders[1], dir, pageIndex) + } else if (dirID == 'video') { + isGet = await AliDirFileList._ApiVideoListRecent(orders[0], orders[1], dir, pageIndex) } else if (dirID === 'video.recentplay') { isGet = await AliDirFileList._ApiVideoListRecent(orders[0], orders[1], dir, pageIndex) } else if (dirID === 'video.compilation') { isGet = await AliDirFileList._ApiVideoListOnePage(orders[0], orders[1], dir, pageIndex) - } else if (dirID.startsWith('video')) { - isGet = await AliDirFileList._ApiVideoFileListOnePage(orders[0], orders[1], dir, pageIndex) + } else if (albumID && albumID.length > 0) { + isGet = await AliDirFileList._ApiAlbumListFilesOnePage(orders[0], orders[1], dir, pageIndex) + dir.itemsTotal = dir.items.length + } else if (dirID === 'mypic') { + isGet = await AliDirFileList._ApiAlbumListOnePage(orders[0], orders[1], dir, pageIndex) } else { if (!needTotal) { - needTotal = AliDirFileList._ApiDirFileListCount(dir, type).then((total) => { + needTotal = AliDirFileList._ApiDirFileListCount(dir, dirID, type).then((total) => { dir.itemsTotal = total }) } - if (openApi) { - isGet = await AliDirFileList._ApiDirFileListOnePageOpenApi(orders[0], orders[1], dir, type, pageIndex) + if (openApi || useSettingStore().uiFileListMode === 'movie') { + isGet = await AliDirFileList._ApiDirFileListOnePageOpenApi(orders[0], orders[1], dir, type, pageIndex, search) } else { isGet = await AliDirFileList._ApiDirFileListOnePage(orders[0], orders[1], dir, type, pageIndex) } - } if (!isGet) { if (needTotal) dir.itemsTotal = -1 - break + break } if (dir.next_marker == 'cancel') { if (needTotal) dir.itemsTotal = -1 - break + break } if (dir.items.length >= max && max > 0) { - dir.next_marker = '' + dir.next_marker = '' break } pageIndex++ } while (dir.next_marker) + for (const fileItem of dir.items) { + const videoInfo = await DB.getVideoInfo(fileItem.file_id) + if (videoInfo) { + fileItem.duration = videoInfo.duration + fileItem.play_cursor = videoInfo.play_cursor + } + } + if (needTotal) await needTotal + if (useSettingStore().uiFileListMode === 'movie' && search) { + await this.searchTMDB(dir) + } + return dir } + static seasonNumber(name: string): string { + const match = name.match(/(?:[sS](\d+)[ -]?[eE](\d+))|(?:[sS](\d+))|(?:[sS][eE](\d+))/) + if (match) { + return match[1] || match[3] || match[4] || "1"; + } else { + const additionalMatch = name.match(/\s*(\d+)\s*第\s*\d+\s*集/); + if (additionalMatch) { + return additionalMatch[1] || "1"; + } + } + return "1"; + } + + static episodeNumber(name: string) :string { + let match = name.match(/(?:[sS](\d+)[ -]?[eE](\d+))|(?:[sS](\d+))/) + if (match && match[2]) { + return match[2] + } else { + match = name.match(/[-\s]([0-9]+)[\s.\]]/) + if (match) { + return match[1] || "-1" + } else { + match = name.match(/第\s*(\d+)\s*集/) + if (match) { + return match[1] || "-1"; + } else { + match = name.match(/(?:[sS][eE]?(\d+)[\.-](\d+))/) + if (match && match[2]) { + return match[2]; + } + } + } + return "-1" + } + } + + static async searchTMDB(dir: IAliFileResp): Promise { + const regex = /(?:s|S)(\d+)(?:(?:e|E)(\d+))?|(\d{4})|(\d{3,4}p)|([sS][eE]\d+)|(\d{4})|(\d{3,4}p)|([sS][eE]\d+)|(\d+)\s*第\s*(\d+)集/g; + + for (const fileItem of dir.items) { + // const videoInfo = await DB.getVideoInfo(fileItem.file_id) + // if (videoInfo) { + // fileItem.duration = videoInfo.duration + // fileItem.play_cursor = videoInfo.play_cursor + // } + const tmdb_cache = await DB.getValueString(fileItem.file_id) + if (tmdb_cache) { + const media_type = tmdb_cache.split(",")[0] + const tmdb_id = tmdb_cache.split(",")[1] + if (media_type === 'tv') { + const cacheData = await DB.getValueString(fileItem.file_id + "movieinfo") + if (cacheData) { + // @ts-ignore + fileItem.minfo = cacheData + if (fileItem.minfo) { + fileItem.minfo.cached = true + fileItem.minfo.media_type = 'tv' + } + } else { + const urlTv = `${Config.tmdbProxyUrl}/3/tv/${tmdb_id}?api_key=87d8eb3d0895eaf37c2929fd5d9f7cce&language=zh-CN` + const resp = await AliHttp.GetWithOutUserId(urlTv) + if (AliHttp.IsSuccess(resp.code)) { + fileItem.minfo = resp.body + await DB.saveValueString(fileItem.file_id+"movieinfo", resp.body) + if (fileItem.minfo) { + fileItem.minfo.cached = true + fileItem.minfo.media_type = 'tv' + } + } + } + + } else if (media_type === 'movie') { + const cacheData = await DB.getValueString(fileItem.file_id+"movieinfo") + if (cacheData) { + // @ts-ignore + fileItem.minfo = cacheData + if (fileItem.minfo) { + fileItem.minfo.cached = true + fileItem.minfo.media_type = 'movie' + } + } else { + const url = `${Config.tmdbProxyUrl}/3/movie/${tmdb_id}?api_key=87d8eb3d0895eaf37c2929fd5d9f7cce&language=zh-CN` + const resp = await AliHttp.GetWithOutUserId(url) + if (AliHttp.IsSuccess(resp.code)) { + fileItem.minfo = resp.body + await DB.saveValueString(fileItem.file_id+"movieinfo", resp.body) + if (fileItem.minfo) { + fileItem.minfo.cached = true + fileItem.minfo.media_type = 'movie' + } + } + } + } + } + + if (!fileItem.minfo) { + const cacheData = await DB.getValueString(fileItem.file_id+"movieinfo") + if (cacheData) { + // @ts-ignore + fileItem.minfo = cacheData + } else { + const parts = fileItem.name.split(regex) + const searchName = parts[0].replaceAll('.', ' ').replace(/\[.*?\]/g, '').replaceAll('&', ' ').trimEnd() + if (searchName.toLowerCase() == '4k' + || searchName == '字幕' || searchName == 'test' + || searchName.toLowerCase().includes('season')) continue + const url = `${Config.tmdbProxyUrl}/3/search/multi?api_key=87d8eb3d0895eaf37c2929fd5d9f7cce&language=zh-CN&query=${searchName}&page=1&include_adult=false` + const resp = await AliHttp.GetWithOutUserId(url); + if (AliHttp.IsSuccess(resp.code) && resp.body.results && resp.body.results.length > 0) { + fileItem.minfo = resp.body.results[0] + if (fileItem.minfo) { + await DB.saveValueString(fileItem.file_id+"movieinfo", resp.body.results[0]) + } + } + } + } + + if (fileItem.minfo && fileItem.minfo.media_type === 'tv') { + const seasonNum = this.seasonNumber(fileItem.name) + const episodeNum = this.episodeNumber(fileItem.name) + fileItem.season_num = Number(seasonNum) + fileItem.episode_num = Number(episodeNum) + // if (Number(seasonNum) != -1) { + // if (Number(episodeNum) != -1) { + // const season_poster = await DB.getValueString(fileItem.file_id+"season_poster") + // const episode_name = await DB.getValueString(fileItem.file_id+"episode_name") + // if (season_poster && episode_name) { + // fileItem.season_poster = season_poster + // fileItem.episode_name = episode_name + // } else { + // const urlSeason = `${Config.tmdbProxyUrl}/3/tv/${fileItem.minfo.id}/season/${fileItem.season_num}?api_key=87d8eb3d0895eaf37c2929fd5d9f7cce&language=zh-CN` + // const resp = await AliHttp.GetWithOutUserId(urlSeason) + // if (AliHttp.IsSuccess(resp.code)) { + // fileItem.season_poster = resp.body.poster_path + // // fileItem.season_poster = resp.body.episodes.filter(episode => episode.episode_number === fileItem.episode_num)[0].still_path + // // @ts-ignore + // const episodeRes = resp.body.episodes.filter(episode => episode.episode_number === fileItem.episode_num) + // if (episodeRes.length > 0) { + // fileItem.episode_name = episodeRes[0].name + // } + // if ( fileItem.season_poster) { + // await DB.saveValueString(fileItem.file_id+"season_poster", fileItem.season_poster) + // } + // if (fileItem.episode_name) { + // await DB.saveValueString(fileItem.file_id+"episode_name", fileItem.episode_name) + // } + // } + // } + // } else { + // const season_poster = await DB.getValueString(fileItem.file_id+"season_poster") + // if (season_poster) { + // fileItem.season_poster = season_poster + // } else { + // const tvApi = `${Config.tmdbProxyUrl}/3/tv/${fileItem.minfo.id}?api_key=87d8eb3d0895eaf37c2929fd5d9f7cce&language=zh-CN` + // const tvResp = await AliHttp.GetWithOutUserId(tvApi) + // if (AliHttp.IsSuccess(tvResp.code) && tvResp.body.seasons && tvResp.body.seasons.length > 0) { + // // fileItem.tvSeason = tvResp.body.seasons + // // @ts-ignore + // const filteredSeasons = tvResp.body.seasons.filter(season => season.season_number === Number(seasonNum)) + // if (filteredSeasons && filteredSeasons.length > 0) { + // fileItem.season_poster = filteredSeasons[0].poster_path + // if ( fileItem.season_poster) { + // await DB.saveValueString(fileItem.file_id+"season_poster", fileItem.season_poster) + // } + // } + // } + // } + // } + // } + } + } + } + private static async _ApiDirFileListOnePage(orderby: string, order: string, dir: IAliFileResp, type: string, pageIndex: number): Promise { let url = 'adrive/v3/file/list' - if (useSettingStore().uiShowPanMedia == false) url += '?jsonmask=next_marker%2Cpunished_file_count%2Ctotal_count%2Citems(' + AliDirFileList.ItemJsonmask + ')' - else url += '?jsonmask=next_marker%2Cpunished_file_count%2Ctotal_count%2Citems(' + AliDirFileList.ItemJsonmask + '%2Cvideo_media_metadata(duration%2Cwidth%2Cheight%2Ctime)%2Cvideo_preview_metadata%2Fduration%2Cimage_media_metadata)' - - let postData = { + if (useSettingStore().uiShowPanMedia == false) { + url += '?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + ')' + } else { + url += '?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + '%2Cuser_meta%2Cvideo_media_metadata(duration%2Cwidth%2Cheight%2Ctime)%2Cvideo_preview_metadata%2Fduration%2Cimage_media_metadata)' + } + let postData: any = { drive_id: dir.m_drive_id, - parent_file_id: dir.dirID, + parent_file_id: dir.dirID.includes('root') ? 'root' : dir.dirID, marker: dir.next_marker, - limit: 200, + limit: 100, all: false, url_expire_sec: 14400, fields: '*', @@ -250,15 +446,28 @@ export default class AliDirFileList { return await AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex, type) } - private static async _ApiDirFileListOnePageOpenApi(orderby: string, order: string, dir: IAliFileResp, type: string, pageIndex: number): Promise { + private static async _ApiDirFileListOnePageOpenApi(orderby: string, order: string, dir: IAliFileResp, type: string, pageIndex: number, search= true): Promise { let url = 'adrive/v1.0/openFile/list' - let postData = { - drive_id: dir.m_drive_id, - parent_file_id: dir.dirID, - marker: dir.next_marker, - limit: 200, - order_by: orderby, - order_direction: order.toUpperCase() + let postData + if (useSettingStore().uiFileListMode === 'movie' && search) { + postData = { + drive_id: dir.m_drive_id, + parent_file_id: dir.dirID.includes('root') ? 'root' : dir.dirID, + marker: dir.next_marker, + category: "video", + limit: 200, + order_by: orderby, + order_direction: order.toUpperCase() + } + } else { + postData = { + drive_id: dir.m_drive_id, + parent_file_id: dir.dirID, + marker: dir.next_marker, + limit: 200, + order_by: orderby, + order_direction: order.toUpperCase() + } } if (type) { postData = Object.assign(postData, { type }) @@ -268,25 +477,37 @@ export default class AliDirFileList { return AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex, type) } - - private static async _ApiDirFileListCount(dir: IAliFileResp, type: string): Promise { - const url = 'adrive/v1.0/openFile/search' - const postData = { + + private static async _ApiDirFileListCount(dir: IAliFileResp, dirID: string, type: string): Promise { + let isPic = dirID.includes('pic') + type = isPic ? 'file' : type + let url = '' + if (!isPic) { + url = 'adrive/v1.0/openFile/search' + } else { + url = 'adrive/v3/file/search' + } + let parent_file_id = dir.dirID.includes('_root') ? 'root' : dir.dirID + const postData: any = { drive_id: dir.m_drive_id, marker: '', limit: 1, all: false, url_expire_sec: 14400, fields: 'thumbnail', - query: 'parent_file_id="' + dir.dirID + '"' + (type ? ' and type="' + type + '"' : ''), + query: 'parent_file_id="' + parent_file_id + '"' + (type ? ' and type="' + type + '"' : ''), return_total_count: true } + if (!isPic) { + delete postData.all + delete postData.url_expire_sec + } const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') try { if (AliHttp.IsSuccess(resp.code)) { return resp.body.total_count || 0 - } else { - DebugLog.mSaveWarning('_ApiDirFileListCount err=' + dir.dirID + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ApiDirFileListCount err=' + dir.dirID + ' ' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ApiDirFileListCount ' + dir.dirID, err) @@ -294,7 +515,7 @@ export default class AliDirFileList { return 0 } - + private static async _ApiFavoriteFileListCount(dir: IAliFileResp): Promise { const url = 'adrive/v3/file/search' const postData = { @@ -311,8 +532,8 @@ export default class AliDirFileList { try { if (AliHttp.IsSuccess(resp.code)) { return resp.body.total_count || 0 - } else { - DebugLog.mSaveWarning('_ApiFavoriteFileListCount err=' + dir.dirID + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ApiFavoriteFileListCount err=' + dir.dirID + ' ' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ApiFavoriteFileListCount ' + dir.dirID, err) @@ -322,8 +543,8 @@ export default class AliDirFileList { private static async _ApiFavorFileListOnePage(orderby: string, order: string, dir: IAliFileResp, pageIndex: number): Promise { let url = 'v2/file/list_by_custom_index_key' - if (useSettingStore().uiShowPanMedia == false) url += '?jsonmask=next_marker%2Cpunished_file_count%2Ctotal_count%2Citems(' + AliDirFileList.ItemJsonmask + ')' - else url += '?jsonmask=next_marker%2Cpunished_file_count%2Ctotal_count%2Citems(' + AliDirFileList.ItemJsonmask + '%2Cvideo_media_metadata(duration%2Cwidth%2Cheight%2Ctime)%2Cvideo_preview_metadata%2Fduration%2Cimage_media_metadata)' + if (useSettingStore().uiShowPanMedia == false) url += '?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + ')' + else url += '?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + '%2Cuser_meta%2Cvideo_media_metadata(duration%2Cwidth%2Cheight%2Ctime)%2Cvideo_preview_metadata%2Fduration%2Cimage_media_metadata)' const postData = { drive_id: dir.m_drive_id, @@ -341,7 +562,7 @@ export default class AliDirFileList { } private static async _ApiTrashFileListOnePage(orderby: string, order: string, dir: IAliFileResp, pageIndex: number): Promise { - const url = 'v2/recyclebin/list?jsonmask=next_marker%2Cpunished_file_count%2Ctotal_count%2Citems(' + AliDirFileList.ItemJsonmask + ')' + const url = 'v2/recyclebin/list?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + ')' const postData = { drive_id: dir.m_drive_id, @@ -361,7 +582,9 @@ export default class AliDirFileList { const url = 'adrive/v1/file/listDeleted' const postData = { drive_id: dir.m_drive_id, - album_drive_id: dir.m_drive_id, + limit: 100, + order_by: 'gmt_deleted', + order_direction: 'DESC', marker: dir.next_marker } const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') @@ -369,14 +592,17 @@ export default class AliDirFileList { } static async _ApiSearchFileListOnePage(orderby: string, order: string, dir: IAliFileResp, pageIndex: number): Promise { - let url = 'adrive/v1.0/openFile/search' + let url = 'adrive/v3/file/search' + if (useSettingStore().uiShowPanMedia == false) url += '?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + ')' + else url += '?jsonmask=next_marker%2Citems(' + AliDirFileList.ItemJsonmask + '%2Cuser_meta%2Cvideo_media_metadata(duration%2Cwidth%2Cheight%2Ctime)%2Cvideo_preview_metadata%2Fduration%2Cimage_media_metadata)' + let query = '' + let drive_id_list = [] if (dir.dirID.startsWith('color')) { const color = dir.dirID.substring('color'.length).split(' ')[0].replace('#', 'c') query = 'description="' + color + '"' } else if (dir.dirID.startsWith('search')) { - const search = dir.dirID.substring('search'.length).split(' ') - + const search = dir.dirID.substring('search'.length).split(' ') let word = '' for (let i = 0; i < search.length; i++) { const itemstr = search[i] @@ -384,74 +610,78 @@ export default class AliDirFileList { word += itemstr + ' ' continue } - const kv = search[i].split(':') const k = kv[0] const v = kv[1] - if (k == 'type') { + if (k == 'range') { + const arr = v.split(',') + for (let j = 0; j < arr.length; j++) { + drive_id_list.push(GetDriveID(dir.m_user_id, arr[j])) + } + } else if (k == 'type') { const arr = v.split(',') let type = '' for (let j = 0; j < arr.length; j++) { - if (arr[j] == 'folder') type += 'type="' + arr[j] + '" or ' - else if (arr[j]) type += 'category="' + arr[j] + '" or ' + if (arr[j] == 'folder') type += 'type="' + arr[j] + '" or ' + else if (arr[j]) type += 'category="' + arr[j] + '" or ' } type = type.substring(0, type.length - 4).trim() if (type && type.indexOf(' or ') > 0) query += '(' + type + ') and ' else if (type) query += type + ' and ' - } - // else if (k == 'size') { - // const size = parseInt(v) - // if (size > 0) query += 'size = ' + v + ' and ' - // } - // else if (k == 'max') { - // const max = parseInt(v) - // if (max > 0) query += 'size <= ' + v + ' and ' - // } else if (k == 'min') { - // const min = parseInt(v) - // if (min > 0) query += 'size >= ' + v + ' and ' - // } - else if (k == 'begin') { + } else if (k == 'size') { + const size = parseInt(v) + if (size > 0) query += 'size = ' + v + ' and ' + } else if (k == 'description') { + query += 'description = ' + v + ' and ' + } else if (k == 'max') { + const max = parseInt(v) + if (max > 0) query += 'size <= ' + v + ' and ' + } else if (k == 'min') { + const min = parseInt(v) + if (min > 0) query += 'size >= ' + v + ' and ' + } else if (k == 'begin') { const dt = new Date(v).toISOString() - query += 'created_at >= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' + query += 'updated_at >= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' } else if (k == 'end') { const dt = new Date(v).toISOString() - query += 'created_at <= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' + query += 'updated_at <= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' } else if (k == 'ext') { const arr = v.split(',') let extin = '' for (let j = 0; j < arr.length; j++) { extin += '"' + arr[j] + '",' } - if (extin.length > 0) extin = extin.substring(0, extin.length - 1) + if (extin.length > 0) extin = extin.substring(0, extin.length - 1) if (extin) query += 'file_extension in [' + extin + '] and ' } else if (k == 'fav') query += 'starred = ' + v + ' and ' } word = word.trim() if (word) query += 'name match "' + word.replaceAll('"', '\\"') + '" and ' - if (query.length > 0) query = query.substring(0, query.length - 5) + if (query.length > 0) query = query.substring(0, query.length - 5) if (query.startsWith('(') && query.endsWith(')')) query = query.substring(1, query.length - 1) } - const postData = { - drive_id: dir.m_drive_id, + const postData: any = { marker: dir.next_marker, - limit: 100 , + limit: 100, fields: '*', query: query, order_by: orderby + ' ' + order } + if (drive_id_list.length > 0) postData.drive_id_list = drive_id_list + else postData.drive_id = dir.m_drive_id const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') return AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex) } static async _ApiSearchFileListCount(dir: IAliFileResp): Promise { - const url = 'adrive/v1.0/openFile/search' - + const url = 'adrive/v3/file/search' let query = '' + let drive_id_list = [] if (dir.dirID.startsWith('color')) { const color = dir.dirID.substring('color'.length).split(' ')[0].replace('#', 'c') query = 'description="' + color + '"' } else if (dir.dirID.startsWith('search')) { - const search = dir.dirID.substring('search'.length).split(' ') + const search = dir.dirID.substring('search'.length).split(' ') let word = '' for (let i = 0; i < search.length; i++) { @@ -464,62 +694,68 @@ export default class AliDirFileList { const kv = search[i].split(':') const k = kv[0] const v = kv[1] - if (k == 'type') { + if (k == 'range') { + const arr = v.split(',') + for (let j = 0; j < arr.length; j++) { + drive_id_list.push(GetDriveID(dir.m_user_id, arr[j])) + } + } else if (k == 'type') { const arr = v.split(',') let type = '' for (let j = 0; j < arr.length; j++) { - if (arr[j] == 'folder') type += 'type="' + arr[j] + '" or ' - else if (arr[j]) type += 'category="' + arr[j] + '" or ' + if (arr[j] == 'folder') type += 'type="' + arr[j] + '" or ' + else if (arr[j]) type += 'category="' + arr[j] + '" or ' } type = type.substring(0, type.length - 4).trim() if (type && type.indexOf(' or ') > 0) query += '(' + type + ') and ' else if (type) query += type + ' and ' - } - // else if (k == 'size') { - // const size = parseInt(v) - // if (size > 0) query += 'size = ' + v + ' and ' - // } else if (k == 'max') { - // const max = parseInt(v) - // if (max > 0) query += 'size <= ' + v + ' and ' - // } else if (k == 'min') { - // const min = parseInt(v) - // if (min > 0) query += 'size >= ' + v + ' and ' - // } - else if (k == 'begin') { + } else if (k == 'size') { + const size = parseInt(v) + if (size > 0) query += 'size = ' + v + ' and ' + } else if (k == 'description') { + query += 'description = ' + v + ' and ' + } else if (k == 'max') { + const max = parseInt(v) + if (max > 0) query += 'size <= ' + v + ' and ' + } else if (k == 'min') { + const min = parseInt(v) + if (min > 0) query += 'size >= ' + v + ' and ' + } else if (k == 'begin') { const dt = new Date(v).toISOString() - query += 'created_at >= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' + query += 'updated_at >= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' } else if (k == 'end') { const dt = new Date(v).toISOString() - query += 'created_at <= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' + query += 'updated_at <= "' + dt.substring(0, dt.lastIndexOf('.')) + '" and ' } else if (k == 'ext') { const arr = v.split(',') let extin = '' for (let j = 0; j < arr.length; j++) { extin += '"' + arr[j] + '",' } - if (extin.length > 0) extin = extin.substring(0, extin.length - 1) + if (extin.length > 0) extin = extin.substring(0, extin.length - 1) if (extin) query += 'file_extension in [' + extin + '] and ' } else if (k == 'fav') query += 'starred = ' + v + ' and ' } word = word.trim() if (word) query += 'name match "' + word.replaceAll('"', '\\"') + '" and ' - if (query.length > 0) query = query.substring(0, query.length - 5) + if (query.length > 0) query = query.substring(0, query.length - 5) if (query.startsWith('(') && query.endsWith(')')) query = query.substring(1, query.length - 1) } - const postData = { - drive_id: dir.m_drive_id, + const postData: any = { marker: dir.next_marker, - limit: 1 , + limit: 1, fields: '*', query: query, return_total_count: true } + if (drive_id_list.length > 0) postData.drive_id_list = drive_id_list + else postData.drive_id = dir.m_drive_id const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') try { if (AliHttp.IsSuccess(resp.code)) { return (resp.body.total_count as number) || 0 - } else { - DebugLog.mSaveWarning('_ApiSearchFileListCount err=' + dir.dirID + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ApiSearchFileListCount err=' + dir.dirID + ' ' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ApiSearchFileListCount ' + dir.dirID, err) @@ -527,8 +763,42 @@ export default class AliDirFileList { return 0 } + static async _ApiAlbumListOnePage(orderby: string, order: string, dir: IAliFileResp, pageIndex: number): Promise { + const url = 'adrive/v1/album/list' + const postData = { + limit: 100, + order_by: 'updated_at', + order_direction: 'DESC' + } + const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') + return AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex) + } + + static async _ApiAlbumListFilesOnePage(orderby: string, order: string, dir: IAliFileResp, pageIndex: number): Promise { + const url = 'adrive/v1/album/list_files' + const postData = { + album_id: dir.albumID, + fields: '*', + filter: '', + limit: 100, + order_by: 'joined_at', + order_direction: 'DESC', + image_thumbnail_process: 'image/resize,w_400/format,jpeg', + image_url_process: 'image/resize,w_1920/format,jpeg', + video_thumbnail_process: 'video/snapshot,t_0,f_jpg,ar_auto,w_1000' + } + const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') + return AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex) + } + static async _ApiVideoListRecent(orderby: string, order: string, dir: IAliFileResp, pageIndex: number): Promise { - const url = 'adrive/v1.0/openFile/video/recentList' + let need_open_api = true + let url = '' + if (need_open_api) { + url = 'adrive/v1.1/openFile/video/recentList' + } else { + url = 'adrive/v2/video/recentList' + } const postData = {} const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') return AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex) @@ -565,8 +835,8 @@ export default class AliDirFileList { return AliDirFileList._FileListOnePage(orderby, order, dir, resp, pageIndex) } - - static _FileListOnePage(orderby: string, order: string, dir: IAliFileResp, resp: IUrlRespData, pageIndex: number, type: string = ''): boolean { + + static _FileListOnePage(orderby: string, order: string, dir: IAliFileResp, resp: IUrlRespData, pageIndex: number, type: string = '', refresh: boolean = true): boolean { try { if (AliHttp.IsSuccess(resp.code)) { const dirPart: IAliFileResp = { @@ -582,48 +852,55 @@ export default class AliDirFileList { dir.next_marker = resp.body.next_marker || '' const isRecover = dir.dirID == 'recover' - const isDirFile = dir.dirID == 'root' || (dir.dirID.length == 40 && !dir.dirID.startsWith('search')) + const isDirFile = dir.dirID.includes('root') || (dir.dirID.length == 40 && !dir.dirID.startsWith('search')) const isVideo = dir.dirID.startsWith('video') - // const issearch = dir.dirID.startsWith('search') - // const iscolor = dir.dirID.startsWith('color') - const downUrl = isRecover ? '' : 'https://api.aliyundrive.com/v2/file/download?t=' + Date.now().toString() + const isPic = dir.dirID.includes('pic') + const downUrl = isRecover ? '' : 'https://api.alipan.com/v2/file/download?t=' + Date.now().toString() if (resp.body.items) { + let settingStore = useSettingStore() const driverData = TreeStore.GetDriver(dir.m_drive_id) const DirFileSizeMap = driverData?.DirFileSizeMap || {} const DirTotalSizeMap = driverData?.DirTotalSizeMap || {} - const isFolderSize = useSettingStore().uiFolderSize + const isFolderSize = settingStore.uiFolderSize let dirList: IAliGetFileModel[] = [] let fileList: IAliGetFileModel[] = [] for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { const item = resp.body.items[i] as IAliFileItem if (isVideo) { - if (!item.compilation_id && (!item.drive_id || !item.file_id)) continue - + if (!item.compilation_id && (!item.drive_id || !item.file_id)) continue if (!item.compilation_id) { item.type = 'file' + item.category = 'video' item.compilation_id = item.drive_id + '_' + item.file_id } - if (item.video_type == 'COMPILATION') { item.type = 'folder' item.drive_id = item.compilation_id.split('_')[0] item.file_id = item.compilation_id.split('_')[1] + } else { + item.category = 'video' + } + } + if (isPic) { + if (!item.album_id && (!item.drive_id || !item.file_id)) continue + if (item.album_id) { + item.type = 'folder' + item.file_id = item.album_id } } if (dir.itemsKey.has(item.file_id)) continue - const add = AliDirFileList.getFileInfo(item, downUrl) + const add = AliDirFileList.getFileInfo(dir.m_user_id, item, downUrl) if (isRecover) add.description = item.content_hash - if (isVideo) { - add.compilation_id = item.compilation_id - } + if (isVideo) add.compilation_id = item.compilation_id + if (isPic) add.album_id = item.album_id if (add.isDir) { if (isFolderSize) { add.size = DirTotalSizeMap[add.file_id] || DirFileSizeMap[add.file_id] || 0 add.sizeStr = humanSize(add.size) } if (isDirFile) dirList.push(add) - else fileList.push(add) + else fileList.push(add) } else fileList.push(add) dir.itemsKey.add(item.file_id) } @@ -638,34 +915,32 @@ export default class AliDirFileList { dir.items.push(...fileList) } } - dirPart.punished_file_count = resp.body.punished_file_count || 0 dir.punished_file_count += resp.body.punished_file_count || 0 - - if (pageIndex >= 0 && type == '') { + // 相册文件数 + if (isPic && dir.dirID != 'pic_root') { + dir.itemsTotal = resp.body.totalCount + } + if (pageIndex >= 0 && type == '' && refresh) { const pan = usePanFileStore() if (pan.DriveID == dir.m_drive_id) { pan.mSaveDirFileLoadingPart(pageIndex, dirPart, dir.itemsTotal || 0) - } else { - const resPan = useResPanFileStore(); - resPan.mSaveDirFileLoadingPart(pageIndex, dirPart, dir.itemsTotal || 0) } } if (dirPart.next_marker == 'cancel') dir.next_marker = 'cancel' if (isVideo && dir.items.length >= 500) dir.next_marker = '' return true } else if (resp.code == 404) { - dir.items.length = 0 dir.next_marker = '' return true } else if (resp.body && resp.body.code) { dir.items.length = 0 - dir.next_marker = resp.body.code + dir.next_marker = resp.body.code message.warning('列出文件出错 ' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_FileListOnePage err=' + dir.dirID + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_FileListOnePage err=' + dir.dirID + ' ' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_FileListOnePage ' + dir.dirID, err) @@ -674,62 +949,43 @@ export default class AliDirFileList { return false } - - static async ApiDirFileSize(user_id: string, drive_id: string, file_idList: string[]): Promise<{ dirID: string; size: number }[] | undefined> { - const list: Map = new Map() - let postData = '{"requests":[' + static async ApiDirFileSize(user_id: string, drive_id: string, file_idList: string[]): Promise<{ + dirID: string; + size: number + }[] | undefined> { + const list: Map = new Map() for (let i = 0, maxi = file_idList.length; i < maxi; i++) { list.set(file_idList[i], { dirID: file_idList[i], size: 0 }) - if (i > 0) postData = postData + ',' let id = file_idList[i].includes('root') ? 'root' : file_idList[i] - const data2 = { - body: { - drive_id: drive_id, - query: 'parent_file_id="' + file_idList[i] + '" and type="file"', - limit: 100, - fields: 'thumbnail', - order_by: 'size DESC' - }, - headers: { 'Content-Type': 'application/json' }, - id: id, - method: 'POST', - url: '/file/search' + let postData = { + drive_id: drive_id, + limit: 100, + query: 'parent_file_id="' + id + '" and type="file"', + fields: 'thumbnail', + order_by: 'size DESC' } - postData = postData + JSON.stringify(data2) - } - postData += '],"resource":"file"}' - - const url = 'v2/batch?jsonmask=responses(id%2Cstatus%2Cbody(next_marker%2Citems(size)))' - const resp = await AliHttp.Post(url, postData, user_id, '') - - try { - if (AliHttp.IsSuccess(resp.code)) { - const responses = resp.body.responses - for (let j = 0, maxj = responses.length; j < maxj; j++) { - const respi = responses[j] - - if (respi.id && respi.status && respi.status >= 200 && respi.status <= 205) { - if (respi.body && respi.body.items && respi.body.items.length > 0) { - let size = 0 - const items = respi.body.items - for (let k = 0, maxk = items.length; k < maxk; k++) { - size += items[k].size || 0 - } - const find = list.get(respi.id) - if (find) find.size = size + const url = 'adrive/v3/file/search?jsonmask=next_marker%2Citems(size)' + const resp = await AliHttp.Post(url, postData, user_id, '') + try { + if (AliHttp.IsSuccess(resp.code)) { + if (resp.body && resp.body.items && resp.body.items.length > 0) { + let size = 0 + const items = resp.body.items + for (let k = 0, maxk = items.length; k < maxk; k++) { + size += items[k].size || 0 } + const find = list.get(id) + if (find) find.size = size } - + return MapValueToArray(list) + } else { + DebugLog.mSaveWarning('ApiDirFileSize err=' + (resp.code || ''), resp.body) + return undefined } - return MapValueToArray(list) - } else { - - DebugLog.mSaveWarning('ApiDirFileSize err=' + (resp.code || '')) - return undefined + } catch (err: any) { + DebugLog.mSaveWarning('ApiDirFileSize', err) } - } catch (err: any) { - DebugLog.mSaveWarning('ApiDirFileSize', err) } return MapValueToArray(list) } diff --git a/aliyunpan/src/aliapi/dirlist.ts b/aliyunpan/src/aliapi/dirlist.ts index ff96c07a37..691a655fab 100644 --- a/aliyunpan/src/aliapi/dirlist.ts +++ b/aliyunpan/src/aliapi/dirlist.ts @@ -1,43 +1,41 @@ import DebugLog from '../utils/debuglog' import { MapValueToArray } from '../utils/utils' import AliHttp from './alihttp' -import {IAliFileItem, IAliGetDirModel, IAliGetFileModel} from './alimodels' -import AliDirFileList from './dirfilelist' +import { IAliGetDirModel, IAliGetFileModel } from './alimodels' import dayjs from 'dayjs' import AliTrash from './trash' import { DirData } from '../store/treestore' import AliUser from './user' -import AliFile from "./file"; export interface IAliDirResp { items: IAliGetDirModel[] next_marker: string - m_user_id: string - m_drive_id: string - dirID: string - dirName: string + m_user_id: string + m_drive_id: string + dirID: string + dirName: string } export interface IDirDataResp { items: DirData[] next_marker: string - m_user_id: string - m_drive_id: string - dirID: string - dirName: string + m_user_id: string + m_drive_id: string + dirID: string + dirName: string } export interface IAliDirBatchResp { items: IAliGetFileModel[] itemsKey: Set next_marker: string - dirID: string + dirID: string } export default class AliDirList { - + static async ApiFastAllDirList(user_id: string, drive_id: string): Promise { const result: IAliDirResp = { items: [], @@ -88,7 +86,7 @@ export default class AliDirList { namesearch: '', size: item.size || 0, time: new Date(item.updated_at).getTime(), - + description: item.description || '' } allMap.set(add.file_id, add) @@ -109,31 +107,30 @@ export default class AliDirList { namesearch: '', size: item.size || 0, time: new Date(item.updated_at).getTime(), - + description: item.description || '' } allMap.set(add.file_id, add) lockSet.add(item.file_id) } } - if (isFind) break - if (resp1.body.next_marker == '' && resp2.body.next_marker == '') break + if (isFind) break + if (resp1.body.next_marker == '' && resp2.body.next_marker == '') break } else { - + errorMessage += 'err1' + (resp1.status || '') errorMessage += 'err2' + (resp2.status || '') break } } else if (resp.code == 402) { - - DebugLog.mSaveWarning('ApiFastAllDirList err=' + drive_id + ' ' + (resp.code || '')) + DebugLog.mSaveWarning('ApiFastAllDirList err=' + drive_id + ' ' + (resp.code || ''), resp.body) break - } else { + } else if (!AliHttp.HttpCodeBreak(resp.code)) { errorMessage += 'err' + (resp.code || '') - DebugLog.mSaveWarning('ApiFastAllDirList err=' + (resp.code || '')) + DebugLog.mSaveWarning('ApiFastAllDirList err=' + (resp.code || ''), resp.body) } } catch (err: any) { - + errorMessage += 'err' + (err.message || '') DebugLog.mSaveWarning('ApiFastAllDirList', err) break @@ -148,87 +145,6 @@ export default class AliDirList { return result } - - static async ApiBatchDirFileList(user_id: string, drive_id: string, dirList: IAliDirBatchResp[], limit: number, listTypeOrQuery: string): Promise { - if (!user_id || !drive_id) return false - - let postData = '{"requests":[' - for (let i = 0, maxi = dirList.length; i < maxi; i++) { - if (i > 0) postData = postData + ',' - - let query = 'parent_file_id="' + dirList[i].dirID + '"' - if (listTypeOrQuery == 'all') query = 'parent_file_id="' + dirList[i].dirID + '"' - else if (listTypeOrQuery == 'folder') query = 'parent_file_id="' + dirList[i].dirID + '" and type = "folder"' - else if (listTypeOrQuery == 'file') query = 'parent_file_id="' + dirList[i].dirID + '" and type = "file"' - else query = query + listTypeOrQuery - const data2 = { - body: { - drive_id: drive_id, - query: query, - marker: dirList[i].next_marker, - url_expire_sec: 14400, - limit: limit, - // fields: 'thumbnail', - image_thumbnail_process: '', - video_thumbnail_process: '', - image_url_process: '' - }, - headers: { 'Content-Type': 'application/json' }, - id: dirList[i].dirID, - method: 'POST', - url: '/file/search' - } - postData = postData + JSON.stringify(data2) - } - postData += '],"resource":"file"}' - - const url = 'v2/batch' - const resp = await AliHttp.Post(url, postData, user_id, '') - - try { - if (AliHttp.IsSuccess(resp.code)) { - const responses = resp.body.responses - for (let j = 0, maxj = responses.length; j < maxj; j++) { - const status = responses[j].status as number - if (status >= 200 && status <= 205) { - const respi = responses[j] - const id = respi.id || '' - for (let i = 0, maxi = dirList.length; i < maxi; i++) { - if (dirList[i].dirID == id) { - const dir = dirList[i] - dir.next_marker = respi.body.next_marker - const items = respi.body.items - for (let i = 0, maxi = items.length; i < maxi; i++) { - const fileItem = items[i] as IAliFileItem - if (fileItem.type === 'file') { - const fileDetails = await AliFile.ApiFileInfoOpenApi(user_id, drive_id, fileItem.file_id); - fileItem.thumbnail = fileDetails?.thumbnail - fileItem.url = fileDetails?.url || fileItem.url - fileItem.download_url = fileDetails?.download_url || fileItem.download_url - fileItem.starred = fileDetails?.starred || fileItem.starred - fileItem.trashed = fileDetails?.trashed || fileItem.trashed - fileItem.deleted = fileDetails?.deleted || fileItem.deleted - fileItem.description = fileDetails?.description || fileItem.description - } - const add = AliDirFileList.getFileInfo(fileItem, '') - dir.items.push(add) - } - break - } - } - } - } - return true - } else { - DebugLog.mSaveWarning('ApiBatchDirFileList err=' + (resp.code || '')) - } - } catch (err: any) { - DebugLog.mSaveWarning('ApiBatchDirFileList', err) - } - return false - } - - static async ApiFastAllDirListByTime(user_id: string, drive_id: string, created_at: Date): Promise { const result: IAliDirResp = { items: [], @@ -240,7 +156,7 @@ export default class AliDirList { } if (!user_id || !drive_id) return result - + const allMap = new Map() let dirList: IAliDirBatchResp[] = [] @@ -301,7 +217,13 @@ export default class AliDirList { const respi = responses[j] const id = respi.id || '' for (let i = 0, maxi = dirList.length; i < maxi; i++) { - if (dirList[i].dirID.replaceAll('"', '').replaceAll(' ', '').replaceAll('-', '').replaceAll(':', '').replaceAll(',', '') == id) { + let pid = dirList[i].dirID + .replaceAll('"', '') + .replaceAll(' ', '') + .replaceAll('-', '') + .replaceAll(':', '') + .replaceAll(',', '') + if (pid == id) { const dir = dirList[i] const items = respi.body.items dir.next_marker = respi.body.next_marker @@ -318,7 +240,7 @@ export default class AliDirList { namesearch: '', size: item.size || 0, time: new Date(item.updated_at).getTime(), - + description: item.description || '' } allMap.set(add.file_id, add) @@ -330,9 +252,9 @@ export default class AliDirList { } dirList.length = 0 dirList = list - } else { + } else if (!AliHttp.HttpCodeBreak(resp.code)) { errorMessage = (resp.code || '').toString() - DebugLog.mSaveWarning('SSApiBatchDirFileList err=' + (resp.code || '')) + DebugLog.mSaveWarning('SSApiBatchDirFileList err=' + (resp.code || ''), resp.body) } } catch (err: any) { errorMessage = err.message || '' @@ -348,7 +270,7 @@ export default class AliDirList { } static async _ApiDirFileListInfo(user_id: string, drive_id: string) { - const url = 'adrive/v1.0/openFile/search' + const url = 'adrive/v3/file/search' const postData = { drive_id: drive_id, marker: '', @@ -367,8 +289,8 @@ export default class AliDirList { const created_at = items.length > 0 ? new Date(items[0].created_at) : new Date() const total_count = resp.body.total_count || 0 return { created_at, total_count } - } else { - DebugLog.mSaveWarning('_ApiDirFileListInfo err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ApiDirFileListInfo err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ApiDirFileListInfo', err) @@ -376,14 +298,14 @@ export default class AliDirList { return { created_at: new Date(), total_count: 0 } } - - static async ApiFastAllDirListByPID(user_id: string, drive_id: string): Promise { + + static async ApiFastAllDirListByPID(user_id: string, drive_id: string, drive_root: string): Promise { const result: IDirDataResp = { items: [], next_marker: '', m_user_id: user_id, m_drive_id: drive_id, - dirID: 'root', + dirID: drive_root, dirName: '' } if (!user_id || !drive_id) return result @@ -391,25 +313,27 @@ export default class AliDirList { const allMap = new Map() const dirCount = await AliUser.ApiUserDriveFileCount(user_id, '', 'folder') const PIDList: string[] = [] - - const root = await AliTrash.ApiDirFileListNoLock(user_id, drive_id, 'root', '', 'name ASC', 'folder', 0) + + const root = await AliTrash.ApiDirFileListNoLock(user_id, drive_id, drive_root, '', 'name ASC', 'folder', 0) for (let i = 0, maxi = root.items.length; i < maxi; i++) { const item = root.items[i] + if (item.parent_file_id === 'root') { + item.parent_file_id = drive_root + } const add: DirData = { file_id: item.file_id, + drive_id: item.drive_id, parent_file_id: item.parent_file_id, name: item.name, time: item.time, + description: item.description, size: 0 } - allMap.set(add.file_id, add) PIDList.push(add.file_id) } - - - let dirList: IAliDirBatchResp[] = [] let errorMessage = '' + let dirList: IAliDirBatchResp[] = [] let index = 0 while (true) { while (dirList.length < 30) { @@ -417,7 +341,92 @@ export default class AliDirList { let dirID = 'parent_file_id in [' let add = 0 for (let maxj = PIDList.length; index < maxj; index++) { - dirID += add == 0 ? '"' + PIDList[index] + '"' : ',"' + PIDList[index] + '"' + let PID = PIDList[index].includes('root') ? 'root' : PIDList[index] + dirID += add == 0 ? '"' + PID + '"' : ',"' + PID + '"' + add++ + if (add >= 50) break + } + dirID += ']' + dirList.push({ dirID: dirID, next_marker: '', items: [], itemsKey: new Set() } as IAliDirBatchResp) + } else break + } + if (dirList.length == 0) break + for (let i = 0, maxi = dirList.length; i < maxi; i++) { + const dir = dirList[i] + if(!dir) break + const query = 'type="folder" and ' + dir.dirID + let postData = { + drive_id: drive_id, + limit: 100, + query: query, + fields: 'thumbnail', + order_by: 'name ASC', + marker: dir.next_marker + } + const url = 'adrive/v3/file/search?jsonmask=next_marker%2Citems(drive_id%2Ccreated_at%2Cfile_id%2Cname%2Cparent_file_id%2Cupdated_at%2Cdescription)' + const resp = await AliHttp.Post(url, postData, user_id, '') + try { + if (AliHttp.IsSuccess(resp.code)) { + const items = resp.body.items + const list: IAliDirBatchResp[] = [] + dir.next_marker = resp.body.next_marker + if (dir.next_marker) { + list.push(dir) + } + for (let i = 0, maxi = items.length; i < maxi; i++) { + const item = items[i] + if (allMap.has(item.file_id)) continue + if (item.parent_file_id === 'root') { + item.parent_file_id = drive_root + } + const add: DirData = { + file_id: item.file_id, + drive_id: item.drive_id, + parent_file_id: item.parent_file_id, + name: item.name, + time: new Date(item.updated_at).getTime(), + description: item.description, + size: 0 + } + allMap.set(add.file_id, add) + PIDList.push(add.file_id) + } + dirList.length = 0 + dirList = list + if (window.WinMsgToMain) window.WinMsgToMain({ + cmd: 'MainShowAllDirProgress', + drive_id, + index: allMap.size, + total: dirCount + }) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + errorMessage = (resp.code || '').toString() + DebugLog.mSaveWarning('SSApiBatchDirFileList err=' + (resp.code || ''), resp.body) + } + } catch (err: any) { + errorMessage = err.message || '' + DebugLog.mSaveWarning('ApiBatchDirFileList', err) + } + } + } + const list = MapValueToArray(allMap) + console.log('listcount', list.length) + result.items = errorMessage ? [] : list + result.next_marker = errorMessage + return result + } + + static async ApiBatchLoadDirListByPID(user_id: string, drive_root: string, drive_id: string, PIDList: string[], allMap: Map, dirCount: number, errorMessage: string = '') { + let dirList: IAliDirBatchResp[] = [] + let index = 0 + while (true) { + while (dirList.length < 30) { + if (PIDList.length > index) { + let dirID = 'parent_file_id in [' + let add = 0 + for (let maxj = PIDList.length; index < maxj; index++) { + let PID = PIDList[index].includes('root') ? 'root' : PIDList[index] + dirID += add == 0 ? '"' + PID + '"' : ',"' + PID + '"' add++ if (add >= 50) break } @@ -431,6 +440,12 @@ export default class AliDirList { for (let i = 0, maxi = dirList.length; i < maxi; i++) { if (i > 0) postData = postData + ',' const query = 'type="folder" and ' + dirList[i].dirID + let id = dirList[i].dirID + .replaceAll('"', '') + .replaceAll(' ', '') + .replaceAll(',', '') + .substring(0, 54) + if (id.includes('root')) id = 'root' const data2 = { body: { drive_id: drive_id, @@ -441,7 +456,7 @@ export default class AliDirList { order_by: 'name ASC' }, headers: { 'Content-Type': 'application/json' }, - id: dirList[i].dirID.replaceAll('"', '').replaceAll(' ', '').replaceAll(',', '').substring(0, 54), + id: id, method: 'POST', url: '/file/search' } @@ -462,19 +477,29 @@ export default class AliDirList { const respi = responses[j] const id = respi.id || '' for (let i = 0, maxi = dirList.length; i < maxi; i++) { - if (dirList[i].dirID.replaceAll('"', '').replaceAll(' ', '').replaceAll(',', '').substring(0, 54) == id) { + let pid = dirList[i].dirID + .replaceAll('"', '') + .replaceAll(' ', '') + .replaceAll(',', '') + .substring(0, 54) + if (pid == id) { const dir = dirList[i] const items = respi.body.items dir.next_marker = respi.body.next_marker if (dir.next_marker) list.push(dir) for (let i = 0, maxi = items.length; i < maxi; i++) { - if (allMap.has(items[i].file_id)) continue const item = items[i] + if (allMap.has(item.file_id)) continue + if (item.parent_file_id === 'root') { + item.parent_file_id = drive_root + } const add: DirData = { file_id: item.file_id, + drive_id: item.drive_id, parent_file_id: item.parent_file_id, name: item.name, time: new Date(item.updated_at).getTime(), + description: item.description, size: 0 } allMap.set(add.file_id, add) @@ -487,22 +512,20 @@ export default class AliDirList { } dirList.length = 0 dirList = list - if (window.WinMsgToMain) window.WinMsgToMain({ cmd: 'MainShowAllDirProgress', drive_id, index: allMap.size, total: dirCount }) - } else { + if (window.WinMsgToMain) window.WinMsgToMain({ + cmd: 'MainShowAllDirProgress', + drive_id, + index: allMap.size, + total: dirCount + }) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { errorMessage = (resp.code || '').toString() - DebugLog.mSaveWarning('SSApiBatchDirFileList err=' + (resp.code || '')) + DebugLog.mSaveWarning('SSApiBatchDirFileList err=' + (resp.code || ''), resp.body) } } catch (err: any) { errorMessage = err.message || '' DebugLog.mSaveWarning('ApiBatchDirFileList', err) } } - - const list = MapValueToArray(allMap) - console.log('listcount', list.length) - result.items = errorMessage ? [] : list - result.next_marker = errorMessage - - return result } } diff --git a/aliyunpan/src/aliapi/file.ts b/aliyunpan/src/aliapi/file.ts index 145d8ac436..6d902cdfc6 100644 --- a/aliyunpan/src/aliapi/file.ts +++ b/aliyunpan/src/aliapi/file.ts @@ -1,13 +1,50 @@ import { useSettingStore } from '../store' import DebugLog from '../utils/debuglog' -import message from '../utils/message' -import { GetOssExpires, HanToPin } from '../utils/utils' +import { GetExpiresTime, HanToPin } from '../utils/utils' import AliHttp from './alihttp' -import {IAliFileItem, IAliGetDirModel, IAliGetFileModel, IAliGetForderSizeModel} from './alimodels' +import { IAliFileItem, IAliGetDirModel, IAliGetFileModel, IAliGetForderSizeModel } from './alimodels' import AliDirFileList from './dirfilelist' -import {ICompilationList, IDownloadUrl, IOfficePreViewUrl, IVideoPreviewUrl, IVideoXBTUrl} from './models' +import { ICompilationList, IDownloadUrl, IOfficePreViewUrl, IVideoPreviewUrl, IVideoXBTUrl } from './models' +import { DecodeEncName, GetDriveType } from './utils' +import { getRawUrl } from '../utils/proxyhelper' +import message from '../utils/message' export default class AliFile { + static async ApiFileDownloadUrlOpenApi(user_id: string, drive_id: string, file_id: string, expire_sec= 14400): Promise { + if (!user_id || !drive_id || !file_id) return 'file_id错误' + const data: IDownloadUrl = { + drive_id: drive_id, + file_id: file_id, + expire_time: 0, + url: '', + size: 0 + } + + const url = 'adrive/v1.0/openFile/getDownloadUrl' + let postData = { + "expire_sec": 14400, + "drive_id": drive_id, + "file_id": file_id + } + const resp = await AliHttp.Post(url, postData, user_id, '') + + if (AliHttp.IsSuccess(resp.code)) { + data.url = resp.body.url + data.size = resp.body.size + data.expire_time = GetExpiresTime(data.url) + console.log("ApiFileDownloadUrlOpenApi: ", data) + return data + } else if (resp.body.code == 'NotFound.FileId') { + return '文件已从网盘中彻底删除' + } else if (resp.body.code == 'ForbiddenFileInTheRecycleBin') { + return '文件已放入回收站' + } else if (resp.body.code) { + return resp.body.code as string + } else { + DebugLog.mSaveWarning('ApiFileDownloadUrl err=' + file_id + ' ' + (resp.code || '')) + } + return '网络错误' + } static async ApiFileInfoOpenApi(user_id: string, drive_id: string, file_id: string, image_thumbnail_width=100, @@ -31,30 +68,55 @@ export default class AliFile { } return undefined } - - static async ApiFileInfo(user_id: string, drive_id: string, file_id: string): Promise { + + static async ApiFileInfo(user_id: string, drive_id: string, file_id: string, ispic: boolean = false): Promise { if (!user_id || !drive_id || !file_id) return undefined - const url = 'v2/file/get' - const postData = { - drive_id: drive_id, - file_id: file_id, - url_expire_sec: 14400, - office_thumbnail_process: 'image/resize,w_400/format,jpeg', - image_thumbnail_process: 'image/resize,w_400/format,jpeg', - image_url_process: 'image/resize,w_1920/format,jpeg', - video_thumbnail_process: 'video/snapshot,t_106000,f_jpg,ar_auto,m_fast,w_400' + let url = '' + let postData = {} + if (!ispic) { + url = 'adrive/v1.0/openFile/get' + postData = { + drive_id: drive_id, + file_id: file_id, + image_thumbnail_width: 100, + video_thumbnail_width: 100, + video_thumbnail_time: 120000 + } + } else { + url = 'v2/file/get' + postData = { + drive_id: drive_id, + file_id: file_id, + url_expire_sec: 14400, + office_thumbnail_process: 'image/resize,w_400/format,jpeg', + image_thumbnail_process: 'image/resize,w_400/format,jpeg', + image_url_process: 'image/resize,w_1920/format,jpeg', + video_thumbnail_process: 'video/snapshot,t_106000,f_jpg,ar_auto,m_fast,w_400' + } } const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { - return resp.body as IAliFileItem - } else { - DebugLog.mSaveWarning('ApiFileInfo err=' + file_id + ' ' + (resp.code || '')) + let fileInfo = resp.body as IAliFileItem + if (fileInfo.name.toLowerCase() === 'default') { + fileInfo.name = '备份盘' + } else if (fileInfo.name.toLowerCase() === 'resource') { + fileInfo.name = '资源盘' + } else if (fileInfo.name.toLowerCase() === 'alibum') { + fileInfo.name = '相册' + } else { + fileInfo.name = DecodeEncName(user_id, fileInfo).name + } + return fileInfo + } else if (AliHttp.HttpCodeBreak(resp.code)) { + return (resp.body.message || resp.body) as string + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileInfo err=' + file_id + ' ' + (resp.code || ''), resp.body) } - return undefined + return '网络错误' } - + static async ApiFileInfoByPath(user_id: string, drive_id: string, file_path: string): Promise { if (!user_id || !drive_id || !file_path) return undefined if (!file_path.startsWith('/')) file_path = '/' + file_path @@ -71,109 +133,94 @@ export default class AliFile { const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { - return resp.body as IAliFileItem - } else { - DebugLog.mSaveWarning('ApiFileInfoByPath err=' + file_path + ' ' + (resp.code || '')) + let fileInfo = resp.body as IAliFileItem + fileInfo.name = DecodeEncName(user_id, fileInfo).name + return fileInfo + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileInfoByPath err=' + file_path + ' ' + (resp.code || ''), resp.body) } return undefined } - static async ApiFileDownloadUrlOpenApi(user_id: string, drive_id: string, file_id: string, expire_sec= 14400): Promise { - if (!user_id || !drive_id || !file_id) return 'file_id错误' - const data: IDownloadUrl = { - drive_id: drive_id, - file_id: file_id, - expire_sec: 0, - url: '', - size: 0 - } - - const url = 'adrive/v1.0/openFile/getDownloadUrl' - let postData = { - "expire_sec": 36000, - "drive_id": drive_id, - "file_id": file_id - } - const resp = await AliHttp.Post(url, postData, user_id, '') - - if (AliHttp.IsSuccess(resp.code)) { - data.url = resp.body.url - data.size = resp.body.size - data.expire_sec = GetOssExpires(data.url) - console.log("ApiFileDownloadUrlOpenApi: ", data) - return data - } else if (resp.body.code == 'NotFound.FileId') { - return '文件已从网盘中彻底删除' - } else if (resp.body.code == 'ForbiddenFileInTheRecycleBin') { - return '文件已放入回收站' - } else if (resp.body.code) { - return resp.body.code as string - } else { - DebugLog.mSaveWarning('ApiFileDownloadUrl err=' + file_id + ' ' + (resp.code || '')) - } - return '网络错误' - } - static async ApiFileDownloadUrl(user_id: string, drive_id: string, file_id: string, expire_sec: number): Promise { - if (!user_id || !drive_id || !file_id) return 'file_id错误' + if (!user_id || !drive_id || !file_id) return '参数错误' const data: IDownloadUrl = { drive_id: drive_id, file_id: file_id, - expire_sec: 0, + expire_time: 0, url: '', size: 0 } - const url = 'v2/file/get_download_url' - const postData = { drive_id: drive_id, file_id: file_id, expire_sec: expire_sec } + let url = '' + // 处理OpenApi无法访问相册 + let isPic = GetDriveType(user_id, drive_id).name === 'pic' + if (!isPic) { + url = 'adrive/v1.0/openFile/getDownloadUrl' + } else { + url = 'v2/file/get_download_url' + } + const postData: any = { + drive_id: drive_id, + file_id: file_id, + expire_sec: expire_sec + } + if (isPic) { + delete postData.expire_sec + } const resp = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(resp.code)) { - data.url = resp.body.url + data.url = resp.body.cdn_url || resp.body.url data.size = resp.body.size - data.expire_sec = GetOssExpires(data.url) + data.expire_time = GetExpiresTime(data.url) return data } else if (resp.body.code == 'NotFound.FileId') { return '文件已从网盘中彻底删除' } else if (resp.body.code == 'ForbiddenFileInTheRecycleBin') { return '文件已放入回收站' + } else if (AliHttp.HttpCodeBreak(resp.code)) { + return (resp.body.message || resp.body) as string } else if (resp.body.code) { return resp.body.code as string - } else { - DebugLog.mSaveWarning('ApiFileDownloadUrl err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileDownloadUrl err=' + file_id + ' ' + (resp.code || ''), resp.body) } return '网络错误' } - static async ApiVideoPreviewUrlOpenApi(user_id: string, drive_id: string, file_id: string): Promise { - if (!user_id || !drive_id || !file_id) return undefined - - const url = 'adrive/v1.0/openFile/getVideoPreviewPlayInfo' - const postData = { drive_id: drive_id, file_id: file_id, category: 'live_transcoding', template_id: '', get_subtitle_info: true, url_expire_sec: 14400, with_play_cursor:true } + static async ApiVideoPreviewUrl(user_id: string, drive_id: string, file_id: string): Promise { + if (!user_id || !drive_id || !file_id) return '参数错误' + let url = '' + let need_open_api = true + if (need_open_api) { + url = 'adrive/v1.0/openFile/getVideoPreviewPlayInfo' + } else { + url = 'v2/file/get_video_preview_play_info' + } + const postData = { + drive_id: drive_id, + file_id: file_id, + category: 'live_transcoding', + template_id: '', + get_subtitle_info: true, + url_expire_sec: 14400 + } const resp = await AliHttp.Post(url, postData, user_id, '') if (resp.body.code == 'VideoPreviewWaitAndRetry') { - message.warning('视频正在转码中,稍后重试') - return undefined + return '视频正在转码中,稍后重试' } if (resp.body.code == 'ExceedCapacityForbidden') { - message.warning('容量超限, 请清理超出的容量') - return undefined + return '容量超限限制播放,需要扩容或者删除不必要的文件释放空间' } - const data: IVideoPreviewUrl = { drive_id: drive_id, file_id: file_id, - expire_sec: 0, + size: 0, + expire_time: 0, width: 0, height: 0, - url: '', duration: 0, - play_cursor: 0, - urlQHD: '', - urlFHD: '', - urlHD: '', - urlSD: '', - urlLD: '', + qualities: [], subtitles: [] } if (AliHttp.IsSuccess(resp.code)) { @@ -184,30 +231,41 @@ export default class AliFile { } } const taskList = resp.body.video_preview_play_info?.live_transcoding_task_list || [] + const qualityMap: any = { + 'LD': { label: '低清', value: '480p' }, + 'SD': { label: '标清', value: '540P' }, + 'HD': { label: '高清', value: '720P' }, + 'FHD': { label: '全高清', value: '1080p' }, + 'QHD': { label: '超高清', value: '2560p' } + } for (let i = 0, maxi = taskList.length; i < maxi; i++) { - if (taskList[i].template_id && taskList[i].template_id == 'QHD' && taskList[i].status == 'finished') { - data.urlQHD = taskList[i].url - } else if (taskList[i].template_id && taskList[i].template_id == 'FHD' && taskList[i].status == 'finished') { - data.urlFHD = taskList[i].url - } else if (taskList[i].template_id && taskList[i].template_id == 'HD' && taskList[i].status == 'finished') { - data.urlHD = taskList[i].url - } else if (taskList[i].template_id && taskList[i].template_id == 'SD' && taskList[i].status == 'finished') { - data.urlSD = taskList[i].url - } else if (taskList[i].template_id && taskList[i].template_id == 'LD' && taskList[i].status == 'finished') { - data.urlLD = taskList[i].url + if (!taskList[i].url) { + continue + } + let templateId = taskList[i].template_id + if (templateId && taskList[i].status == 'finished') { + let quality = qualityMap[templateId] + data.qualities.push({ + html: quality.label + ' ' + quality.value, + quality: templateId, + height: taskList[i].template_height || 0, + width: taskList[i].template_width || 0, + label: quality.label, + value: quality.value, + url: taskList[i].url + }) } } - data.url = data.urlQHD || data.urlFHD || data.urlHD || data.urlSD || data.urlLD || '' + data.qualities = data.qualities.sort((a, b) => b.width - a.width) data.duration = Math.floor(resp.body.video_preview_play_info?.meta?.duration || 0) data.width = resp.body.video_preview_play_info?.meta?.width || 0 data.height = resp.body.video_preview_play_info?.meta?.height || 0 - data.expire_sec = GetOssExpires(data.url) - data.play_cursor = Number(resp.body.play_cursor) + data.expire_time = GetExpiresTime(data.qualities[0].url) return data - } else { - DebugLog.mSaveWarning('ApiVideoPreviewUrl err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiVideoPreviewUrl err=' + file_id + ' ' + (resp.code || ''), resp.body) } - return undefined + return '网络错误' } static async ApiListByFileInfo(user_id: string, drive_id: string, file_id: string, limit?: number): Promise { @@ -229,34 +287,35 @@ export default class AliFile { category: item.category, drive_id: item.drive_id, file_id: item.file_id, + file_extension: item.file_extension, url: item.url, - expire_sec: GetOssExpires(item.url), + expire_time: GetExpiresTime(item.url), play_cursor: Math.floor(item?.play_cursor || 0), - compilation_id: item.compilation_id, + compilation_id: item.compilation_id }) } return data - } else { - DebugLog.mSaveWarning('ApiListByFileInfo err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiListByFileInfo err=' + file_id + ' ' + (resp.code || ''), resp.body) } } - static async ApiAudioPreviewUrl(user_id: string, drive_id: string, file_id: string): Promise { - if (!user_id || !drive_id || !file_id) return undefined - + static async ApiAudioPreviewUrl(user_id: string, drive_id: string, file_id: string): Promise { + if (!user_id || !drive_id || !file_id) return '参数错误' + const url = 'v2/file/get_audio_play_info' - + const postData = { drive_id: drive_id, file_id: file_id, url_expire_sec: 14400 } const resp = await AliHttp.Post(url, postData, user_id, '') if (resp.body.code == 'AudioPreviewWaitAndRetry') { - message.warning('音频正在转码中,稍后重试') + return '音频正在转码中,稍后重试' } const data: IDownloadUrl = { drive_id: drive_id, file_id: file_id, - expire_sec: 0, + expire_time: 0, url: '', size: 0 } @@ -264,20 +323,28 @@ export default class AliFile { const template_list = resp.body.template_list || [] if (!data.url) { for (let i = 0, maxi = template_list.length; i < maxi; i++) { - if (template_list[i].template_id && template_list[i].template_id == 'HQ' && template_list[i].status == 'finished') data.url = template_list[i].url + if (template_list[i].template_id + && template_list[i].template_id == 'HQ' + && template_list[i].status == 'finished') { + data.url = template_list[i].url + } } } if (!data.url) { for (let i = 0, maxi = template_list.length; i < maxi; i++) { - if (template_list[i].template_id && template_list[i].template_id == 'LQ' && template_list[i].status == 'finished') data.url = template_list[i].url + if (template_list[i].template_id + && template_list[i].template_id == 'LQ' + && template_list[i].status == 'finished') { + data.url = template_list[i].url + } } } return data - } else { - DebugLog.mSaveWarning('ApiAudioPreviewUrl err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiAudioPreviewUrl err=' + file_id + ' ' + (resp.code || ''), resp.body) } - return undefined + return '网络错误' } static async ApiOfficePreViewUrl(user_id: string, drive_id: string, file_id: string): Promise { @@ -295,22 +362,35 @@ export default class AliFile { data.access_token = resp.body.access_token data.preview_url = resp.body.preview_url return data - } else { - DebugLog.mSaveWarning('ApiOfficePreViewUrl err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiOfficePreViewUrl err=' + file_id + ' ' + (resp.code || ''), resp.body) } return undefined } static async ApiGetFile(user_id: string, drive_id: string, file_id: string): Promise { if (!user_id || !drive_id || !file_id) return undefined - const fileItem = await AliFile.ApiFileInfoOpenApi(user_id, drive_id, file_id); - if (fileItem) { - return AliDirFileList.getFileInfo(fileItem, '') + const url = 'v2/file/get' + const postData = { + drive_id: drive_id, + file_id: file_id, + url_expire_sec: 14400, + office_thumbnail_process: 'image/resize,w_400/format,jpeg', + image_thumbnail_process: 'image/resize,w_400/format,jpeg', + image_url_process: 'image/resize,w_1920/format,jpeg', + video_thumbnail_process: 'video/snapshot,t_106000,f_jpg,ar_auto,m_fast,w_400' + } + const resp = await AliHttp.Post(url, postData, user_id, '') + + if (AliHttp.IsSuccess(resp.code)) { + return AliDirFileList.getFileInfo(user_id, resp.body as IAliFileItem, '') + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiGetFile err=' + file_id + ' ' + (resp.code || ''), resp.body) } return undefined } - + static async ApiFileGetPath(user_id: string, drive_id: string, file_id: string): Promise { if (!user_id || !drive_id || !file_id) return [] const url = 'adrive/v1/file/get_path' @@ -319,72 +399,84 @@ export default class AliFile { file_id: file_id } const resp = await AliHttp.Post(url, postData, user_id, '') - - if (AliHttp.IsSuccess(resp.code) && resp.body.items && resp.body.items.length > 0) { + const driveType = GetDriveType(user_id, drive_id) + let items = resp.body.items + if (AliHttp.IsSuccess(resp.code) && items && items.length > 0) { const list: IAliGetDirModel[] = [] - for (let i = resp.body.items.length - 1; i > 0; i--) { - const item = resp.body.items[i] - list.push({ - __v_skip: true, - drive_id: item.drive_id, - file_id: item.file_id, - parent_file_id: item.parent_file_id || '', - name: item.name, - namesearch: HanToPin(item.name), - size: item.size || 0, - time: new Date(item.updated_at).getTime(), - - description: item.description || '' - } as IAliGetDirModel) - } list.push({ __v_skip: true, drive_id: drive_id, - file_id: 'root', + album_id: '', + file_id: driveType.key, parent_file_id: '', - name: '根目录', - namesearch: HanToPin('root'), + name: driveType.title, + namesearch: HanToPin(driveType.title), size: 0, time: 0, - description: '' } as IAliGetDirModel) + for (let i = items.length - 1; i >= 0; i--) { + const item = items[i] + if (item.name === 'Default' || item.name === 'resource' || item.name === 'alibum') { + continue + } + list.push({ + __v_skip: true, + drive_id: item.drive_id, + album_id: '', + file_id: item.file_id, + parent_file_id: item.parent_file_id || '', + name: DecodeEncName(user_id, item).name, + namesearch: HanToPin(item.name), + size: item.size || 0, + time: new Date(item.updated_at).getTime(), + description: item.description || '' + } as IAliGetDirModel) + } return list - } else { - DebugLog.mSaveWarning('ApiFileGetPath err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileGetPath err=' + file_id + ' ' + (resp.code || ''), resp.body) } return [] } - + static async ApiFileGetPathString(user_id: string, drive_id: string, file_id: string, dirsplit: string): Promise { if (!user_id || !drive_id || !file_id) return '' - if (file_id == 'root') return '根目录' + if (file_id.includes('root')) { + if (file_id.startsWith('backup')) { + return '备份盘' + } else if (file_id.startsWith('resource')) { + return '资源盘' + } else if (file_id.startsWith('pic')) { + return '相册' + } + } const url = 'adrive/v1/file/get_path' const postData = { drive_id: drive_id, file_id: file_id } const resp = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(resp.code) && resp.body.items && resp.body.items.length > 0) { - const list: string[] = [] + const driveType = GetDriveType(user_id, drive_id) + const list: string[] = [driveType.title] for (let i = resp.body.items.length - 1; i >= 0; i--) { const item = resp.body.items[i] - list.push(item.name) + list.push(DecodeEncName(user_id, item).name) } return list.join(dirsplit) - } else { - DebugLog.mSaveWarning('ApiFileGetPathString err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileGetPathString err=' + file_id + ' ' + (resp.code || ''), resp.body) } return '' } - + static async ApiFileGetFolderSize(user_id: string, drive_id: string, file_id: string): Promise { if (!user_id || !drive_id || !file_id) return undefined const url = 'adrive/v1/file/get_folder_size_info' - + const postData = { drive_id: drive_id, file_id: file_id @@ -393,31 +485,31 @@ export default class AliFile { if (AliHttp.IsSuccess(resp.code)) { return resp.body as IAliGetForderSizeModel - } else { - DebugLog.mSaveWarning('ApiFileGetFolderSize err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileGetFolderSize err=' + file_id + ' ' + (resp.code || ''), resp.body) } return { size: 0, folder_count: 0, file_count: 0, reach_limit: false } } - - static async ApiFileDownText(user_id: string, drive_id: string, file_id: string, filesize: number, maxsize: number): Promise { + + static async ApiFileDownText(user_id: string, drive_id: string, file_id: string, filesize: number, maxsize: number, encType: string = '', password: string = ''): Promise { if (!user_id || !drive_id || !file_id) return '' - const downUrl = await AliFile.ApiFileDownloadUrlOpenApi(user_id, drive_id, file_id, 14400) + const downUrl = await getRawUrl(user_id, drive_id, file_id, encType, password) if (typeof downUrl == 'string') return downUrl // 原始文件大小 if (filesize === -1) filesize = downUrl.size if (maxsize === -1) maxsize = downUrl.size - const resp = await AliHttp.GetString(downUrl.url, '', filesize, maxsize) + const resp = await AliHttp.GetString(downUrl.url, '', filesize, maxsize) if (AliHttp.IsSuccess(resp.code)) { if (typeof resp.body == 'string') return resp.body return JSON.stringify(resp.body, undefined, 2) - } else { - DebugLog.mSaveWarning('ApiFileDownText err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiFileDownText err=' + file_id + ' ' + (resp.code || ''), resp.body) } return '' } - + static async ApiBiXueTuBatch(user_id: string, drive_id: string, file_id: string, duration: number, imageCount: number, imageWidth: number): Promise { if (!user_id || !drive_id || !file_id) return [] if (duration <= 0) return [] @@ -431,7 +523,12 @@ export default class AliFile { mtime += subtime if (mtime > duration) break const postData = { - body: { drive_id: drive_id, file_id: file_id, url_expire_sec: 14400, video_thumbnail_process: 'video/snapshot,t_' + mtime.toString() + '000,f_jpg,ar_auto,m_fast,w_' + imageWidth.toString() }, + body: { + drive_id: drive_id, + file_id: file_id, + url_expire_sec: 14400, + video_thumbnail_process: 'video/snapshot,t_' + mtime.toString() + '000,f_jpg,ar_auto,m_fast,w_' + imageWidth.toString() + }, headers: { 'Content-Type': 'application/json' }, id: (i.toString() + file_id).substr(0, file_id.length), method: 'POST', @@ -475,51 +572,33 @@ export default class AliFile { console.log(responses[i]) } } - } else { - DebugLog.mSaveWarning('ApiBiXueTuBatch err=' + file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiBiXueTuBatch err=' + file_id + ' ' + (resp.code || ''), resp.body) } return imgList } - static async ApiUpdateVideoTimeOpenApi(user_id: string, drive_id: string, file_id: string, play_cursor: number): Promise { + + static async ApiUpdateVideoTime(user_id: string, drive_id: string, file_id: string, play_cursor: number): Promise { if (!useSettingStore().uiAutoPlaycursorVideo) return if (!user_id || !drive_id || !file_id) return undefined - const upateCursorUrl = 'adrive/v1.0/openFile/video/updateRecord' - const postData = { - "drive_id": drive_id, - "file_id": file_id, - "play_cursor":play_cursor.toString() + let url = '' + let need_open_api = true + if (need_open_api) { + url = 'adrive/v1.0/openFile/video/updateRecord' + } else { + url = 'adrive/v2/video/update' } - const respvideo = await AliHttp.Post(upateCursorUrl, postData, user_id, '') + const postVideoData = { + drive_id: drive_id, + file_id: file_id, + play_cursor: Math.trunc(play_cursor).toString() + } + const respvideo = await AliHttp.Post(url, postVideoData, user_id, '') if (AliHttp.IsSuccess(respvideo.code)) { return respvideo.body as IAliFileItem } else { - DebugLog.mSaveWarning('ApiUpdateVideoTime2 err=' + file_id + ' ' + (respvideo.code || '')) - } - return undefined - } - - - static async ApiUpdateVideoTime(user_id: string, drive_id: string, file_id: string, play_cursor: number): Promise { - if (!useSettingStore().uiAutoPlaycursorVideo) return - if (!user_id || !drive_id || !file_id) return undefined - const resp = await AliFile.ApiFileInfoOpenApi(user_id, drive_id, file_id) - if (resp) { - const urlvideo = 'adrive/v2/video/update' - const postVideoData = { - drive_id: drive_id, - file_id: file_id, - play_cursor: play_cursor.toString(), - thumbnail: resp.thumbnail || '' - } - const respvideo = await AliHttp.Post(urlvideo, postVideoData, user_id, '') - if (AliHttp.IsSuccess(respvideo.code)) { - return respvideo.body as IAliFileItem - } else { - DebugLog.mSaveWarning('ApiUpdateVideoTime2 err=' + file_id + ' ' + (respvideo.code || '')) - } - } else { - DebugLog.mSaveWarning('ApiUpdateVideoTime err=' + file_id) + DebugLog.mSaveWarning('ApiUpdateVideoTime err=' + file_id + ' ' + (respvideo.code || ''), respvideo.body) } return undefined } diff --git a/aliyunpan/src/aliapi/filecmd.ts b/aliyunpan/src/aliapi/filecmd.ts index ff55e857e1..8a7d8fd84d 100644 --- a/aliyunpan/src/aliapi/filecmd.ts +++ b/aliyunpan/src/aliapi/filecmd.ts @@ -1,57 +1,44 @@ import DebugLog from '../utils/debuglog' -import message from '../utils/message' import AliHttp from './alihttp' import { IAliFileItem, IAliGetFileModel } from './alimodels' import AliDirFileList from './dirfilelist' -import { ApiBatch, ApiBatchMaker, ApiBatchMaker2, ApiBatchSuccess } from './utils' -import AliFile from "./file"; -import path from "path"; +import { ApiBatch, ApiBatchMaker, ApiBatchMaker2, ApiBatchSuccess, EncodeEncName } from './utils' +import { IDownloadUrl } from './models' +import AliFile from './file' +import message from '../utils/message' export default class AliFileCmd { - - static async ApiCreatNewForder(user_id: string, drive_id: string, parent_file_id: string, creatDirName: string): Promise<{ file_id: string; error: string }> { + static async ApiCreatNewForder( + user_id: string, drive_id: string, + parent_file_id: string, creatDirName: string, + encType: string = '', check_name_mode: string = 'refuse' + ): Promise<{ file_id: string; error: string }> { const result = { file_id: '', error: '新建文件夹失败' } if (!user_id || !drive_id || !parent_file_id) return result - const url = 'adrive/v1.0/openFile/create' - const pathSplitor = creatDirName.split(path.sep); - if (pathSplitor.length > 1) { - let parentFileId = parent_file_id; - for (let i = 0; i < pathSplitor.length; i++) { - const folderName = pathSplitor[i]; - const resp = await AliFileCmd.ApiCreatNewForder(user_id, drive_id, parentFileId, folderName); - parentFileId = resp.file_id - } - return { file_id:parentFileId, error: '' } - } else if (path.sep === '\\' && creatDirName.split('/').length > 1) { - let parentFileId = parent_file_id; - for (let i = 0; i < creatDirName.split('/').length; i++) { - const folderName = creatDirName.split('/')[i]; - const resp = await AliFileCmd.ApiCreatNewForder(user_id, drive_id, parentFileId, folderName); - parentFileId = resp.file_id - } - return { file_id:parentFileId, error: '' } - } - + if (parent_file_id.includes('root')) parent_file_id = 'root' + const url = 'adrive/v2/file/createWithFolders' + const name = EncodeEncName(user_id, creatDirName, true, encType) const postData = JSON.stringify({ drive_id: drive_id, parent_file_id: parent_file_id, - name: creatDirName, - check_name_mode: 'refuse', - type: 'folder' + name: name, + check_name_mode: check_name_mode, + type: 'folder', + description: encType }) const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { const file_id = resp.body.file_id as string | undefined if (file_id) return { file_id, error: '' } - } else { - DebugLog.mSaveWarning('ApiCreatNewForder err=' + parent_file_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiCreatNewForder err=' + parent_file_id + ' ' + (resp.code || ''), resp.body) } if (resp.body?.code == 'QuotaExhausted.Drive') return { file_id: '', error: '网盘空间已满,无法创建' } if (resp.body?.code) return { file_id: '', error: resp.body?.code } return result } - + static async ApiTrashBatch(user_id: string, drive_id: string, file_idList: string[]): Promise { const batchList = ApiBatchMaker('/recyclebin/trash', file_idList, (file_id: string) => { return { drive_id: drive_id, file_id: file_id } @@ -59,7 +46,7 @@ export default class AliFileCmd { return ApiBatchSuccess('放入回收站', batchList, user_id, '') } - + static async ApiDeleteBatch(user_id: string, drive_id: string, file_idList: string[]): Promise { const batchList = ApiBatchMaker('/file/delete', file_idList, (file_id: string) => { return { drive_id: drive_id, file_id: file_id } @@ -67,8 +54,13 @@ export default class AliFileCmd { return ApiBatchSuccess('彻底删除', batchList, user_id, '') } - - static async ApiRenameBatch(user_id: string, drive_id: string, file_idList: string[], names: string[]): Promise<{ file_id: string; parent_file_id: string; name: string; isDir: boolean }[]> { + + static async ApiRenameBatch(user_id: string, drive_id: string, file_idList: string[], names: string[]): Promise<{ + file_id: string; + parent_file_id: string; + name: string; + isDir: boolean + }[]> { const batchList = ApiBatchMaker2('/file/update', file_idList, names, (file_id: string, name: string) => { return { drive_id: drive_id, file_id: file_id, name: name, check_name_mode: 'refuse' } }) @@ -76,11 +68,16 @@ export default class AliFileCmd { if (batchList.length == 0) return Promise.resolve([]) const successList: { file_id: string; parent_file_id: string; name: string; isDir: boolean }[] = [] const result = await ApiBatch(file_idList.length <= 1 ? '' : '批量重命名', batchList, user_id, '') - result.reslut.map((t) => successList.push({ file_id: t.file_id!, name: t.name!, parent_file_id: t.parent_file_id!, isDir: t.type !== 'folder' })) + result.reslut.map((t) => successList.push({ + file_id: t.file_id!, + name: t.name!, + parent_file_id: t.parent_file_id!, + isDir: t.type !== 'folder' + })) return successList } - + static async ApiFavorBatch(user_id: string, drive_id: string, isfavor: boolean, ismessage: boolean, file_idList: string[]): Promise { const batchList = ApiBatchMaker('/file/update', file_idList, (file_id: string) => { return { drive_id: drive_id, file_id: file_id, custom_index_key: isfavor ? 'starred_yes' : '', starred: isfavor } @@ -88,7 +85,7 @@ export default class AliFileCmd { return ApiBatchSuccess(ismessage ? (isfavor ? '收藏文件' : '取消收藏') : '', batchList, user_id, '') } - + static async ApiTrashCleanBatch(user_id: string, drive_id: string, ismessage: boolean, file_idList: string[]): Promise { const batchList = ApiBatchMaker('/file/delete', file_idList, (file_id: string) => { return { drive_id: drive_id, file_id: file_id } @@ -96,7 +93,7 @@ export default class AliFileCmd { return ApiBatchSuccess(ismessage ? '从回收站删除' : '', batchList, user_id, '') } - + static async ApiTrashRestoreBatch(user_id: string, drive_id: string, ismessage: boolean, file_idList: string[]): Promise { const batchList = ApiBatchMaker('/recyclebin/restore', file_idList, (file_id: string) => { return { drive_id: drive_id, file_id: file_id } @@ -104,16 +101,55 @@ export default class AliFileCmd { return ApiBatchSuccess(ismessage ? '从回收站还原' : '', batchList, user_id, '') } - - static async ApiFileColorBatch(user_id: string, drive_id: string, color: string, file_idList: string[]): Promise { - const batchList = ApiBatchMaker('/file/update', file_idList, (file_id: string) => { - return { drive_id: drive_id, file_id: file_id, description: color } - }) - return ApiBatchSuccess(color == '' ? '清除文件标记' : color == 'c5b89b8' ? '' : '标记文件', batchList, user_id, '') + static async ApiFileHistoryBatch(user_id: string, drive_id: string, file_idList: string[]) { + let allTask = [] + const loadingKey = 'filehistorybatch' + Date.now().toString() + message.loading('清除历史 执行中...', 60, loadingKey) + for (const file_id of file_idList) { + allTask.push(AliFile.ApiUpdateVideoTime(user_id, drive_id, file_id, 0)) + if (allTask.length >= 3) { + await Promise.all(allTask).catch() + allTask = [] + } + } + if (allTask.length > 0) { + await Promise.all(allTask).catch() + } + message.success('成功执行 清除历史', 1, loadingKey) } - - static async ApiRecoverBatch(user_id: string, resumeList: { drive_id: string; file_id: string; content_hash: string; size: number; name: string }[]): Promise { + static async ApiFileColorBatch(user_id: string, drive_id: string, description: string, color: string, file_idList: string[]): Promise { + // 防止加密标记清空 + if (color && color != 'notEncrypt') { + let parts = description.split(',') || [] + let encryptPart = parts.find((part: any) => part.includes('xbyEncrypt')) || '' + let colorPart = color || parts.find((part: any) => /c.{6}$/.test(part)) || '' + color = color ? [encryptPart, colorPart].filter(Boolean).join(',') : encryptPart + } else { + color = '' + } + let batchList = ApiBatchMaker('/file/update', file_idList, (file_id: string) => { + return { drive_id: drive_id, file_id: file_id, description: color } + }) + let title = '' + if (color == '') { + title = '清除标记' + } else if (color.includes('ce74c3c')) { + title = '' + } else if (color.includes('xbyEncrypt')) { + title = '标记加密' + } + return ApiBatchSuccess(title, batchList, user_id, '') + } + + + static async ApiRecoverBatch(user_id: string, resumeList: { + drive_id: string; + file_id: string; + content_hash: string; + size: number; + name: string + }[]): Promise { const successList: string[] = [] if (!resumeList || resumeList.length == 0) return Promise.resolve(successList) @@ -127,7 +163,7 @@ export default class AliFileCmd { const url2 = 'adrive/v1/file/checkResumeTask' const resp2 = await AliHttp.Post(url2, { task_id }, user_id, '') if (AliHttp.IsSuccess(resp2.code)) { - + if (resp2.body.state == 'running') continue if (resp2.body.state == 'done') { const results = resp2.body.results as any[] @@ -137,60 +173,63 @@ export default class AliFileCmd { return true }) } - return successList + return successList } } } } else if (resp.code && resp.code == 403) { - if (resp.body?.code == 'UserNotVip') message.error('文件恢复功能需要开通阿里云盘会员') - else message.error(resp.body?.code || '拒绝访问') - } else { - DebugLog.mSaveWarning('ApiRecoverBatch err=' + (resp.code || '')) - message.error('操作失败') + if (resp.body?.code == 'UserNotVip') return '文件恢复功能需要开通阿里云盘会员' + else return resp.body?.code || '拒绝访问' + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiRecoverBatch err=' + (resp.code || ''), resp.body) + return '操作失败' } return successList } - + static async ApiMoveBatch(user_id: string, drive_id: string, file_idList: string[], to_drive_id: string, to_parent_file_id: string): Promise { + if (to_parent_file_id.includes('root')) to_parent_file_id = 'root' const batchList = ApiBatchMaker('/file/move', file_idList, (file_id: string) => { - if (drive_id == to_drive_id) return { drive_id: drive_id, file_id: file_id, to_parent_file_id: to_parent_file_id, auto_rename: true } - else return { drive_id: drive_id, file_id: file_id, to_drive_id: to_drive_id, to_parent_file_id: to_parent_file_id, auto_rename: true } + if (drive_id == to_drive_id) return { + drive_id: drive_id, + file_id: file_id, + to_parent_file_id: to_parent_file_id, + auto_rename: true + } + else return { + drive_id: drive_id, + file_id: file_id, + to_drive_id: to_drive_id, + to_parent_file_id: to_parent_file_id, + auto_rename: true + } }) return ApiBatchSuccess(file_idList.length <= 1 ? '移动' : '批量移动', batchList, user_id, '') } - + static async ApiCopyBatch(user_id: string, drive_id: string, file_idList: string[], to_drive_id: string, to_parent_file_id: string): Promise { + if (to_parent_file_id.includes('root')) to_parent_file_id = 'root' const batchList = ApiBatchMaker('/file/copy', file_idList, (file_id: string) => { - if (drive_id == to_drive_id) return { drive_id: drive_id, file_id: file_id, to_parent_file_id: to_parent_file_id, auto_rename: true } - else return { drive_id: drive_id, file_id: file_id, to_drive_id: to_drive_id, to_parent_file_id: to_parent_file_id, auto_rename: true } + if (drive_id == to_drive_id) return { + drive_id: drive_id, + file_id: file_id, + to_parent_file_id: to_parent_file_id, + auto_rename: true + } + else return { + drive_id: drive_id, + file_id: file_id, + to_drive_id: to_drive_id, + to_parent_file_id: to_parent_file_id, + auto_rename: true + } }) return ApiBatchSuccess(file_idList.length <= 1 ? '复制' : '批量复制', batchList, user_id, '') } - static async ApiGetFileBatchOpenApi(user_id: string, drive_id: string, file_idList: string[]): Promise { - const url = "adrive/v1.0/openFile/batch/get" - const file_list: { file_id: string, drive_id: string}[] = [] - file_idList.map((file_id) => { - file_list.push({drive_id, file_id}) - }) - const postData = { - file_list: file_list - } - const successList: IAliGetFileModel[] = [] - const result = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(result.code)) { - const fileItems = result.body.items as IAliFileItem[] - fileItems.forEach((fileItem) => { - successList.push(AliDirFileList.getFileInfo(fileItem, 'download_url')) - }) - } - return successList - } - - static async ApiGetFileBatch(user_id: string, drive_id: string, file_idList: string[]): Promise { const batchList = ApiBatchMaker('/file/get', file_idList, (file_id: string) => { return { @@ -205,21 +244,25 @@ export default class AliFileCmd { }) const successList: IAliGetFileModel[] = [] const result = await ApiBatch('', batchList, user_id, '') - result.reslut.map(async (t) => { - if (t.body) { - const fileItem = t.body as IAliFileItem - if (fileItem.type === 'file') { - const fileDetails = await AliFile.ApiFileInfoOpenApi(user_id, drive_id, fileItem.file_id); - fileItem.thumbnail = fileDetails?.thumbnail - fileItem.url = fileDetails?.url || fileItem.url - fileItem.download_url = fileDetails?.download_url || fileItem.download_url - fileItem.starred = fileDetails?.starred || fileItem.starred - fileItem.trashed = fileDetails?.trashed || fileItem.trashed - fileItem.deleted = fileDetails?.deleted || fileItem.deleted - fileItem.description = fileDetails?.description || fileItem.description - } - successList.push(AliDirFileList.getFileInfo(t.body as IAliFileItem, 'download_url')) + result.reslut.map((t) => { + if (t.body) successList.push(AliDirFileList.getFileInfo(user_id, t.body as IAliFileItem, 'download_url')) + return true + }) + return successList + } + + static async ApiGetFileDownloadUrlBatch(user_id: string, drive_id: string, file_idList: string[]): Promise { + const batchList = ApiBatchMaker('/file/get_download_url', file_idList, (file_id: string) => { + return { + drive_id: drive_id, + file_id: file_id, + expire_sec: 14400 } + }) + const successList: IDownloadUrl[] = [] + const result = await ApiBatch('', batchList, user_id, '') + result.reslut.map((t) => { + if (t.body) successList.push(t.body as IDownloadUrl) return true }) return successList diff --git a/aliyunpan/src/aliapi/fileicon.ts b/aliyunpan/src/aliapi/fileicon.ts index 65481557d0..1b08c9fa74 100644 --- a/aliyunpan/src/aliapi/fileicon.ts +++ b/aliyunpan/src/aliapi/fileicon.ts @@ -52,11 +52,11 @@ export default function getFileIcon(category: string | undefined, ext: string | if (';.apng.avif.ico.webp.gif.'.indexOf(ext) > 0) { - return ['image2', 'iconfile-img'] + return ['image2', 'iconfile-img'] } if (category == 'image') { - return ['image', 'iconfile-img'] + return ['image', 'iconfile-img'] } if (mime.startsWith('image/')) return ['image3', 'iconfile-image'] @@ -66,28 +66,24 @@ export default function getFileIcon(category: string | undefined, ext: string | if (';.pot.ett.'.indexOf(ext) > 0) return ['doc2', 'iconfile-doc'] 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'] if ((mimext.startsWith('.txt') || mimext.startsWith('.xls')) && ';.xls.xlsx.et.xlsm.xlt.xltm.xltx.'.indexOf(ext) > 0) return ['doc', 'iconfile-xsl'] - - if (mime.startsWith('text/')) return ['others', 'iconfile_txt2'] + if (mime.startsWith('text/')) return ['others', 'iconfile_txt2'] if (ext == '.json.') return ['others', 'iconfile_txt2'] - if (category == 'video') { - return ['video', 'iconfile_video'] } - if (mime.startsWith('video/')) return ['video2', 'iconfile_video'] if (ext == '.ts.' && size > 5 * 1024 * 1024) return ['video2', 'iconfile_video'] 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) { return ['video2', 'iconfile_video'] } if (ext == '.mp3.' && category == 'audio') return ['audio', 'iconfile-mp3'] if (category == 'audio' && mimext != '.unknown.') { - return ['audio', 'iconfile-audio'] } if (mime.startsWith('audio/')) return ['audio', 'iconfile-audio'] if (';.ape.aac.cda.dsf.dtshd.eac3.m1a.m2a.m4a.mka.mpa.mpc.opus.ra.tak.tta.wma.wv.'.indexOf(ext) > 0) { return ['audio2', 'iconfile-audio'] } + if (mime.startsWith('video/')) return ['video2', 'iconfile_video'] return ['others', 'iconwenjian'] } diff --git a/aliyunpan/src/aliapi/filewalk.ts b/aliyunpan/src/aliapi/filewalk.ts index c049657e8e..ca7a27a9bb 100644 --- a/aliyunpan/src/aliapi/filewalk.ts +++ b/aliyunpan/src/aliapi/filewalk.ts @@ -20,7 +20,7 @@ export default class AliFileWalk { const orders = order.split(' ') do { const isGet = await AliFileWalk._ApiWalkFileListOnePage(orders[0], orders[1], dir, type) - if (isGet != true) { + if (!isGet) { break } if (dir.items.length >= max && max > 0) { @@ -32,7 +32,7 @@ export default class AliFileWalk { } private static async _ApiWalkFileListOnePage(orderby: string, order: string, dir: IAliFileResp, type: string = '') { - 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)' + const url = 'v2/file/walk?jsonmask=next_marker%2Citems(category%2Ccreated_at%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription)' let postData = { drive_id: dir.m_drive_id, parent_file_id: dir.dirID, @@ -54,12 +54,12 @@ export default class AliFileWalk { if (AliHttp.IsSuccess(resp.code)) { dir.next_marker = resp.body.next_marker const isRecover = dir.dirID == 'recover' - const downUrl = isRecover ? '' : 'https://api.aliyundrive.com/v2/file/download?t=' + Date.now().toString() + const downUrl = isRecover ? '' : 'https://api.alipan.com/v2/file/download?t=' + Date.now().toString() for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { const item = resp.body.items[i] as IAliFileItem if (dir.itemsKey.has(item.file_id)) continue - const add = AliDirFileList.getFileInfo(item, downUrl) + const add = AliDirFileList.getFileInfo(dir.m_user_id, item, downUrl) if (isRecover) add.description = item.content_hash dir.items.push(add) dir.itemsKey.add(item.file_id) @@ -74,11 +74,11 @@ export default class AliFileWalk { return true } else if (resp.body && resp.body.code) { dir.items.length = 0 - dir.next_marker = resp.body.code + dir.next_marker = resp.body.code // message.warning('列出文件出错 ' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_FileListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_FileListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_FileListOnePage ' + dir.dirID, err) diff --git a/aliyunpan/src/aliapi/following.ts b/aliyunpan/src/aliapi/following.ts index 1e17bdbc98..116bda9bf0 100644 --- a/aliyunpan/src/aliapi/following.ts +++ b/aliyunpan/src/aliapi/following.ts @@ -2,7 +2,7 @@ import { humanTimeAgo } from '../utils/format' import message from '../utils/message' import DebugLog from '../utils/debuglog' import AliHttp, { IUrlRespData } from './alihttp' -import { IAliOtherFollowingModel, IAliMyFollowingModel } from './alimodels' +import { IAliMyFollowingModel, IAliOtherFollowingModel } from './alimodels' export interface IAliOtherFollowingResp { items: IAliOtherFollowingModel[] @@ -77,8 +77,8 @@ export default class AliFollowing { dir.next_marker = resp.body.code message.warning('列出官方推荐列表出错' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_OtherFollowingListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_OtherFollowingListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_OtherFollowingListOnePage', err) @@ -101,8 +101,8 @@ export default class AliFollowing { do { const isGet = await AliFollowing.ApiMyFollowingListOnePage(dir) - if (isGet != true) { - break + if (!isGet) { + break } } while (dir.next_marker) return dir @@ -165,8 +165,8 @@ export default class AliFollowing { dir.next_marker = resp.body.code message.warning('列出订阅列表出错' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_MyFollowingListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_MyFollowingListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_MyFollowingListOnePage', err) @@ -184,8 +184,8 @@ export default class AliFollowing { const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { if (tip) message.success(isFollowing ? '订阅成功' : '取消订阅成功') - } else { - DebugLog.mSaveWarning('ApiSetFollowing err=' + followingid + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiSetFollowing err=' + followingid + ' ' + (resp.code || ''), resp.body) message.error((isFollowing ? '订阅' : '取消订阅') + ' 操作失败,请稍后重试') } } @@ -198,10 +198,10 @@ export default class AliFollowing { const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { return true - } else { - DebugLog.mSaveWarning('ApiSetFollowingMarkRead err=' + followingid + ' ' + (resp.code || '')) - return false + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiSetFollowingMarkRead err=' + followingid + ' ' + (resp.code || ''), resp.body) } + return false } diff --git a/aliyunpan/src/aliapi/models.ts b/aliyunpan/src/aliapi/models.ts index ccc776a951..93594e57c0 100644 --- a/aliyunpan/src/aliapi/models.ts +++ b/aliyunpan/src/aliapi/models.ts @@ -1,12 +1,35 @@ - export interface IDownloadUrl { drive_id: string file_id: string - expire_sec: number + expire_time: number url: string size: number } + +export interface IVideoPreviewUrl { + drive_id: string + file_id: string + size: number + duration: number + expire_time: number + width: number + height: number + qualities: { + html: string + quality: string + height: number + width: number + label: string + value: string + url: string + }[] + subtitles: { + language: string + url: string + }[] +} + export interface ICompilationList { name: string type: string @@ -16,36 +39,14 @@ export interface ICompilationList { category: string drive_id: string file_id: string + file_extension: string url: string - expire_sec: number + expire_time: number play_cursor: number compilation_id: string } -export interface IVideoPreviewUrl { - drive_id: string - file_id: string - expire_sec: number - url: string - duration: number - width: number - height: number - urlQHD: string - play_cursor: number - - urlFHD: string - urlHD: string - urlSD: string - urlLD: string - file_name?:string, - subtitles: { - language: string - url: string - }[] -} - - export interface IOfficePreViewUrl { drive_id: string file_id: string @@ -102,17 +103,17 @@ export interface IAliBatchResult { reslut: { id: string file_id?: string - + name?: string type?: string parent_file_id?: string - + share_id?: string share_pwd?: string share_url?: string expiration?: string share_name?: string - + body?: any }[] error: { @@ -143,15 +144,15 @@ export interface IBatchResult { export interface IAliGetAlbumModel { - album_id: string - created_at: number - description: string - file_count: number - image_count: number - name: string - owner: string - updated_at: number - video_count: number + album_id: string + created_at: number + description: string + file_count: number + image_count: number + name: string + owner: string + updated_at: number + video_count: number } export interface IAliUserDriveDetails { @@ -167,12 +168,12 @@ export interface IAliUserDriveDetails { } export interface IAliUserDriveCapacity { - type: string + type: string size: number sizeStr: string - expired: string - expiredstr: string - description: string + expired: string + expiredstr: string + description: string latest_receive_time: string /* "2022-05-02T00:50:51.379Z" */ } @@ -181,29 +182,29 @@ export interface IStateUploadFile { UploadID: string Info: { user_id: string - + localFilePath: string - + parent_file_id: string drive_id: string path: string - + name: string - + size: number sizeStr: string icon: string isDir: boolean isMiaoChuan: boolean - + sha1: string - + crc64: string } - + Upload: { - + DownState: string DownTime: number DownSize: number @@ -218,11 +219,11 @@ export interface IStateUploadFile { failedCode: number /** 失败的消息 */ failedMessage: string - + AutoTry: number - + upload_id: string - + file_id: string /** 是否覆盖上传 */ IsBreakExist: boolean diff --git a/aliyunpan/src/aliapi/server.ts b/aliyunpan/src/aliapi/server.ts new file mode 100644 index 0000000000..d1e86ed2ae --- /dev/null +++ b/aliyunpan/src/aliapi/server.ts @@ -0,0 +1,256 @@ +import { b64decode, Sleep } from '../utils/format' +import { getPkgVersion } from '../utils/utils' +import axios, { AxiosResponse } from 'axios' +import { IShareSiteGroupModel, IShareSiteModel, useServerStore, useSettingStore, useUserStore } from '../store' +import ShareDAL from '../share/share/ShareDAL' +import { modalShowPost, modalUpdate } from '../utils/modal' +import { getResourcesPath } from '../utils/electronhelper' +import { existsSync, readFileSync } from 'fs' +import message from '../utils/message' +import path from 'path' +import DebugLog from '../utils/debuglog' + +export interface IServerRespData { + state: string + msg: string + + [k: string]: any +} + +export interface IServerVerData { + version: string + verName: string + verUrl: string + verInfo: string + verHtml: string + fileExt: string + fileSize: number +} + +export default class ServerHttp { + static baseApi = b64decode('aHR0cDovLzEyMS41LjE0NC44NDo1MjgyLw==') + static configUrl = b64decode('aHR0cHM6Ly9naXRlZS5jb20vemhhbm5hby9yZXNvdXJjZS9yYXcvbWFzdGVyL2ltYWdlcy9zaGFyZV9jb25maWcuanNvbg==') + static updateUrl = b64decode('aHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9nYW96aGFuZ21pbi9hbGl5dW5wYW4vcmVsZWFzZXMvbGF0ZXN0') + + static compareVer(version1: string, version2: string): number { + // Split version strings into arrays of numbers + const v1Parts = version1.split('.').map(Number) + const v2Parts = version2.split('.').map(Number) + + // Pad the shorter version with zeros to make their lengths equal + const maxLength = Math.max(v1Parts.length, v2Parts.length) + v1Parts.push(...Array(maxLength - v1Parts.length).fill(0)) + v2Parts.push(...Array(maxLength - v2Parts.length).fill(0)) + + // Compare each part of the version numbers + for (let i = 0; i < maxLength; i++) { + if (v1Parts[i] > v2Parts[i]) { + return 1 + } else if (v1Parts[i] < v2Parts[i]) { + return -1 + } + } + + // Version numbers are equal + return 0 + } + + static compareVersions(version1: string, version2: string): number { + // Split version strings into arrays of numbers + const v1Parts = version1.split('.').map(Number); + const v2Parts = version2.split('.').map(Number); + + // Pad the shorter version with zeros to make their lengths equal + const maxLength = Math.max(v1Parts.length, v2Parts.length); + v1Parts.push(...Array(maxLength - v1Parts.length).fill(0)); + v2Parts.push(...Array(maxLength - v2Parts.length).fill(0)); + + // Compare each part of the version numbers + for (let i = 0; i < maxLength; i++) { + if (v1Parts[i] > v2Parts[i]) { + return 1; + } else if (v1Parts[i] < v2Parts[i]) { + return -1; + } + } + + // Version numbers are equal + return 0; + } + + + + static async Post(postData: any, isfirst = true): Promise { + const url = ServerHttp.baseApi + 'xby2' + return axios + .post(url, postData, { + responseType: 'arraybuffer', + timeout: 30000, + headers: {} + }) + .then((response: AxiosResponse) => { + if (response.status != 200) return { state: 'error', msg: '网络错误' } + const buff = response.data as ArrayBuffer + const uint8array = new Uint8Array(buff) + for (let i = 0, maxi = uint8array.byteLength; i < maxi; i++) { + uint8array[i] ^= 9 + (i % 200) + } + const str = new TextDecoder().decode(uint8array) + return JSON.parse(str) as IServerRespData + }) + .catch(() => { + return { state: 'error', msg: '网络错误' } + }) + .then(async (resp) => { + if (resp.state == 'error' && resp.msg == '网络错误' && isfirst) { + await Sleep(2000) + return await ServerHttp.Post(postData, false) + } else return resp + }) + } + + + static async PostToServer(postData: any): Promise { + postData.appVersion = getPkgVersion() + const str = JSON.stringify(postData) + if (window.postdataFunc) { + let enstr = '' + try { + enstr = window.postdataFunc(str) + console.log(enstr) + } catch { + return { state: 'error', msg: '联网失败' } + } + return ServerHttp.Post(enstr).catch(() => { + return { state: 'error', msg: '网络错误' } + }) + } else { + return { state: 'error', msg: '程序错误' } + } + } + + static async CheckConfigUpgrade(): Promise { + axios + .get(ServerHttp.configUrl, { + withCredentials: false, + responseType: 'json', + timeout: 30000 + }) + .then(async (response: AxiosResponse) => { + console.log('CheckConfigUpgrade', response) + let GroupList: IShareSiteGroupModel[] = [] + if (response.data.GroupList && response.data.GroupList.length > 0) { + const list = response.data.GroupList + for (let item of list) { + GroupList.push({ group: item.group, title: item.title }) + } + } else { + GroupList = [ + { group: 'search', title: '搜索' }, + { group: 'nav', title: '导航' }, + { group: 'bbs', title: '论坛' } + ] + } + ShareDAL.SaveShareSiteGroup(GroupList) + if (response.data.SSList && response.data.SSList.length > 0) { + const list: IShareSiteModel[] = [] + const SSList = response.data.SSList + for (let item of SSList) { + const add: any = { + title: item.title, + url: item.url, + tip: item.tip, + group: item.group, + color: item.color + } + if (add.url.length > 0) list.push(add) + } + ShareDAL.SaveShareSite(list) + } + if (response.data.HELP && response.data.HELP.length > 0) { + useServerStore().mSaveHelpUrl(response.data.HELP) + } + if (response.data.POST && response.data.POST.length > 0) { + let postId = localStorage.getItem('postmodal') + if (!postId || postId != response.data.POST_ID) { + modalShowPost(response.data.POST, response.data.POST_ID) + } + } + }).catch((err: any) => { + DebugLog.mSaveDanger('CheckConfigUpgrade', err) + }) + } + + static async CheckUpgrade(showMessage: boolean = true): Promise { + axios + .get(ServerHttp.updateUrl, { + withCredentials: false, + responseType: 'json', + timeout: 30000 + }) + .then(async (response: AxiosResponse) => { + console.log('CheckUpgrade', response) + if (!response.data || !response.data.assets || !response.data.html_url) { + showMessage && message.error('获取新版本出错') + return + } + let tagName = response.data.tag_name // 版本号 + let remoteVer = tagName.replaceAll('v', '').trim() + let verHtml = response.data.html_url // 详情 + let verInfo = response.data.body // 日志 + let verData: IServerVerData = { + version: remoteVer, + verName: '', + verUrl: '', + verInfo: verInfo, + verHtml: verHtml, + fileExt: '', + fileSize: 0 + } + let assets = response.data.assets // 文件 + function isMatchingPlatformAndExtension(platform: string, fileName: string, extension: string): boolean { + return platform === window.platform && fileName.indexOf(process.arch) > 0 && fileName.endsWith(extension) + } + + for (let asset of assets) { + if (asset.name.endsWith('.asar')) { + verData.fileSize = asset.size + verData.fileExt = path.extname(asset.name) + verData.verUrl = asset.browser_download_url + verData.verName = asset.name + break + } + if (isMatchingPlatformAndExtension('win32', asset.name, '.exe') || + isMatchingPlatformAndExtension('darwin', asset.name, '.dmg')) { + verData.fileSize = asset.size + verData.fileExt = path.extname(asset.name) + verData.verUrl = asset.browser_download_url + verData.verName = asset.name + } + } + if (remoteVer) { + let configVer = getPkgVersion().replaceAll('v', '').trim() + if (window.platform !== 'linux') { + let localVersion = getResourcesPath('localVersion') + if (localVersion && existsSync(localVersion)) { + configVer = readFileSync(localVersion, 'utf-8').replaceAll('v', '').trim() + } + } + if (useSettingStore().uiUpdateProxyEnable && + useSettingStore().uiUpdateProxyUrl.length > 0) { + verData.verUrl = useSettingStore().uiUpdateProxyUrl + '/' + verData.verUrl + } + if (this.compareVersions(remoteVer, configVer) > 0) { + // 打开更新弹窗 + modalUpdate(verData) + } else if (showMessage) { + message.info('已经是最新版 ' + configVer, 6) + } + } + }) + .catch((err: any) => { + showMessage && message.info('检查更新失败,请检查网络是否正常') + // DebugLog.mSaveDanger('CheckUpgrade', err) + }) + } +} diff --git a/aliyunpan/src/aliapi/server.tsx b/aliyunpan/src/aliapi/server.tsx deleted file mode 100644 index fd98ff911e..0000000000 --- a/aliyunpan/src/aliapi/server.tsx +++ /dev/null @@ -1,410 +0,0 @@ -import { B64decode, b64decode, humanSize } from '../utils/format' -import { getPkgVersion } from '../utils/utils' -import axios, { AxiosResponse } from 'axios' -import message from '../utils/message' -import { IShareSiteModel, useServerStore } from '../store' -import { Modal, Button, Space } from '@arco-design/web-vue' -import { h } from 'vue' -import { getAppNewPath, getResourcesPath, getUserDataPath, openExternal } from '../utils/electronhelper' -import ShareDAL from '../share/share/ShareDAL' -import DebugLog from '../utils/debuglog' -import { writeFile, rmSync, existsSync, readFileSync } from 'fs' -import { execFile, SpawnOptions } from 'child_process' -import path from 'path' - -const { shell } = require('electron') - -export interface IServerRespData { - state: string - msg: string - - [k: string]: any -} - -export default class ServerHttp { - static baseApi = b64decode('aHR0cDovLzEyMS41LjE0NC44NDo1MjgyLw==') - - static async PostToServer(postData: any): Promise { - postData.appVersion = getPkgVersion() - const str = JSON.stringify(postData) - if (window.postdataFunc) { - let enstr = '' - try { - enstr = window.postdataFunc(str) - console.log(enstr) - } catch { - return { state: 'error', msg: '联网失败' } - } - return ServerHttp.Post(enstr).catch(() => { - return { state: 'error', msg: '网络错误' } - }) - } else { - return { state: 'error', msg: '程序错误' } - } - } - - static async Post(postData: any, isfirst = true): Promise { - const url = ServerHttp.baseApi + 'xby2' - return axios - .post(url, postData, { - responseType: 'arraybuffer', - timeout: 30000, - headers: {} - }) - .then((response: AxiosResponse) => { - if (response.status != 200) return { state: 'error', msg: '网络错误' } - const buff = response.data as ArrayBuffer - const uint8array = new Uint8Array(buff) - for (let i = 0, maxi = uint8array.byteLength; i < maxi; i++) { - uint8array[i] ^= 9 + (i % 200) - } - const str = new TextDecoder().decode(uint8array) - return JSON.parse(str) as IServerRespData - }) - .catch(() => { - return { state: 'error', msg: '网络错误' } - }) - .then((resp) => { - if (resp.state == 'error' && resp.msg == '网络错误' && isfirst) { - return ServerHttp.Sleep(2000).then(() => { - return ServerHttp.Post(postData, false) - }) - } else return resp - }) - } - - static Sleep(msTime: number) { - return new Promise((resolve) => - setTimeout( - () => - resolve({ - success: true, - time: msTime - }), - msTime - ) - ) - } - - static configUrl = b64decode('aHR0cHM6Ly9naXRlZS5jb20vemhhbm5hby9yZXNvdXJjZS9yYXcvbWFzdGVyL2ltYWdlcy9zaGFyZV9jb25maWcuanNvbg==') - static updateUrl = b64decode('aHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9nYW96aGFuZ21pbi9hbGl5dW5wYW4vcmVsZWFzZXMvbGF0ZXN0') - - static async CheckConfigUpgrade(): Promise { - axios - .get(ServerHttp.configUrl, { - withCredentials: false, - responseType: 'json', - timeout: 30000 - }) - .then(async (response: AxiosResponse) => { - const content = B64decode(response.data.content) - const config = JSON.parse(content) - if (config.SIP) { - ServerHttp.baseApi = B64decode(config.SIP) - } - if (config.SSList) { - const list: IShareSiteModel[] = [] - for (let i = 0, maxi = config.SSList.length; i < maxi; i++) { - const item = config.SSList[i] - const add = { title: item.title, url: item.url, tip: item.tip } - if (add.url.length > 0) list.push(add) - } - ShareDAL.SaveShareSite(list) - } - if (config.HELP) { - useServerStore().mSaveHelpUrl(response.data.HELP) - } - }) - } - - static compareVersions(version1: string, version2: string): number { - // Split version strings into arrays of numbers - const v1Parts = version1.split('.').map(Number); - const v2Parts = version2.split('.').map(Number); - - // Pad the shorter version with zeros to make their lengths equal - const maxLength = Math.max(v1Parts.length, v2Parts.length); - v1Parts.push(...Array(maxLength - v1Parts.length).fill(0)); - v2Parts.push(...Array(maxLength - v2Parts.length).fill(0)); - - // Compare each part of the version numbers - for (let i = 0; i < maxLength; i++) { - if (v1Parts[i] > v2Parts[i]) { - return 1; - } else if (v1Parts[i] < v2Parts[i]) { - return -1; - } - } - - // Version numbers are equal - return 0; - } - - static async CheckUpgrade(showMessage: boolean = true): Promise { - axios - .get(ServerHttp.updateUrl, { - withCredentials: false, - responseType: 'json', - timeout: 30000 - }) - .then(async (response: AxiosResponse) => { - console.log('CheckUpgrade', response) - if (!response.data || !response.data.assets || !response.data.html_url) { - showMessage && message.error('获取新版本出错') - return - } - let platform = process.platform - let tagName = response.data.tag_name // 版本号 - let assets = response.data.assets // 文件 - let html_url = response.data.html_url // 详情 - let asarFileUrl = '' - let updateData = { name: '', url: '', size: 0 } - for (let asset of assets) { - const fileData = { - name: asset.name, - url: asset.browser_download_url, - size: asset.size - } - if (platform === 'win32' - && fileData.name.indexOf(process.arch) > 0 - && fileData.name.endsWith('.exe')) { - updateData = fileData - break - } else if (platform === 'darwin' - && fileData.name.indexOf(process.arch) > 0 - && fileData.name.endsWith('.dmg')) { - updateData = fileData - break - } else if (fileData.name.endsWith('.asar')) { - asarFileUrl = 'https://ghproxy.com/' + fileData.url - break - } - } - if (tagName) { - let configVer = getPkgVersion().replaceAll('v', '').trim() - if (process.platform !== 'linux') { - let localVersion = getResourcesPath('localVersion') - if (localVersion && existsSync(localVersion)) { - configVer = readFileSync(localVersion, 'utf-8').replaceAll('v', '').trim() - } - } - const remoteVer = tagName.replaceAll('v', '').trim() - const verInfo = this.dealText(response.data.body as string) - let verUrl = '' - if (updateData.url) { - verUrl = 'https://ghproxy.com/' + updateData.url - } - if (this.compareVersions(remoteVer, configVer) > 0) { - Modal.confirm({ - mask: true, - alignCenter: true, - title: () => h('div', { - innerHTML: `有新版本${tagName}`, - class: { vermodalhead: true }, - style: { maxWidth: '540px' } - }), - content: () => h('div', { - innerHTML: '
\n' + - ' 公众号\n' + - '
\n' + - '

Github需要代理,如果下载失败,请关注公众号从网盘下载

' + verInfo, - class: { vermodal: true } - }), - onClose: () => { - if (updateData.name) { - let resourcesPath = getResourcesPath(updateData.name) - if (existsSync(resourcesPath)) { - rmSync(resourcesPath, { force: true }) - } - } - return true - }, - footer: () => h(Space, {}, () => [ - h(Button, { - innerHTML: '取消', - onClick: async () => { - if (updateData.name) { - let resourcesPath = getResourcesPath(updateData.name) - if (existsSync(resourcesPath)) { - rmSync(resourcesPath, { force: true }) - } - } - try { - // @ts-ignore - document.querySelector('.arco-overlay-modal').remove() - } catch (err) { - } - return true - } - }), - h(Button, { - type: 'outline', - style: asarFileUrl.length == 0 ? '' : 'display: none', - innerHTML: platform !== 'linux' && verUrl.length > 0 ? '全量更新' : '详情', - onClick: async () => { - if (verUrl.length > 0 && platform !== 'linux') { - // 下载安装 - const msgKey = 'download_' + Date.now().toString() - await this.AutoDownload(verUrl, html_url, updateData.name, false, msgKey) - } else { - openExternal(html_url) - } - return true - } - }), - h(Button, { - type: 'primary', - style: asarFileUrl.length > 0 && platform !== 'linux' ? '' : 'display: none', - innerHTML: '热更新', - onClick: async () => { - if (asarFileUrl.length > 0 && platform !== 'linux') { - // 下载安装 - const msgKey = 'download_' + Date.now().toString() - const flag = await this.AutoDownload(asarFileUrl, html_url, updateData.name, true, msgKey) - // 更新本地版本号 - if (flag && tagName) { - const localVersion = getResourcesPath('localVersion') - if (localVersion) { - writeFile(localVersion, tagName, async (err)=> { - if (err) { - return false - } else { - message.info('热更新完毕,自动重启应用中...', 0, msgKey) - await this.Sleep(2000) - window.WebRelaunch() - return true - } - }) - } - } - } - return false - } - }) - ]) - }) - } else if (showMessage ) { - message.info('已经是最新版 ', 6) - } - } - }) - .catch((err: any) => { - showMessage && message.info('检查更新失败,请检查网络是否正常') - DebugLog.mSaveDanger('CheckUpgrade', err) - }) - } - - static dealText(context: string): string { - let splitTextArr = context.trim().split(/\r\n/g) - let resultTextArr: string[] = [] - splitTextArr.forEach((item, i) => { - let links = item.match(/!?\[.+?\]\(https?:\/\/.+\)/g) - // 处理链接 - if (links != null) { - for (let index = 0; index < links.length; index++) { - const text_link = links[index].match(/[^!\[\(\]\)]+/g)//提取文字和链接 - if (text_link) { - if (links[index][0] == '!') { //解析图片 - item = item.replace(links[index], '' + text_link[0] + '') - } else { //解析超链接 - item = item.replace(links[index], `【${text_link[0]}】`) - } - } - } - } - if (item.indexOf('- ')) { // 无序列表 - item = item.replace(/.*-\s+(.*)/g, '$1') - } - if (item.indexOf('* ')) { // 无序列表 - item = item.replace(/.*\*\s+(.*)/g, '$1') - } - if (item.includes('**')) { - item = item.replaceAll(/\*\*/g, '') - } - if (item.startsWith('# ')) { // 1 级标题(h1) - resultTextArr.push(`

${item.replace('# ', '')}

`) - } else if (item.startsWith('## ')) { // 2 级标题(h2) - resultTextArr.push(`

${item.replace('## ', '')}

`) - } else if (item.startsWith('### ')) { // 3 级标题(h3) - resultTextArr.push(`

${item.replace('### ', '')}

`) - } else if (item.indexOf('---') == 0) { - resultTextArr.push(item.replace('---', '
')) - } else { // 普通的段落 - resultTextArr.push(`${item}`) - } - }) - return resultTextArr.join('
') - } - - static async AutoDownload(appNewUrl: string, html_url: string, file_name: string, hot: boolean, msgKey: string): Promise { - const resourcesPath = hot ? getAppNewPath() : getUserDataPath(file_name) - if (!hot && existsSync(resourcesPath)) { - await this.autoInstallNewVersion(resourcesPath, msgKey) - return true - } - message.loading('新版本正在后台下载中,请耐心等待。。。', 0, msgKey) - return axios - .get(appNewUrl, { - withCredentials: false, - responseType: 'arraybuffer', - timeout: 60000, - headers: { - 'Cache-Control': 'no-cache', - Pragma: 'no-cache', - Expires: '0' - } - }) - .then(async (response: AxiosResponse) => { - writeFile(resourcesPath, Buffer.from(response.data), (err) => { - if(err) { - message.error('下载更新失败,请检查【Resources文件夹】是否有写入权限',5, msgKey) - return false - } - }) - if (!hot) { - await this.Sleep(2000) - await this.autoInstallNewVersion(resourcesPath, msgKey) - } - return true - }) - .catch(() => { - message.error('新版本下载失败,关注公众号从网盘下载', 5) - rmSync(resourcesPath, { force: true }) - openExternal(html_url) - return false - }) - } - - static async autoInstallNewVersion(resourcesPath: string, msgKey: string) { - // 自动安装 - const options: SpawnOptions = { shell: true, windowsVerbatimArguments: true } - if (process.platform === 'win32') { - execFile('\"' + resourcesPath + '\"', options, error => { - if(error) { - message.info('安装失败,请前往文件夹手动安装', 5, msgKey) - const resources = getResourcesPath('') - shell.openPath(path.join(resources, '/')) - } else { - message.info('安装成功,请重新打开', 5, msgKey) - window.WebToElectron({ cmd: 'exit' }) - } - }) - } else if (process.platform === 'darwin') { - execFile('open ' + '\"' + resourcesPath + '\"', options, error => { - if(error) { - message.info('安装失败,请前往文件夹手动安装', 5, msgKey) - const resources = getResourcesPath('') - shell.openPath(path.join(resources, '/')) - } else { - message.info('请手动移动到应用程序目录,完成安装', 5, msgKey) - window.WebToElectron({ cmd: 'exit' }) - } - }) - } - } -} - - - - - diff --git a/aliyunpan/src/aliapi/share.ts b/aliyunpan/src/aliapi/share.ts index aef7cd1313..998bf87768 100644 --- a/aliyunpan/src/aliapi/share.ts +++ b/aliyunpan/src/aliapi/share.ts @@ -1,11 +1,11 @@ import DebugLog from '../utils/debuglog' -import { humanDateTime, humanExpiration, humanSize } from '../utils/format' +import { humanDateTime, humanDateTimeDateStr, humanExpiration, humanSize } from '../utils/format' import message from '../utils/message' import AliHttp, { IUrlRespData } from './alihttp' import ServerHttp from './server' import { ApiBatch, ApiBatchMaker, ApiBatchSuccess } from './utils' import { useSettingStore } from '../store' -import { IAliShareItem, IAliShareAnonymous, IAliShareFileItem } from './alimodels' +import { IAliFileItem, IAliShareAnonymous, IAliShareBottleFish, IAliShareFileItem, IAliShareItem } from './alimodels' import getFileIcon from './fileicon' import { IAliBatchResult } from './models' @@ -13,13 +13,13 @@ export interface IAliShareFileResp { items: IAliShareFileItem[] itemsKey: Set punished_file_count: number - + next_marker: string - m_user_id: string - m_share_id: string - dirID: string - dirName: string + m_user_id: string + m_share_id: string + dirID: string + dirName: string } export interface UpdateShareModel { @@ -30,11 +30,21 @@ export interface UpdateShareModel { } export default class AliShare { - - static async ApiGetShareAnonymous(share_id: string): Promise { - - + static async ApiShareFileCheckAvailable(user_id: string, drive_id: string, file_id_list: string[]) { + if (!user_id || !drive_id || !file_id_list) return [] + const url = 'adrive/v2/share_link/check_available' + const postData = { drive_id, file_id_list } + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body.invalid_items as IAliFileItem[] + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiShareFileCheckAvailable err=' + (resp.code || '')) + } + return [] + } + + static async ApiGetShareAnonymous(share_id: string): Promise { const share: IAliShareAnonymous = { shareinfo: { share_id: share_id, @@ -55,7 +65,7 @@ export default class AliShare { error: '解析分享链接失败' } if (!share_id) return share - const url = 'adrive/v2/share_link/get_share_by_anonymous?share_id=' + share_id + const url = 'adrive/v3/share_link/get_share_by_anonymous?share_id=' + share_id const postData = { share_id: share_id } const resp = await AliHttp.Post(url, postData, '', '') if (AliHttp.IsSuccess(resp.code)) { @@ -75,13 +85,14 @@ export default class AliShare { share.shareinfo.album_id = resp.body.album_id || '' share.shareinfojson = JSON.stringify(resp.body) share.error = '' - return share + return share } - } else { - DebugLog.mSaveWarning('ApiGetShareAnonymous err=' + share_id + ' ' + (resp.code || '')) + } else if (resp.code != 429 && !AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiGetShareAnonymous err=' + share_id + ' ' + (resp.code || ''), resp.body) } - - if (resp.body?.code == 'ShareLink.Cancelled') share.error = '分享链接被取消分享了' + + if (resp.body?.code == 'TooManyRequests') share.error = '429' + else if (resp.body?.code == 'ShareLink.Cancelled') share.error = '分享链接被取消分享了' else if (resp.body?.code == 'ShareLink.Expired') share.error = '分享链接过期失效了' else if (resp.body?.code == 'ShareLink.Forbidden') share.error = '分享链接违规禁止访问' else if (resp.body?.code) share.error = resp.body.code @@ -89,7 +100,7 @@ export default class AliShare { return share } - + static async ApisSubscription(user_id: string, share_id: string): Promise { if (!user_id || !share_id) return false const url = 'adrive/v1/share_link/subscription/update' @@ -97,16 +108,14 @@ export default class AliShare { const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { return true - } else { - DebugLog.mSaveWarning('ApisSubscription err=' + share_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApisSubscription err=' + share_id + ' ' + (resp.code || ''), resp.body) } return false } - + static async ApiGetShareToken(share_id: string, pwd: string): Promise { - - if (!share_id) return ',分享链接错误' const url = 'v2/share_link/get_share_token' const postData = { share_id: share_id, share_pwd: pwd } @@ -118,31 +127,32 @@ export default class AliShare { if (useSettingStore().yinsiLinkPassword) { const serdata = await ServerHttp.PostToServer({ cmd: 'GetAliSharePwd', shareid: share_id }) if (serdata.password) { - isgetpwd = true + isgetpwd = true postData.share_pwd = serdata.password - resp = await AliHttp.Post(url, postData, '', '') + resp = await AliHttp.Post(url, postData, '', '') } } } - - if (resp.body?.code == 'InvalidResource.SharePwd') return ',提取码错误' if (resp.body?.code == 'ShareLink.Cancelled') return ',分享链接被取消分享了' if (resp.body?.code == 'ShareLink.Expired') return ',分享链接过期失效了' if (resp.body?.code == 'ShareLink.Forbidden') return ',分享链接违规禁止访问' if (resp.body?.code) return ',' + resp.body.code - if (AliHttp.IsSuccess(resp.code)) { - if (useSettingStore().yinsiLinkPassword && isgetpwd == false) ServerHttp.PostToServer({ cmd: 'PostAliShare', shareid: share_id, password: postData.share_pwd }) + if (useSettingStore().yinsiLinkPassword && !isgetpwd) ServerHttp.PostToServer({ + cmd: 'PostAliShare', + shareid: share_id, + password: postData.share_pwd + }) return (resp.body.share_token as string | undefined) || ',share_token错误' - } else { - DebugLog.mSaveWarning('ApiGetShareToken err=' + share_id + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiGetShareToken err=' + share_id + ' ' + (resp.code || ''), resp.body) } return ',网络错误请重试' } - + static async ApiShareFileList(share_id: string, share_token: string, dirID: string): Promise { const dir: IAliShareFileResp = { items: [], @@ -156,18 +166,18 @@ export default class AliShare { } do { const isGet = await AliShare.ApiShareFileListOnePage(dir, share_token) - if (isGet != true) { - break + if (!isGet) { + break } } while (dir.next_marker) return dir } - + static async ApiShareFileListOnePage(dir: IAliShareFileResp, share_token: string): Promise { const url = - 'adrive/v3/file/list?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)' + 'adrive/v3/file/list?jsonmask=next_marker%2Citems(category%2Ccreated_at%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription)' let postData = { share_id: dir.m_share_id, parent_file_id: dir.dirID, @@ -195,7 +205,6 @@ export default class AliShare { name: item.name, type: item.type, parent_file_id: item.parent_file_id, - file_extension: item.file_extension || '', mime_extension: item.mime_extension || '', mime_type: item.mime_type || '', @@ -203,6 +212,9 @@ export default class AliShare { category: item.category || '', punish_flag: item.punish_flag || 0, isDir: item.type == 'folder', + created_at: item.created_at, + updated_at: item.updated_at, + timeStr: humanDateTimeDateStr(item.updated_at || item.created_at), sizeStr: item.type == 'folder' ? '' : humanSize(item.size), icon: getFileIcon(item.category, item.file_extension, item.mime_extension, item.mime_type, item.size)[1] } @@ -212,17 +224,17 @@ export default class AliShare { dir.punished_file_count += resp.body.punished_file_count || 0 return true } else if (resp.code == 404) { - + dir.items.length = 0 dir.next_marker = '' return true } else if (resp.body && resp.body.code) { dir.items.length = 0 - dir.next_marker = resp.body.code + dir.next_marker = resp.body.code message.warning('列出分享链接内文件出错 ' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_ShareFileListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ShareFileListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ShareFileListOnePage ' + dir.dirID, err) @@ -231,13 +243,12 @@ export default class AliShare { return false } - + static async ApiCreatShare(user_id: string, drive_id: string, expiration: string, share_pwd: string, share_name: string, file_id_list: string[]): Promise { if (!user_id || !drive_id || file_id_list.length == 0) return '创建分享链接失败数据错误' const url = 'adrive/v2/share_link/create' - const postData = JSON.stringify({ drive_id, expiration, share_pwd: share_pwd, share_name: share_name, file_id_list }) + const postData = { drive_id, expiration, share_pwd, share_name, file_id_list } const resp = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(resp.code)) { const item = resp.body as IAliShareItem const add: IAliShareItem = Object.assign({}, item, { first_file: undefined, icon: 'iconwenjian' }) @@ -245,14 +256,15 @@ export default class AliShare { if (item.updated_at) add.updated_at = humanDateTime(item.updated_at) add.share_msg = humanExpiration(item.expiration) return add - } else { - DebugLog.mSaveWarning('ApiCreatShare err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiCreatShare err=' + (resp.code || ''), resp.body) } - + if (resp.body?.code.startsWith('UserPunished')) return '账号分享行为异常,无法分享' else if (resp.body?.code == 'InvalidParameter.FileIdList') return '选择文件过多,无法分享' else if (resp.body?.message && resp.body.message.indexOf('size of file_id_list') >= 0) return '选择文件过多,无法分享' else if (resp.body?.code == 'FileShareNotAllowed') return '这个文件禁止分享' + else if (resp.body?.code == 'SharelinkCreateExceedDailyLimit') return '今日分享次数过多,请明天再试' else if (resp.body?.code == 'FeatureTemporaryDisabled') return '分享功能维护中' else if (resp.body?.code) return resp.body.code.toString() else return '创建分享链接失败' @@ -277,11 +289,10 @@ export default class AliShare { } batchList.push(JSON.stringify(postData)) } - const result = await ApiBatch('', batchList, user_id, '') - return result + return await ApiBatch('', batchList, user_id, '') } - + static async ApiCancelShareBatch(user_id: string, share_idList: string[]): Promise { const batchList = ApiBatchMaker('/share_link/cancel', share_idList, (share_id: string) => { return { share_id: share_id } @@ -289,7 +300,7 @@ export default class AliShare { return ApiBatchSuccess(share_idList.length > 1 ? '批量取消分享' : '取消分享', batchList, user_id, '') } - + static async ApiUpdateShareBatch(user_id: string, share_idList: string[], expirationList: string[], share_pwdList: string[], share_nameList: string[] | undefined): Promise { if (!share_idList || share_idList.length == 0) return [] const batchList: string[] = [] @@ -297,7 +308,12 @@ export default class AliShare { for (let i = 0, maxi = share_idList.length; i < maxi; i++) { batchList.push( JSON.stringify({ - body: { share_id: share_idList[i], share_pwd: share_pwdList[i], expiration: expirationList[i], share_name: share_nameList[i] }, + body: { + share_id: share_idList[i], + share_pwd: share_pwdList[i], + expiration: expirationList[i], + share_name: share_nameList[i] + }, headers: { 'Content-Type': 'application/json' }, id: share_idList[i], method: 'POST', @@ -307,25 +323,36 @@ export default class AliShare { } } else { for (let i = 0, maxi = share_idList.length; i < maxi; i++) { - batchList.push(JSON.stringify({ body: { share_id: share_idList[i], share_pwd: share_pwdList[i], expiration: expirationList[i] }, headers: { 'Content-Type': 'application/json' }, id: share_idList[i], method: 'POST', url: '/share_link/update' })) + batchList.push(JSON.stringify({ + body: { + share_id: share_idList[i], + share_pwd: share_pwdList[i], + expiration: expirationList[i] + }, + headers: { 'Content-Type': 'application/json' }, + id: share_idList[i], + method: 'POST', + url: '/share_link/update' + })) } } const successList: UpdateShareModel[] = [] const result = await ApiBatch(share_idList.length > 1 ? '批量更新分享链接' : '更新分享链接', batchList, user_id, '') - result.reslut.map((t) => successList.push({ share_id: t.share_id!, share_pwd: t.share_pwd!, expiration: t.expiration!, share_name: t.share_name! } as UpdateShareModel)) + result.reslut.map((t) => successList.push({ + share_id: t.share_id!, + share_pwd: t.share_pwd!, + expiration: t.expiration!, + share_name: t.share_name! + } as UpdateShareModel)) return successList } - - static async ApiSaveShareFilesBatch(share_id: string, share_token: string, user_id: string, drive_id: string, parent_file_id: string, file_idList: string[]): Promise { - - - - + static async ApiSaveShareFilesBatch(share_id: string, share_token: string, user_id: string, drive_id: string, parent_file_id: string, file_idList: string[]): Promise { if (!share_id || !share_token || !user_id || !drive_id || !parent_file_id) return 'error' if (!file_idList || file_idList.length == 0) return 'success' + if (parent_file_id.includes('root')) parent_file_id = 'root' const batchList: string[] = [] for (let i = 0, maxi = file_idList.length; i < maxi; i++) { const postData = @@ -355,6 +382,18 @@ export default class AliShare { } return 'error' } + } + static async ApiShareBottleFish(user_id: string) { + if (!user_id) return '获取好运瓶失败' + const url = 'adrive/v1/bottle/fish' + const postData = {} + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body as IAliShareBottleFish + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + return resp.body.display_message || '获取好运瓶失败' + } + return '获取好运瓶失败' } } diff --git a/aliyunpan/src/aliapi/sharelist.ts b/aliyunpan/src/aliapi/sharelist.ts index e8721a8c1e..e43876b9fa 100644 --- a/aliyunpan/src/aliapi/sharelist.ts +++ b/aliyunpan/src/aliapi/sharelist.ts @@ -1,20 +1,41 @@ import DebugLog from '../utils/debuglog' -import { humanDateTime, humanExpiration, Sleep } from '../utils/format' +import { humanDateTime, humanExpiration } from '../utils/format' import message from '../utils/message' import AliHttp, { IUrlRespData } from './alihttp' -import { IAliShareItem } from './alimodels' +import { IAliShareBottleFishItem, IAliShareItem, IAliShareRecentItem } from './alimodels' import AliDirFileList from './dirfilelist' +import { useSettingStore } from '../store' export interface IAliShareResp { items: IAliShareItem[] itemsKey: Set next_marker: string - m_time: number - m_user_id: string + m_time: number + m_user_id: string } + +export interface IAliShareRecentResp { + items: IAliShareRecentItem[] + itemsKey: Set + next_marker: string + + m_time: number + m_user_id: string +} + +export interface IAliShareBottleFishResp { + items: IAliShareBottleFishItem[] + itemsKey: Set + next_marker: string + + m_time: number + m_user_id: string +} + + export default class AliShareList { - + static async ApiShareListAll(user_id: string): Promise { const dir: IAliShareResp = { items: [], @@ -27,7 +48,7 @@ export default class AliShareList { do { const isGet = await AliShareList.ApiShareListOnePage(dir) if (!isGet) { - break + break } } while (dir.next_marker) return dir @@ -36,7 +57,6 @@ export default class AliShareList { static async ApiShareListOnePage(dir: IAliShareResp): Promise { const url = 'adrive/v3/share_link/list' const postData = { - marker: dir.next_marker, creator: dir.m_user_id, include_canceled: false, @@ -51,7 +71,7 @@ export default class AliShareList { try { if (AliHttp.IsSuccess(resp.code)) { dir.next_marker = resp.body.next_marker - const downUrl = 'https://api.aliyundrive.com/v2/file/download?t=' + Date.now().toString() + const downUrl = 'https://api.alipan.com/v2/file/download?t=' + Date.now().toString() const timeNow = new Date().getTime() for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { const item = resp.body.items[i] as IAliShareItem @@ -59,7 +79,7 @@ export default class AliShareList { let icon = 'iconwenjian' let first_file if (item.first_file) { - first_file = AliDirFileList.getFileInfo(item.first_file, downUrl) + first_file = AliDirFileList.getFileInfo(dir.m_user_id, item.first_file, downUrl) icon = first_file.icon || 'iconwenjian' } const add = Object.assign({}, item, { first_file, icon }) as IAliShareItem @@ -75,7 +95,6 @@ export default class AliShareList { } else { add.created_at = '' } - add.share_msg = humanExpiration(item.expiration, timeNow) if (item.status == 'forbidden') add.share_msg = '分享违规' dir.items.push(add) @@ -84,7 +103,6 @@ export default class AliShareList { return true } else if (resp.code == 404) { - dir.items.length = 0 dir.next_marker = '' return true @@ -93,8 +111,8 @@ export default class AliShareList { dir.next_marker = resp.body.code message.warning('列出分享列表出错' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_ShareListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ShareListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ShareListOnePage', err) @@ -103,27 +121,160 @@ export default class AliShareList { return false } + + static async ApiShareRecentListAll(user_id: string): Promise { + const dir: IAliShareRecentResp = { + items: [], + itemsKey: new Set(), + next_marker: '', + m_time: 0, + m_user_id: user_id + } + let max: number = useSettingStore().debugFavorListMax + do { + const isGet = await AliShareList.ApiShareRecentListOnePage(dir) + if (!isGet) { + break + } + if (dir.items.length >= max && max > 0) { + dir.next_marker = '' + break + } + } while (dir.next_marker) + return dir + } + + static async ApiShareBottleFishListAll(user_id: string): Promise { + const dir: IAliShareBottleFishResp = { + items: [], + itemsKey: new Set(), + next_marker: '', + m_time: 0, + m_user_id: user_id + } + do { + const isGet = await AliShareList.ApiShareBottleFishListOnePage(dir) + if (!isGet) { + break + } + } while (dir.next_marker) + return dir + } + + static async ApiShareRecentListOnePage(dir: IAliShareRecentResp): Promise { + const url = 'adrive/v2/share_link/recent_copy_list' + const postData = { limit: 50, marker: dir.next_marker, order_by: 'gmt_modified DESC' /* 更新时间 降序 */ } + const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') + return AliShareList._ShareRecentListOnePage(dir, resp) + } + + static async ApiShareBottleFishListOnePage(dir: IAliShareBottleFishResp): Promise { + const url = 'adrive/v1/bottle/list' + const postData = { limit: 100 } + const resp = await AliHttp.Post(url, postData, dir.m_user_id, '') + return AliShareList._ShareBottleFishListOnePage(dir, resp) + } + + static _ShareRecentListOnePage(dir: IAliShareRecentResp, resp: IUrlRespData): boolean { + try { + if (AliHttp.IsSuccess(resp.code)) { + dir.next_marker = resp.body.next_marker + for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { + const item = resp.body.items[i] as IAliShareRecentItem + const add = Object.assign({}, item) as IAliShareRecentItem + if (dir.itemsKey.has(item.share_id)) continue + if (!add.share_msg) add.share_msg = '' + if (!add.share_name) add.share_name = 'share_name' + if (!add.preview_count) add.preview_count = 0 + if (!add.file_count) add.file_count = 0 + if (!add.save_count) add.save_count = 0 + if (!add.browse_count) add.browse_count = 0 + if (add.gmt_created) { + add.gmt_created = humanDateTime(new Date(add.gmt_created).getTime()) + } else { + add.gmt_created = '' + } + if (add.gmt_modified) { + add.gmt_modified = humanDateTime(new Date(add.gmt_modified).getTime()) + } else { + add.gmt_modified = '' + } + if (add.status == 'forbidden') add.share_msg = '分享违规' + dir.items.push(add) + dir.itemsKey.add(add.share_id) + } + return true + } else if (resp.code == 404) { + dir.items.length = 0 + dir.next_marker = '' + return true + } else if (resp.body && resp.body.code) { + dir.items.length = 0 + dir.next_marker = resp.body.code + message.warning('列出历史分享列表出错' + resp.body.code, 2) + return false + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ShareRecentListOnePage err=' + (resp.code || ''), resp.body) + } + } catch (err: any) { + DebugLog.mSaveDanger('_ShareRecentListOnePage', err) + } + dir.next_marker = 'error ' + resp.code + return false + } + + static _ShareBottleFishListOnePage(dir: IAliShareBottleFishResp, resp: IUrlRespData): boolean { + try { + if (AliHttp.IsSuccess(resp.code)) { + dir.next_marker = '' + for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { + const item = resp.body.items[i] as IAliShareBottleFishItem + const add = Object.assign({}, item) as IAliShareBottleFishItem + if (dir.itemsKey.has(item.bottleId)) continue + if (!add.share_name) add.share_name = 'share_name' + if (add.gmtCreate) { + add.gmt_created = humanDateTime(new Date(add.gmtCreate).getTime()) + } else { + add.gmt_created = '' + } + if (add.saved) add.saved_msg = '已保存' + else add.saved_msg = '未保存' + dir.items.push(add) + dir.itemsKey.add(add.bottleId) + } + return true + } else if (resp.code == 404) { + dir.items.length = 0 + dir.next_marker = '' + return true + } else if (resp.body && resp.body.code) { + dir.items.length = 0 + dir.next_marker = resp.body.code + message.warning('列出好运瓶领取记录列表出错' + resp.body.code, 2) + return false + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ShareBottleFishListOnePage err=' + (resp.code || ''), resp.body) + } + } catch (err: any) { + DebugLog.mSaveDanger('_ShareBottleFishListOnePage', err) + } + dir.next_marker = 'error ' + resp.code + return false + } + static async ApiShareListUntilShareID(user_id: string, share_id: string): Promise { const url = 'adrive/v3/share_link/list' const postData = { - marker: '', creator: user_id, include_canceled: false, order_by: 'created_at', order_direction: 'DESC' } - for (let j = 0; j < 10; j++) { - const resp = await AliHttp.Post(url, postData, user_id, '') - try { - if (AliHttp.IsSuccess(resp.code)) { - for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { - const item = resp.body.items[i] as IAliShareItem - if (item.share_id == share_id) return true - } - } - } catch {} - await Sleep(500) + const resp = await AliHttp.Post(url, postData, user_id, '') + for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { + const item = resp.body.items[i] as IAliShareItem + if (item.share_id == share_id) return true } return false } diff --git a/aliyunpan/src/aliapi/transfershare.ts b/aliyunpan/src/aliapi/transfershare.ts index cbaa0a9aca..4af5b9e4d7 100644 --- a/aliyunpan/src/aliapi/transfershare.ts +++ b/aliyunpan/src/aliapi/transfershare.ts @@ -19,8 +19,8 @@ export default class AliTransferShare { const add: IAliShareItem = Object.assign({}, item, { first_file: undefined, icon: 'iconwenjian' }) add.share_msg = humanExpiration(item.expiration) return add - } else { - DebugLog.mSaveWarning('ApiCreatShare err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiCreatShare err=' + (resp.code || ''), resp.body) } if (resp.body?.code.startsWith('UserPunished')) return '账号分享行为异常,无法分享' else if (resp.body?.code == 'InvalidParameter.FileIdList') return '选择文件过多,无法分享' diff --git a/aliyunpan/src/aliapi/transfersharelist.ts b/aliyunpan/src/aliapi/transfersharelist.ts index f2dec1d51a..2692e3a287 100644 --- a/aliyunpan/src/aliapi/transfersharelist.ts +++ b/aliyunpan/src/aliapi/transfersharelist.ts @@ -40,10 +40,10 @@ export default class AliTransferShareList { order_direction: 'DESC' } const resp = await AliHttp.Post(url, postData,user_id,'') - return AliTransferShareList._TransferShareListOnePage(user_id, dir, resp) + return await AliTransferShareList._TransferShareListOnePage(user_id, dir, resp) } - static _TransferShareListOnePage(user_id: string, dir: IAliShareResp, resp: IUrlRespData): boolean { + static async _TransferShareListOnePage(user_id: string, dir: IAliShareResp, resp: IUrlRespData): Promise { try { if (AliHttp.IsSuccess(resp.code)) { const timeNow = new Date().getTime() @@ -51,7 +51,7 @@ export default class AliTransferShareList { const item = resp.body.items[i] as IAliShareItem if (dir.itemsKey.has(item.share_id)) continue let icon = 'iconwenjian' - let first_file: any = item.share_id && AliTransferShareList.ApiTransferShareFileStatus(user_id, item.share_id) + let first_file: any = item.share_id && await AliTransferShareList.ApiTransferShareFileStatus(user_id, item.share_id) const add = Object.assign({}, item, { first_file, icon }) as IAliShareItem if (!add.full_share_msg) add.full_share_msg = '' if (!add.share_msg) add.share_msg = '' @@ -77,8 +77,8 @@ export default class AliTransferShareList { dir.items.length = 0 message.warning('列出分享列表出错' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_ShareListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ShareListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_ShareListOnePage', err) @@ -92,8 +92,8 @@ export default class AliTransferShareList { const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { return resp.body as IAliShareItem - } else { - DebugLog.mSaveWarning('ApiTransferShareFileStatus err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiTransferShareFileStatus err=' + (resp.code || ''), resp.body) } return false } diff --git a/aliyunpan/src/aliapi/trash.ts b/aliyunpan/src/aliapi/trash.ts index d519c55a0f..00f1282f25 100644 --- a/aliyunpan/src/aliapi/trash.ts +++ b/aliyunpan/src/aliapi/trash.ts @@ -8,7 +8,7 @@ export default class AliTrash { static async ApiTrashFileListOnePageForClean(orderby: string, order: string, dir: IAliFileResp): Promise { const url = - 'v2/recyclebin/list?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)' + 'v2/recyclebin/list?jsonmask=next_marker%2Citems(category%2Ccreated_at%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription)' const postData = { drive_id: dir.m_drive_id, marker: dir.next_marker, @@ -26,7 +26,7 @@ export default class AliTrash { static async ApiFavorFileListOnePageForClean(orderby: string, order: string, dir: IAliFileResp): Promise { const url = - 'v2/file/list_by_custom_index_key?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)' + 'v2/file/list_by_custom_index_key?jsonmask=next_marker%2Citems(category%2Ccreated_at%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription)' const postData = { drive_id: dir.m_drive_id, marker: dir.next_marker, @@ -59,7 +59,7 @@ export default class AliTrash { const orders = order.split(' ') do { const isGet = await AliTrash._ApiDirFileListOnePage(orders[0], orders[1], dir, type) - if (isGet != true) { + if (!isGet) { break } if (dir.items.length >= max && max > 0) { @@ -72,10 +72,10 @@ export default class AliTrash { static async _ApiDirFileListOnePage(orderby: string, order: string, dir: IAliFileResp, type: string = ''): Promise { const url = - 'adrive/v3/file/list?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)' + 'adrive/v3/file/list?jsonmask=next_marker%2Citems(category%2Ccreated_at%2Cdrive_id%2Cfile_extension%2Cfile_id%2Chidden%2Cmime_extension%2Cmime_type%2Cname%2Cparent_file_id%2Cpunish_flag%2Csize%2Cstarred%2Ctype%2Cupdated_at%2Cdescription)' let postData = { drive_id: dir.m_drive_id, - parent_file_id: dir.dirID, + parent_file_id: dir.dirID.includes('root') ? 'root': dir.dirID, marker: dir.next_marker, limit: 100, all: false, @@ -94,12 +94,12 @@ export default class AliTrash { if (AliHttp.IsSuccess(resp.code)) { dir.next_marker = resp.body.next_marker const isrecover = dir.dirID == 'recover' - const downurl = isrecover ? '' : 'https://api.aliyundrive.com/v2/file/download?t=' + Date.now().toString() + const downurl = isrecover ? '' : 'https://api.alipan.com/v2/file/download?t=' + Date.now().toString() for (let i = 0, maxi = resp.body.items.length; i < maxi; i++) { const item = resp.body.items[i] as IAliFileItem if (dir.itemsKey.has(item.file_id)) continue - const add = AliDirFileList.getFileInfo(item, downurl) + const add = AliDirFileList.getFileInfo(dir.m_user_id, item, downurl) if (isrecover) add.description = item.content_hash dir.items.push(add) dir.itemsKey.add(item.file_id) @@ -108,7 +108,6 @@ export default class AliTrash { return true } else if (resp.code == 404) { - dir.items.length = 0 dir.next_marker = '' return true @@ -117,8 +116,8 @@ export default class AliTrash { dir.next_marker = resp.body.code message.warning('列出文件出错 ' + resp.body.code, 2) return false - } else { - DebugLog.mSaveWarning('_FileListOnePage err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_FileListOnePage err=' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('_FileListOnePage ' + dir.dirID, err) @@ -141,8 +140,7 @@ export default class AliTrash { order_direction: order } const resp = await AliHttp.Post(url, postdata, dir.m_user_id, '') - //todo:: 这里不完善 + // todo:: 这里不完善 return AliDirFileList._FileListOnePage(orderby, order, dir, resp, -1) - //return Promise.resolve(false) } } diff --git a/aliyunpan/src/aliapi/upload.ts b/aliyunpan/src/aliapi/upload.ts index 8369bbeaf1..b9a0a472be 100644 --- a/aliyunpan/src/aliapi/upload.ts +++ b/aliyunpan/src/aliapi/upload.ts @@ -1,10 +1,14 @@ import DebugLog from '../utils/debuglog' import AliHttp from './alihttp' import { IUploadCreat, IUploadInfo } from './models' +import { EncodeEncName } from './utils' export default class AliUpload { - - static async UploadCreatFileWithPreHash(user_id: string, drive_id: string, parent_file_id: string, name: string, fileSize: number, prehash: string, check_name_mode: string): Promise { + static async UploadCreatFileWithPreHash( + user_id: string, drive_id: string, parent_file_id: string, + filename: string, fileSize: number, + prehash: string, check_name_mode: string, encType: string = '' + ): Promise { const result: IUploadCreat = { user_id, drive_id, @@ -15,11 +19,11 @@ export default class AliUpload { part_info_list: [], errormsg: '' } - if (!user_id || !drive_id || !parent_file_id || !name) { + if (!user_id || !drive_id || !parent_file_id || !filename) { result.errormsg = '创建文件失败(数据错误)' return result } - + if (parent_file_id.includes('root')) parent_file_id = 'root' const url = 'adrive/v2/file/createWithFolders' const postData: { drive_id: string @@ -34,7 +38,7 @@ export default class AliUpload { } = { drive_id, parent_file_id: parent_file_id, - name: name, + name: filename, type: 'file', check_name_mode: check_name_mode == 'ignore' ? 'refuse' : check_name_mode, size: fileSize, @@ -42,30 +46,25 @@ export default class AliUpload { part_info_list: [] } - let partSize = 10485760 + let partSize = 10485760 if (fileSize > 0) { let partIndex = 0 - - while (fileSize > partSize * 8000) partSize = partSize + 10485760 - + while (fileSize > partSize * 8000) partSize = partSize + 10485760 while (partIndex * partSize < fileSize) { postData.part_info_list.push({ part_number: partIndex + 1, part_size: partSize }) partIndex++ } - postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize + postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize } const resp = await AliHttp.Post(url, postData, user_id, '') - if (typeof resp.body === 'object' && JSON.stringify(resp.body).indexOf('file size is exceed') > 0) { result.errormsg = '创建文件失败(单文件最大100GB/2TB)' return result } - if (resp.body && resp.body.code) { if (resp.body?.code == 'PreHashMatched') { - result.errormsg = 'PreHashMatched' } else if (resp.body?.code == 'QuotaExhausted.Drive') { result.errormsg = '出错暂停,网盘空间已满' @@ -73,32 +72,33 @@ export default class AliUpload { result.errormsg = resp.body?.code || '创建失败,网络错误' DebugLog.mSaveDanger('createWithFolders', result.errormsg + ' ' + name) } - return result } - + if (AliHttp.IsSuccess(resp.code)) { result.file_id = resp.body.file_id if (resp.body.exist) { - if (check_name_mode == 'ignore') { - - await AliUpload.UploadFileDelete(user_id, drive_id, result.file_id).catch(() => {}) - return await AliUpload.UploadCreatFileWithPreHash(user_id, drive_id, parent_file_id, name, fileSize, prehash, check_name_mode) + await AliUpload.UploadFileDelete(user_id, drive_id, result.file_id).catch() + return await AliUpload.UploadCreatFileWithPreHash(user_id, drive_id, parent_file_id, filename, fileSize, prehash, check_name_mode) } else { - result.errormsg = '出错暂停,网盘内有重名文件' } } - result.isexist = resp.body.exist || false + result.isexist = resp.body.exist || false result.israpid = false result.upload_id = resp.body.upload_id || '' if (resp.body.part_info_list && resp.body.part_info_list.length > 0) { const part_info_list = resp.body.part_info_list for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { const item = part_info_list[i] - result.part_info_list.push({ upload_url: item.upload_url, part_number: item.part_number, part_size: partSize, isupload: false }) + result.part_info_list.push({ + upload_url: item.upload_url, + part_number: item.part_number, + part_size: partSize, + isupload: false + }) } } return result @@ -109,7 +109,12 @@ export default class AliUpload { } } - static async UploadCreatFileWithFolders(user_id: string, drive_id: string, parent_file_id: string, name: string, fileSize: number, hash: string, proof_code: string, check_name_mode: string): Promise { + static async UploadCreatFileWithFolders( + user_id: string, drive_id: string, + parent_file_id: string, filename: string, + fileSize: number, hash: string, proof_code: string, + check_name_mode: string, encType: string = '' + ): Promise { const result: IUploadCreat = { user_id, drive_id, @@ -120,14 +125,13 @@ export default class AliUpload { part_info_list: [], errormsg: '' } - if (!user_id || !drive_id || !parent_file_id || !name) { + if (!user_id || !drive_id || !parent_file_id || !filename) { result.errormsg = '创建文件失败(数据错误)' return result } - - - + if (parent_file_id.includes('root')) parent_file_id = 'root' const url = 'adrive/v2/file/createWithFolders' + const name = EncodeEncName(user_id, filename, false, encType) const postData: { drive_id: string parent_file_id: string @@ -140,7 +144,8 @@ export default class AliUpload { proof_code?: string proof_version?: string part_info_list: { part_number: number; part_size: number }[] - ignore_rapid?: boolean + ignore_rapid?: boolean, + description: string } = { drive_id, parent_file_id: parent_file_id, @@ -148,29 +153,33 @@ export default class AliUpload { type: 'file', check_name_mode: check_name_mode == 'ignore' ? 'refuse' : check_name_mode, size: fileSize, - part_info_list: [] + part_info_list: [], + description: encType } - if (hash) { postData.content_hash = hash.toUpperCase() postData.content_hash_name = 'sha1' postData.proof_version = 'v1' - postData.proof_code = proof_code - + postData.proof_code = proof_code + } else { + postData.content_hash = '' + postData.content_hash_name = 'none' + postData.proof_version = 'v1' + postData.proof_code = '' } - let partSize = 10485760 + let partSize = 10485760 if (fileSize > 0) { let partIndex = 0 - while (fileSize > partSize * 8000) partSize = partSize + 10485760 + while (fileSize > partSize * 8000) partSize = partSize + 10485760 while (partIndex * partSize < fileSize) { postData.part_info_list.push({ part_number: partIndex + 1, part_size: partSize }) partIndex++ } - postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize + postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize } const resp = await AliHttp.Post(url, postData, user_id, '') @@ -180,23 +189,23 @@ export default class AliUpload { return result } - + if (resp.body && resp.body.code) { if (resp.body?.code == 'QuotaExhausted.Drive') { result.errormsg = '出错暂停,网盘空间已满' } else if (resp.body?.code == 'InvalidRapidProof') { - + result.errormsg = resp.body.code DebugLog.mSaveDanger('InvalidRapidProof', name) } else { result.errormsg = resp.body?.code || '创建失败,网络错误' DebugLog.mSaveDanger('createWithFolders', result.errormsg + ' ' + name) } - + return result } - + if (AliHttp.IsSuccess(resp.code)) { result.file_id = resp.body.file_id if (resp.body.exist) { @@ -205,23 +214,27 @@ export default class AliUpload { result.errormsg = '' } else { if (check_name_mode == 'ignore') { - - await AliUpload.UploadFileDelete(user_id, drive_id, result.file_id).catch(() => {}) - return await AliUpload.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, name, fileSize, hash, proof_code, check_name_mode) + await AliUpload.UploadFileDelete(user_id, drive_id, result.file_id).catch() + return await AliUpload.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, name, fileSize, hash, proof_code, check_name_mode) } else { - + result.errormsg = '出错暂停,网盘内有重名文件' } } } - result.isexist = resp.body.exist || false - result.israpid = result.israpid || resp.body.rapid_upload || false + result.isexist = resp.body.exist || false + result.israpid = result.israpid || resp.body.rapid_upload || false result.upload_id = resp.body.upload_id || '' if (resp.body.part_info_list && resp.body.part_info_list.length > 0) { const part_info_list = resp.body.part_info_list for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { const item = part_info_list[i] - result.part_info_list.push({ upload_url: item.upload_url, part_number: item.part_number, part_size: partSize, isupload: false }) + result.part_info_list.push({ + upload_url: item.upload_url, + part_number: item.part_number, + part_size: partSize, + isupload: false + }) } } return result @@ -232,7 +245,7 @@ export default class AliUpload { } } - + static async UploadFileCheckHash(user_id: string, drive_id: string, file_id: string, hash: string): Promise { if (!user_id || !drive_id || !file_id) return false const url = 'v2/file/get?jsonmask=content_hash' @@ -242,13 +255,14 @@ export default class AliUpload { const content_hash = resp.body.content_hash.toUpperCase() hash = hash.toUpperCase() return hash === content_hash - } else { - DebugLog.mSaveWarning('UploadFileCheckHash err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('UploadFileCheckHash err=' + (resp.code || ''), resp.body) return false } + return false } - + static async UploadFileDelete(user_id: string, drive_id: string, file_id: string, permanently: boolean = false): Promise { if (!user_id || !drive_id || !file_id) return false const url = 'v2/recyclebin/trash' @@ -256,13 +270,14 @@ export default class AliUpload { const resp = await AliHttp.Post(url, postData, user_id, '') if (AliHttp.IsSuccess(resp.code)) { return true - } else { - DebugLog.mSaveWarning('UploadFileDelete err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('UploadFileDelete err=' + (resp.code || ''), resp.body) return false } + return false } - + static async UploadFileComplete(user_id: string, drive_id: string, file_id: string, upload_id: string, fileSize: number, fileSha1: string): Promise { if (!user_id || !drive_id || !file_id || !upload_id) return false const url = 'v2/file/complete' @@ -270,30 +285,24 @@ export default class AliUpload { let resp = await AliHttp.Post(url, postData, user_id, '') if (resp.code == 400 || resp.code == 429) { resp = await AliHttp.Post(url, postData, user_id, '') - - } - if (AliHttp.IsSuccess(resp.code)) { if (resp.body.size == fileSize) { if (fileSha1) { - if (resp.body.content_hash && resp.body.content_hash == fileSha1) { return true } else { - - await AliUpload.UploadFileDelete(user_id, drive_id, file_id, true).catch(() => {}) + await AliUpload.UploadFileDelete(user_id, drive_id, file_id, true).catch() DebugLog.mSaveDanger('UploadFileComplete', '合并文件后发现SHA1不一致,删除已上传的文件,重新上传') return false } } else if (fileSize < 10485760) { - return true + return true } else { - return true + return true } } else { - - await AliUpload.UploadFileDelete(user_id, drive_id, file_id, true).catch(() => {}) + await AliUpload.UploadFileDelete(user_id, drive_id, file_id, true).catch() DebugLog.mSaveDanger('UploadFileComplete', '合并文件后发现大小不一致,删除已上传的文件,重新上传') return false } @@ -303,7 +312,7 @@ export default class AliUpload { } } - + static async UploadFilePartUrl(user_id: string, drive_id: string, file_id: string, upload_id: string, fileSize: number, uploadInfo: IUploadInfo): Promise<'neterror' | 'success' | 'error'> { const url = 'v2/file/get_upload_url' const postData: { @@ -319,32 +328,37 @@ export default class AliUpload { } let partIndex = 0 - let partSize = 10485760 + let partSize = 10485760 while (fileSize > partSize * 8000) partSize = partSize + 10485760 while (partIndex * partSize < fileSize) { postData.part_info_list.push({ part_number: partIndex + 1, part_size: partSize }) partIndex++ } - postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize + postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize const resp = await AliHttp.Post(url, postData, user_id, '') if (resp.code >= 600 && resp.code <= 610) { - return 'neterror' + return 'neterror' } if (AliHttp.IsSuccess(resp.code)) { if (resp.body.part_info_list && resp.body.part_info_list.length > 0) { - + const part_info_list = resp.body.part_info_list if (uploadInfo.part_info_list.length == 0) { - + for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { const item = part_info_list[i] - uploadInfo.part_info_list.push({ upload_url: item.upload_url, part_number: item.part_number, part_size: partSize, isupload: false }) + uploadInfo.part_info_list.push({ + upload_url: item.upload_url, + part_number: item.part_number, + part_size: partSize, + isupload: false + }) } } else { - + for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { const item = part_info_list[i] uploadInfo.part_info_list[item.part_number - 1].upload_url = item.upload_url @@ -354,12 +368,12 @@ export default class AliUpload { return 'success' } else { uploadInfo.part_info_list = [] - DebugLog.mSaveWarning('UploadFilePartUrl err=' + upload_id + ' ' + (resp.code || '')) + DebugLog.mSaveWarning('UploadFilePartUrl err=' + upload_id + ' ' + (resp.code || ''), resp.body) return 'error' } } - + static async UploadFileListUploadedParts(user_id: string, drive_id: string, file_id: string, upload_id: string, part_number_marker: number, uploadInfo: IUploadInfo): Promise<'neterror' | 'success' | 'error'> { if (!user_id || !drive_id || !file_id || !upload_id) return 'error' @@ -367,7 +381,7 @@ export default class AliUpload { const postData = { drive_id: drive_id, upload_id: upload_id, file_id: file_id, part_number_marker /* 1开始 */ } const resp = await AliHttp.Post(url, postData, user_id, '') if (resp.code >= 600 && resp.code <= 610) { - return 'neterror' + return 'neterror' } if (AliHttp.IsSuccess(resp.code)) { if (resp.body.uploaded_parts && resp.body.uploaded_parts.length > 0) { @@ -377,7 +391,7 @@ export default class AliUpload { const part_number = item.part_number const uploadpart = uploadInfo.part_info_list[part_number - 1] if (uploadpart.part_size != item.part_size) { - + DebugLog.mSaveDanger('list_uploaded_parts', '分片数据错误 uploadpart=' + uploadpart.part_size + ' item=' + item.part_size) return 'error' } @@ -386,11 +400,12 @@ export default class AliUpload { } if (resp.body.next_part_number_marker && parseInt(resp.body.next_part_number_marker) > 0) { const next = parseInt(resp.body.next_part_number_marker) - await AliUpload.UploadFileListUploadedParts(user_id, drive_id, file_id, upload_id, next, uploadInfo).catch(() => {}) + await AliUpload.UploadFileListUploadedParts(user_id, drive_id, file_id, upload_id, next, uploadInfo).catch(() => { + }) } return 'success' } else { - DebugLog.mSaveWarning('UploadFileListUploadedParts err=' + upload_id + ' ' + (resp.code || '')) + DebugLog.mSaveWarning('UploadFileListUploadedParts err=' + upload_id + ' ' + (resp.code || ''), resp.body) return 'error' } } diff --git a/aliyunpan/src/aliapi/uploadOpenApi.ts b/aliyunpan/src/aliapi/uploadOpenApi.ts deleted file mode 100644 index 18baa70ebc..0000000000 --- a/aliyunpan/src/aliapi/uploadOpenApi.ts +++ /dev/null @@ -1,416 +0,0 @@ -import DebugLog from '../utils/debuglog' -import AliHttp from './alihttp' -import { IUploadCreat, IUploadInfo } from './models' -import path from "path"; -import AliFileCmd from "./filecmd"; -import AliAlbum from './album' - -export default class AliUploadOpenApi { - - static async UploadCreatFileWithPreHash(user_id: string, drive_id: string, parent_file_id: string, name: string, fileSize: number, prehash: string, check_name_mode: string): Promise { - const result: IUploadCreat = { - user_id, - drive_id, - israpid: false, - isexist: false, - upload_id: '', - file_id: '', - part_info_list: [], - errormsg: '' - } - if (!user_id || !drive_id || !parent_file_id || !name) { - result.errormsg = '创建文件失败(数据错误)' - return result - } - - const pathSplitor = name.split(path.sep); - let newFileName = name; - if (pathSplitor.length > 1) { - const dirFullName = pathSplitor.slice(0, pathSplitor.length - 1).join(path.sep); - newFileName = pathSplitor[pathSplitor.length-1] - const resp = await AliFileCmd.ApiCreatNewForder(user_id, drive_id, parent_file_id, dirFullName); - parent_file_id = resp.file_id - } - let url = 'adrive/v1.0/openFile/create' - if (name.includes('_album_id')) { - url = 'adrive/v1/biz/albums/file/create' - newFileName = name.split('album_id=')[0] - } - const postData: { - drive_id: string - parent_file_id: string - name: string - type: string - check_name_mode: string - size: number - pre_hash: string - part_info_list: { part_number: number; part_size: number }[] - ignore_rapid?: boolean - } = { - drive_id, - parent_file_id: parent_file_id, - name: newFileName, - type: 'file', - check_name_mode: check_name_mode == 'ignore' ? 'refuse' : check_name_mode, - size: fileSize, - pre_hash: prehash, - part_info_list: [] - } - - let partSize = 10485760 - if (fileSize > 0) { - let partIndex = 0 - - while (fileSize > partSize * 8000) partSize = partSize + 10485760 - - while (partIndex * partSize < fileSize) { - postData.part_info_list.push({ part_number: partIndex + 1, part_size: partSize }) - partIndex++ - } - postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize - } - - const resp = await AliHttp.Post(url, postData, user_id, '') - - if (typeof resp.body === 'object' && JSON.stringify(resp.body).indexOf('file size is exceed') > 0) { - result.errormsg = '创建文件失败(单文件最大100GB/2TB)' - return result - } - - - if (resp.body && resp.body.code) { - if (resp.body?.code == 'PreHashMatched') { - - result.errormsg = 'PreHashMatched' - } else if (resp.body?.code == 'QuotaExhausted.Drive') { - result.errormsg = '出错暂停,网盘空间已满' - } else { - result.errormsg = resp.body?.code || '创建失败,网络错误' - DebugLog.mSaveDanger('createWithFolders', result.errormsg + ' ' + name) - } - - return result - } - - - if (AliHttp.IsSuccess(resp.code)) { - result.file_id = resp.body.file_id - if (resp.body.exist) { - if (check_name_mode == 'ignore') { - await this.UploadFileDelete(user_id, drive_id, result.file_id).catch(() => {}) - return await this.UploadCreatFileWithPreHash(user_id, drive_id, parent_file_id, name, fileSize, prehash, check_name_mode) - } else { - result.errormsg = '出错暂停,网盘内有重名文件' - } - } - result.isexist = resp.body.exist || false - result.israpid = false - result.upload_id = resp.body.upload_id || '' - if (resp.body.part_info_list && resp.body.part_info_list.length > 0) { - const part_info_list = resp.body.part_info_list - for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { - const item = part_info_list[i] - result.part_info_list.push({ upload_url: item.upload_url, part_number: item.part_number, part_size: partSize, isupload: false }) - } - } - return result - } else { - DebugLog.mSaveWarning('UploadCreatFileWithFolders err=' + (resp.code || '')) - result.errormsg = '创建文件失败' + resp.code.toString() - return result - } - } - - static async UploadCreatFileWithFolders(user_id: string, drive_id: string, - parent_file_id: string, name: string, fileSize: number, - hash: string, proof_code: string, check_name_mode: string): Promise { - const result: IUploadCreat = { - user_id, - drive_id, - israpid: false, - isexist: false, - upload_id: '', - file_id: '', - part_info_list: [], - errormsg: '' - } - if (!user_id || !drive_id || !parent_file_id || !name) { - result.errormsg = '创建文件失败(数据错误)' - return result - } - let newFileName = name - const pathSplitor = name.split(path.sep); - if (pathSplitor.length > 1) { - newFileName = pathSplitor[pathSplitor.length-1] - const dirFullName = pathSplitor.slice(0, pathSplitor.length - 1).join(path.sep); - const resp = await AliFileCmd.ApiCreatNewForder(user_id, drive_id, parent_file_id, dirFullName); - parent_file_id = resp.file_id - } - - const url = 'adrive/v1.0/openFile/create' - const postData: { - drive_id: string - parent_file_id: string - name: string - type: string - check_name_mode: string - size: number - content_hash?: string - content_hash_name?: string - proof_code?: string - proof_version?: string - part_info_list: { part_number: number; part_size: number }[] - } = { - drive_id, - parent_file_id: parent_file_id, - name: newFileName, - type: 'file', - check_name_mode: check_name_mode == 'ignore' ? 'refuse' : check_name_mode, - size: fileSize, - part_info_list: [] - } - - - if (hash) { - postData.content_hash = hash.toUpperCase() - postData.content_hash_name = 'sha1' - postData.proof_version = 'v1' - postData.proof_code = proof_code - - } - - let partSize = 10485760 - if (fileSize > 0) { - let partIndex = 0 - - while (fileSize > partSize * 8000) partSize = partSize + 10485760 - - while (partIndex * partSize < fileSize) { - postData.part_info_list.push({ part_number: partIndex + 1, part_size: partSize }) - partIndex++ - } - postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize - } - - const resp = await AliHttp.Post(url, postData, user_id, '') - - if (typeof resp.body === 'object' && JSON.stringify(resp.body).indexOf('file size is exceed') > 0) { - result.errormsg = '创建文件失败(单文件最大100GB/2TB)' - return result - } - - - if (resp.body && resp.body.code) { - if (resp.body?.code == 'QuotaExhausted.Drive') { - result.errormsg = '出错暂停,网盘空间已满' - } else if (resp.body?.code == 'InvalidRapidProof') { - - result.errormsg = resp.body.code - DebugLog.mSaveDanger('InvalidRapidProof', name) - } else { - result.errormsg = resp.body?.code || '创建失败,网络错误' - DebugLog.mSaveDanger('createWithFolders', result.errormsg + ' ' + name) - } - - return result - } - - - if (AliHttp.IsSuccess(resp.code)) { - result.file_id = resp.body.file_id - if (resp.body.exist) { - const issame = await this.UploadFileCheckHash(user_id, drive_id, result.file_id, hash) - if (issame) { - result.errormsg = '' - } else { - if (check_name_mode == 'ignore') { - await this.UploadFileDelete(user_id, drive_id, result.file_id).catch(() => {}) - return await this.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, name, fileSize, hash, proof_code, check_name_mode) - } else { - - result.errormsg = '出错暂停,网盘内有重名文件' - } - } - } - result.isexist = resp.body.exist || false - result.israpid = result.israpid || resp.body.rapid_upload || false - result.upload_id = resp.body.upload_id || '' - if (resp.body.part_info_list && resp.body.part_info_list.length > 0) { - const part_info_list = resp.body.part_info_list - for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { - const item = part_info_list[i] - result.part_info_list.push({ upload_url: item.upload_url, part_number: item.part_number, part_size: partSize, isupload: false }) - } - } - return result - } else { - DebugLog.mSaveWarning('UploadCreatFileWithFolders err=' + (resp.code || '')) - result.errormsg = '创建文件失败' + resp.code.toString() - return result - } - } - - - static async UploadFileCheckHash(user_id: string, drive_id: string, file_id: string, hash: string): Promise { - if (!user_id || !drive_id || !file_id) return false - const url = 'v2/file/get?jsonmask=content_hash' - const postData = { drive_id: drive_id, file_id: file_id } - const resp = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(resp.code) && resp.body.content_hash) { - const content_hash = resp.body.content_hash.toUpperCase() - hash = hash.toUpperCase() - return hash === content_hash - } else { - DebugLog.mSaveWarning('UploadFileCheckHash err=' + (resp.code || '')) - return false - } - } - - - static async UploadFileDelete(user_id: string, drive_id: string, file_id: string, permanently: boolean = false): Promise { - if (!user_id || !drive_id || !file_id) return false - const url = 'adrive/v1.0/openFile/recyclebin/trash' - const postData = { drive_id: drive_id, file_id: file_id } - const resp = await AliHttp.Post(url, postData, user_id, '') - if (AliHttp.IsSuccess(resp.code)) { - return true - } else { - DebugLog.mSaveWarning('UploadFileDelete err=' + (resp.code || '')) - return false - } - } - - - static async UploadFileComplete(user_id: string, drive_id: string, file_id: string, upload_id: string, fileSize: number, fileSha1: string): Promise { - if (!user_id || !drive_id || !file_id || !upload_id) return false - const url = 'adrive/v1.0/openFile/complete' - const postData = { drive_id: drive_id, upload_id: upload_id, file_id: file_id } - let resp = await AliHttp.Post(url, postData, user_id, '') - if (resp.code == 400 || resp.code == 429) { - resp = await AliHttp.Post(url, postData, user_id, '') - } - if (AliHttp.IsSuccess(resp.code)) { - - if (resp.body.size == fileSize) { - if (fileSha1) { - - if (resp.body.content_hash && resp.body.content_hash == fileSha1) { - return true - } else { - await this.UploadFileDelete(user_id, drive_id, file_id, true).catch(() => {}) - DebugLog.mSaveDanger('UploadFileComplete', '合并文件后发现SHA1不一致,删除已上传的文件,重新上传') - return false - } - } else if (fileSize < 10485760) { - return true - } else { - return true - } - } else { - - await this.UploadFileDelete(user_id, drive_id, file_id, true).catch(() => {}) - DebugLog.mSaveDanger('UploadFileComplete', '合并文件后发现大小不一致,删除已上传的文件,重新上传') - return false - } - } else { - DebugLog.mSaveDanger('UploadFileComplete', '合并文件时出错' + resp.code + ' ' + JSON.stringify(resp.header || {}) + ' ' + JSON.stringify(resp.body || {})) - return false - } - } - - - static async UploadFilePartUrl(user_id: string, drive_id: string, file_id: string, upload_id: string, fileSize: number, uploadInfo: IUploadInfo): Promise<'neterror' | 'success' | 'error'> { - const url = 'adrive/v1.0/openFile/getUploadUrl' - const postData: { - drive_id: string - upload_id: string - file_id: string - part_info_list: { part_number: number; part_size: number }[] - } = { - drive_id: drive_id, - upload_id: upload_id, - file_id: file_id, - part_info_list: [] - } - let partIndex = 0 - - let partSize = 10485760 - while (fileSize > partSize * 8000) partSize = partSize + 10485760 - - while (partIndex * partSize < fileSize) { - postData.part_info_list.push({ part_number: partIndex + 1, part_size: partSize }) - partIndex++ - } - postData.part_info_list[partIndex - 1].part_size = fileSize - (partIndex - 1) * partSize - - const resp = await AliHttp.Post(url, postData, user_id, '') - if (resp.code >= 600 && resp.code <= 610) { - return 'neterror' - } - - if (AliHttp.IsSuccess(resp.code)) { - if (resp.body.part_info_list && resp.body.part_info_list.length > 0) { - - const part_info_list = resp.body.part_info_list - if (uploadInfo.part_info_list.length == 0) { - - for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { - const item = part_info_list[i] - uploadInfo.part_info_list.push({ upload_url: item.upload_url, part_number: item.part_number, part_size: partSize, isupload: false }) - } - } else { - - for (let i = 0, maxi = part_info_list.length; i < maxi; i++) { - const item = part_info_list[i] - uploadInfo.part_info_list[item.part_number - 1].upload_url = item.upload_url - } - } - } - return 'success' - } else { - uploadInfo.part_info_list = [] - DebugLog.mSaveWarning('UploadFilePartUrl err=' + upload_id + ' ' + (resp.code || '')) - return 'error' - } - } - - - static async UploadFileListUploadedParts(user_id: string, drive_id: string, file_id: string, upload_id: string, part_number_marker: string, uploadInfo: IUploadInfo): Promise<'neterror' | 'success' | 'error'> { - if (!user_id || !drive_id || !file_id || !upload_id) return 'error' - - const url = 'adrive/v1.0/openFile/listUploadedParts' - const postData = { drive_id: drive_id, upload_id: upload_id, file_id: file_id, next_part_number_marker:part_number_marker /* 1开始 */ } - const resp = await AliHttp.Post(url, postData, user_id, '') - if (resp.code >= 600 && resp.code <= 610) { - return 'neterror' - } - if (AliHttp.IsSuccess(resp.code)) { - if (resp.body.uploaded_parts && resp.body.uploaded_parts.length > 0) { - const uploaded_parts = resp.body.uploaded_parts - for (let i = 0, maxi = uploaded_parts.length; i < maxi; i++) { - const item = uploaded_parts[i] - const part_number = item.part_number - const uploadpart = uploadInfo.part_info_list[part_number - 1] - if (uploadpart.part_size != item.part_size) { - - DebugLog.mSaveDanger('list_uploaded_parts', '分片数据错误 uploadpart=' + uploadpart.part_size + ' item=' + item.part_size) - return 'error' - } - uploadInfo.part_info_list[part_number - 1].isupload = true - } - } - if (resp.body.next_part_number_marker && parseInt(resp.body.next_part_number_marker) > 0) { - const next = resp.body.next_part_number_marker - await this.UploadFileListUploadedParts(user_id, drive_id, file_id, upload_id, next, uploadInfo).catch(() => {}) - } - return 'success' - } else { - DebugLog.mSaveWarning('UploadFileListUploadedParts err=' + upload_id + ' ' + (resp.code || '')) - return 'error' - } - } - - static isNetworkError(e: Error): boolean { - return e.message == 'Network Error' || e.message.includes('socket hang up') || e.message.includes('getaddrinfo ENOTFOUND') || e.message.includes('timeout of') || e.message.includes('connect ECONNRESET') || e.message.includes('connect ETIMEDOUT') || e.message.includes('EPIPE') - } -} diff --git a/aliyunpan/src/aliapi/uploaddisk.ts b/aliyunpan/src/aliapi/uploaddisk.ts index f656a22ba6..44f2128d02 100644 --- a/aliyunpan/src/aliapi/uploaddisk.ts +++ b/aliyunpan/src/aliapi/uploaddisk.ts @@ -4,23 +4,22 @@ import { OpenFileHandle } from '../utils/filehelper' import { FileHandle, FileReadResult } from 'fs/promises' import { IUploadInfo } from './models' import AliUpload from './upload' -/*import HttpsProxyAgent from 'https-proxy-agent' -import { SocksProxyAgent } from 'socks-proxy-agent' -import { useSettingStore } from '../store'*/ import DBCache from '../utils/dbcache' import UserDAL from '../user/userdal' import { Sleep } from '../utils/format' import AliUploadHashPool from './uploadhashpool' import nodehttps from 'https' +import type { ClientRequest } from 'http' import path from 'path' import { Howl } from 'howler' import { useSettingStore } from '../store' -import AliUploadOpenApi from "./uploadOpenApi"; +import FlowEnc from '../module/flow-enc' +import { getFlowEnc } from '../utils/proxyhelper' const sound = new Howl({ src: ['./audio/upload_finished.mp3'], // 音频文件路径 autoplay: false, // 是否自动播放 - volume: 1.0, // 音量,范围 0.0 ~ 1.0 + volume: 1.0 // 音量,范围 0.0 ~ 1.0 }) const filePosMap = new Map() @@ -28,35 +27,41 @@ let UploadSpeedTotal = 0 export default class AliUploadDisk { static async UploadOneFile(uploadInfo: IUploadInfo, fileui: IUploadingUI): Promise { - if (uploadInfo.part_info_list.length > 1) return AliUploadDisk.UploadOneFileBig(uploadInfo, fileui) + const flowEnc = getFlowEnc(fileui.user_id, fileui.File.size, fileui.encType) + if (uploadInfo.part_info_list.length > 1) { + return AliUploadDisk.UploadOneFileBig(uploadInfo, fileui, flowEnc) + } const upload_url = uploadInfo.part_info_list[0].upload_url const fileHandle = await OpenFileHandle(path.join(fileui.localFilePath, fileui.File.partPath)) if (fileHandle.error) return fileHandle.error - filePosMap.set(fileui.UploadID, 0) let isok = '' for (let i = 0; i < 3; i++) { - isok = await AliUploadDisk.UploadOneFilePartNode(fileui.user_id, fileui.UploadID, fileHandle.handle, 0, fileui.File.size, upload_url) + isok = await AliUploadDisk.UploadOneFilePartNode( + fileui, flowEnc, fileHandle.handle, + 0, fileui.File.size, upload_url + ) if (isok == 'success') { break } } if (fileHandle.handle) await fileHandle.handle.close() - - - return AliUploadOpenApi.UploadFileComplete(fileui.user_id, fileui.drive_id, fileui.Info.up_file_id, fileui.Info.up_upload_id, fileui.File.size, uploadInfo.sha1) - .then((isSuccess) => { + return AliUpload.UploadFileComplete( + fileui.user_id, fileui.drive_id, + fileui.Info.up_file_id, fileui.Info.up_upload_id, + fileui.File.size, uploadInfo.sha1 + ) + .then(async (isSuccess) => { fileui.File.uploaded_file_id = fileui.Info.up_file_id fileui.File.uploaded_is_rapid = false fileui.Info.up_file_id = '' fileui.Info.up_upload_id = '' if (isSuccess) { - if (useSettingStore().downFinishAudio && !sound.playing()) { + if (useSettingStore().downFinishAudio && !sound.playing()) { sound.play() } return 'success' - } - else return '合并文件时出错,请重试' + } else return '合并文件时出错,请重试' }) .catch((err: any) => { DebugLog.mSaveDanger('合并文件时出错', err) @@ -64,43 +69,37 @@ export default class AliUploadDisk { }) } - - static async UploadOneFileBig(uploadInfo: IUploadInfo, fileui: IUploadingUI): Promise { + static async UploadOneFileBig(uploadInfo: IUploadInfo, fileui: IUploadingUI, flowEnc: FlowEnc | null): Promise { filePosMap.set(fileui.UploadID, 0) const fileHandle = await OpenFileHandle(path.join(fileui.localFilePath, fileui.File.partPath)) if (fileHandle.error) return fileHandle.error - const fileSize = fileui.File.size - for (let i = 0, maxi = uploadInfo.part_info_list.length; i < maxi; i++) { let part = uploadInfo.part_info_list[i] - const partStart = (part.part_number - 1) * part.part_size const partEnd = partStart + part.part_size - const part_size = partEnd > fileSize ? fileSize - partStart : part.part_size - + const partSize = partEnd > fileSize ? fileSize - partStart : part.part_size if (part.isupload) { - filePosMap.set(fileui.UploadID, partStart + part_size) + filePosMap.set(fileui.UploadID, partStart + partSize) } else { - - - const url = part.upload_url let expires = url.substring(url.indexOf('x-oss-expires=') + 'x-oss-expires='.length) expires = expires.substring(0, expires.indexOf('&')) const lastTime = parseInt(expires) - Date.now() / 1000 - if (lastTime < 5 * 60) { - - await AliUploadOpenApi.UploadFilePartUrl(fileui.user_id, fileui.drive_id, fileui.Info.up_file_id, fileui.Info.up_upload_id, fileui.File.size, uploadInfo).catch(() => {}) + await AliUpload.UploadFilePartUrl( + fileui.user_id, fileui.drive_id, fileui.Info.up_file_id, + fileui.Info.up_upload_id, fileui.File.size, uploadInfo + ).catch() if (uploadInfo.part_info_list.length == 0) return '获取分片信息失败,请重试' part = uploadInfo.part_info_list[i] } let isok = '' for (let j = 0; j < 3; j++) { - isok = await AliUploadDisk.UploadOneFilePartNode(fileui.user_id, fileui.UploadID, fileHandle.handle, partStart, part_size, part.upload_url) - // isok = await AliUploadDisk.UploadOneFilePartNodeXHR(file.File.user_id, file.UploadID, fileHandle.handle, partStart, part_size, part.upload_url) - + isok = await AliUploadDisk.UploadOneFilePartNode( + fileui, flowEnc, fileHandle.handle, + partStart, partSize, part.upload_url + ) if (isok == 'success') { part.isupload = true break @@ -108,7 +107,7 @@ export default class AliUploadDisk { if (!fileui.IsRunning) break } if (!fileui.IsRunning) break - if (part.isupload == false) { + if (!part.isupload) { if (fileHandle.handle) await fileHandle.handle.close() return isok } @@ -116,16 +115,14 @@ export default class AliUploadDisk { } if (fileHandle.handle) await fileHandle.handle.close() if (!fileui.IsRunning) return '已暂停' - for (let i = 0, maxi = uploadInfo.part_info_list.length; i < maxi; i++) { - if (uploadInfo.part_info_list[i].isupload == false) { + if (!uploadInfo.part_info_list[i].isupload) { return '有分片上传失败,请重试' } } - if (!uploadInfo.sha1) { + if (!fileui.encType && !uploadInfo.sha1) { if (fileui.File.size >= 1024000) { - const prehash = await AliUploadHashPool.GetFilePreHash(path.join(fileui.localFilePath, fileui.File.partPath)) if (fileui.File.size >= 10240000 && !prehash.startsWith('error')) { uploadInfo.sha1 = await DBCache.getFileHash(fileui.File.size, fileui.File.mtime, prehash, path.basename(fileui.File.name)) @@ -133,16 +130,14 @@ export default class AliUploadDisk { } } - - return AliUploadOpenApi.UploadFileComplete(fileui.user_id, fileui.drive_id, fileui.Info.up_file_id, fileui.Info.up_upload_id, fileui.File.size, uploadInfo.sha1) - .then((isSuccess) => { + return AliUpload.UploadFileComplete(fileui.user_id, fileui.drive_id, fileui.Info.up_file_id, fileui.Info.up_upload_id, fileui.File.size, uploadInfo.sha1) + .then(async (isSuccess) => { if (isSuccess) { if (useSettingStore().downFinishAudio && !sound.playing()) { sound.play() } return 'success' - } - else return '合并文件时出错,请重试' + } else return '合并文件时出错,请重试' }) .catch((err: any) => { DebugLog.mSaveDanger('合并文件时出错', err) @@ -151,9 +146,9 @@ export default class AliUploadDisk { } - static UploadOneFilePartNode(user_id: string, UploadID: number, fileHandle: FileHandle, partStart: number, partSize: number, upload_url: string): Promise { + static UploadOneFilePartNode(fileui: IUploadingUI, flowEnc: FlowEnc | null, fileHandle: FileHandle, partStart: number, partSize: number, upload_url: string): Promise { return new Promise(async (resolve) => { - const token = await UserDAL.GetUserTokenFromDB(user_id) + const token = await UserDAL.GetUserTokenFromDB(fileui.user_id) if (!token || !token.access_token) { resolve('找不到上传token,请重试') return @@ -163,35 +158,28 @@ export default class AliUploadDisk { method: 'PUT', strictSSL: false, rejectUnauthorized: false, - timeout: 15000 , + timeout: 15000, headers: { - 'Content-Type': '' , + 'Content-Type': '', 'Content-Length': partSize, - 'Transfer-Encoding': 'chunked' , + 'Transfer-Encoding': 'chunked', Authorization: token.token_type + ' ' + token.access_token, Connection: 'keep-alive' } } - - /*const settingStore = useSettingStore() - const proxy = settingStore.proxyUseProxy ? settingStore.getProxy() : undefined - if (proxy) { - if (settingStore.proxyType.startsWith('http')) { - const agenth = HttpsProxyAgent(proxy) - option = Object.assign(option, { agent: agenth }) - } else { - const agents = new SocksProxyAgent(proxy) - option = Object.assign(option, { agent: agents }) - } - }*/ - - const winfo = { UploadID, isstop: false, partSize, partStart, buff: Buffer.alloc(40960) } - const req = nodehttps.request(upload_url, option, function (res: any) { + const winfo = { + UploadID: fileui.UploadID, + isstop: false, + partSize, partStart, + buff: Buffer.alloc(40960), + flowEnc: flowEnc + } + const req: ClientRequest = nodehttps.request(upload_url, option, function(res: any) { let _data = '' - res.on('data', function (chunk: string) { + res.on('data', function(chunk: string) { _data += chunk }) - res.on('end', function () { + res.on('end', function() { winfo.isstop = true if (res.statusCode == 200) { resolve('success') @@ -211,7 +199,7 @@ export default class AliUploadDisk { resolve('分片上传失败,稍后重试' + message) }) - while (winfo.partSize > 0 && winfo.isstop == false) { + while (winfo.partSize > 0 && !winfo.isstop) { const result = await AliUploadDisk._WriteToRequest(req, fileHandle, winfo) if (result != 'success') { resolve('读取文件数据失败,请重试') @@ -222,19 +210,28 @@ export default class AliUploadDisk { }) } - static async _WriteToRequest(req: any, fileHandle: FileHandle, winfo: { UploadID: number; isstop: boolean; partSize: number; partStart: number; buff: Buffer }): Promise { + static async _WriteToRequest(req: ClientRequest, fileHandle: FileHandle, winfo: { + UploadID: number; + isstop: boolean; + partSize: number; + partStart: number; + buff: Buffer, + flowEnc: FlowEnc | null + }): Promise { return new Promise((resolve) => { try { + const flowEnc = winfo.flowEnc const redLen = Math.min(40960, winfo.partSize) if (redLen != winfo.buff.length) winfo.buff = Buffer.alloc(redLen) fileHandle .read(winfo.buff, 0, redLen, winfo.partStart) - .then((rbuff: FileReadResult) => { + .then(async (rbuff: FileReadResult) => { if (redLen == rbuff.bytesRead) { winfo.partStart += redLen winfo.partSize -= redLen const uploadpos = winfo.partStart - req.write(rbuff.buffer, async function () { + const bufferData = winfo.flowEnc ? winfo.flowEnc.encryptBuff(rbuff.buffer) : rbuff.buffer + req.write(bufferData, async function() { filePosMap.set(winfo.UploadID, uploadpos) UploadSpeedTotal += redLen window.speedLimte -= redLen @@ -249,114 +246,19 @@ export default class AliUploadDisk { resolve('读取文件数据失败,请重试') } }) - .catch(() => { + .catch((error) => { + console.error(error) winfo.isstop = true resolve('读取文件数据失败,请重试') }) - } catch { + } catch (error) { + console.error(error) winfo.isstop = true resolve('读取文件数据失败,请重试') } }) } - static UploadOneFilePartNodeXHR(user_id: string, UploadID: number, fileHandle: FileHandle, partStart: number, partSize: number, upload_url: string): Promise { - return new Promise(async (resolve) => { - const token = await UserDAL.GetUserTokenFromDB(user_id) - if (!token || !token.access_token) { - resolve('找不到上传token,请重试') - return - } - const winfo = { UploadID, isstop: false, partSize: partSize, partStart: partStart, buff: Buffer.alloc(40960) } - const client = new XMLHttpRequest() - client.open('PUT', upload_url) - client.timeout = 15000 - client.setRequestHeader('ContenpartSize', partSize.toString()) - client.setRequestHeader('Content-Type', '') - client.onreadystatechange = function () { - switch (client.readyState) { - case 1: // OPENED - // do something - break - case 2: // HEADERS_RECEIVED - // do something - break - case 3: // LOADING - // do something - break - case 4: // DONE - // do something - break - } - } - client.upload.onprogress = function updateProgress(event) { - if (event.lengthComputable) { - const completedPercent = event.loaded / event.total - console.log('onprogress', event, completedPercent) - filePosMap.set(winfo.UploadID, partStart + event.loaded) - } - } - - client.onabort = function () { - resolve('用户暂停') - } - client.ontimeout = function () { - resolve('网络超时') - } - client.onerror = function () { - resolve('网络出错') - } - - client.onloadend = function () { - - if ((client.status >= 200 && client.status < 300) || client.status == 409) { - resolve('success') - } else { - resolve('分片上传失败,稍后重试' + client.status) - DebugLog.mSaveDanger('分片上传失败,稍后重试' + client.status) - } - } - - const data = await this._ReadPartBuffer(fileHandle, winfo) - if (data != 'success') resolve(data) - else { - try { - client.send(winfo.buff) - } catch (err: any) { - console.log('send', err) - resolve('联网发送失败,请重试') - } - } - }) - } - - static async _ReadPartBuffer(fileHandle: FileHandle, winfo: { UploadID: number; isstop: boolean; partSize: number; partStart: number; buff: Buffer }): Promise { - return new Promise((resolve) => { - try { - const redLen = winfo.partSize - if (redLen != winfo.buff.length) winfo.buff = Buffer.alloc(redLen) - fileHandle - .read(winfo.buff, 0, redLen, winfo.partStart) - .then((rbuff: FileReadResult) => { - if (redLen == rbuff.bytesRead) { - resolve('success') - } else { - winfo.isstop = true - resolve('读取文件数据失败,请重试') - } - }) - .catch(() => { - winfo.isstop = true - resolve('读取文件数据失败,请重试') - }) - } catch { - winfo.isstop = true - resolve('读取文件数据失败,请重试') - } - }) - } - - static GetFileUploadSpeed(UploadID: number): number { return filePosMap.get(UploadID) || 0 } @@ -368,7 +270,7 @@ export default class AliUploadDisk { static GetFileUploadSpeedTotal(): number { - const speed = UploadSpeedTotal + 0 + const speed = Number(UploadSpeedTotal) UploadSpeedTotal = 0 return speed } diff --git a/aliyunpan/src/aliapi/uploadhashpool.ts b/aliyunpan/src/aliapi/uploadhashpool.ts index 50c6a0c2aa..f87a97e394 100644 --- a/aliyunpan/src/aliapi/uploadhashpool.ts +++ b/aliyunpan/src/aliapi/uploadhashpool.ts @@ -2,8 +2,8 @@ import { IUploadingUI } from '../utils/dbupload' import { OpenFileHandle } from '../utils/filehelper' import DBCache from '../utils/dbcache' import Sha1WorkerPool from '../utils/sha1workerpool' -const path = window.require('path') -const crypto = window.require('crypto') +import path from 'node:path' +import crypto from 'crypto' const sha1PosMap = new Map() diff --git a/aliyunpan/src/aliapi/uploadmem.ts b/aliyunpan/src/aliapi/uploadmem.ts index 3f797f6df3..5ca6e7bc0d 100644 --- a/aliyunpan/src/aliapi/uploadmem.ts +++ b/aliyunpan/src/aliapi/uploadmem.ts @@ -3,25 +3,29 @@ import DebugLog from '../utils/debuglog' import axios from 'axios' import AliUpload from './upload' import AliUploadHashPool from './uploadhashpool' -import AliUploadOpenApi from "./uploadOpenApi"; +import { getFlowEnc } from '../utils/proxyhelper' export default class AliUploadMem { - static async UploadMem(user_id: string, drive_id: string, parent_file_id: string, CreatFileName: string, context: string) { + static async UploadMem(user_id: string, drive_id: string, parent_file_id: string, CreatFileName: string, context: string, encType: string = '') { const token = await UserDAL.GetUserTokenFromDB(user_id) - if (!token || !token.access_token || !token.access_token_v2) return '账号失效,操作取消' + if (!token || !token.access_token) return '账号失效,操作取消' let hash = 'DA39A3EE5E6B4B0D3255BFEF95601890AFD80709' let proof = '' let buff = Buffer.from([]) if (context.length > 0) { buff = Buffer.from(context, 'utf-8') - const dd = await AliUploadHashPool.GetBuffHashProof(token!.access_token_v2, buff) + if (encType) { + let flowEnc = getFlowEnc(user_id, buff.length, encType) + buff = flowEnc && flowEnc.encryptBuff(buff) || buff + } + const dd = await AliUploadHashPool.GetBuffHashProof(token!.access_token, buff) hash = dd.sha1 proof = dd.proof_code } const size = buff.length - const upinfo = await AliUploadOpenApi.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, CreatFileName, size, hash, proof, 'refuse') + const upinfo = await AliUpload.UploadCreatFileWithFolders(user_id, drive_id, parent_file_id, CreatFileName, size, hash, proof, 'refuse', encType) if (upinfo.errormsg != '') { return upinfo.errormsg } @@ -33,7 +37,6 @@ export default class AliUploadMem { responseType: 'text', timeout: 30000, headers: { - 'Content-Type': '', Authorization: token!.token_type + ' ' + token!.access_token } @@ -41,7 +44,7 @@ export default class AliUploadMem { .catch(function (err: any) { DebugLog.mSaveDanger('UploadMemError', err) }) - const result = await AliUploadOpenApi.UploadFileComplete(user_id, drive_id, upinfo.file_id, upinfo.upload_id, size, hash) + const result = await AliUpload.UploadFileComplete(user_id, drive_id, upinfo.file_id, upinfo.upload_id, size, hash) if (result) return 'success' else return '合并文件失败' } diff --git a/aliyunpan/src/aliapi/user.ts b/aliyunpan/src/aliapi/user.ts index 51c6581bc3..d75d7ba75a 100644 --- a/aliyunpan/src/aliapi/user.ts +++ b/aliyunpan/src/aliapi/user.ts @@ -1,25 +1,30 @@ import UserDAL from '../user/userdal' import { humanDateTime, humanDateTimeDateStr, humanSize, Sleep } from '../utils/format' import { ITokenInfo } from '../user/userstore' -import AliHttp, {IUrlRespData} from './alihttp' +import AliHttp from './alihttp' import message from '../utils/message' import DebugLog from '../utils/debuglog' import { IAliUserDriveCapacity, IAliUserDriveDetails } from './models' import { GetSignature } from './utils' import getUuid from 'uuid-by-string' -import Config from "../utils/config"; +import { useSettingStore } from '../store' +import Config from '../config' export const TokenReTimeMap = new Map() export const TokenLockMap = new Map() - -export const TokenReTimeMapV2 = new Map() -export const TokenLockMapV2 = new Map() +export const OpenApiTokenReTimeMap = new Map() +export const OpenApiTokenLockMap = new Map() export const SessionLockMap = new Map() export const SessionReTimeMap = new Map() export default class AliUser { - static async ApiSessionRefreshAccount(token: ITokenInfo, showMessage: boolean): Promise { - if(!token.user_id) return false + static async ApiSessionRefreshAccount(token: ITokenInfo, showMessage: boolean, forceRefresh: boolean = false): Promise { + if (!token.user_id) return false + if (!forceRefresh && new Date(token.session_expires_in).getTime() >= Date.now()) return true + if (forceRefresh) { + SessionLockMap.delete(token.user_id) + SessionReTimeMap.delete(token.user_id) + } while (true) { const lock = SessionLockMap.has(token.user_id) if (lock) await Sleep(1000) @@ -31,7 +36,7 @@ export default class AliUser { SessionLockMap.delete(token.user_id) return true } - const apiUrl = 'https://api.aliyundrive.com/users/v1/users/device/create_session' + const apiUrl = 'https://api.alipan.com/users/v1/users/device/create_session' let { signature, publicKey } = GetSignature(0, token.user_id, token.device_id) const postData = { 'deviceName': 'Edge浏览器', @@ -42,11 +47,12 @@ export default class AliUser { SessionLockMap.delete(token.user_id) if (AliHttp.IsSuccess(resp.code)) { SessionReTimeMap.set(token.user_id, Date.now()) + token.session_expires_in = Date.now() + 30 * 60 * 1000 token.signature = signature UserDAL.SaveUserToken(token) return true } else { - DebugLog.mSaveWarning('ApiSessionRefreshAccount err=' + (resp.code || '') + ' ' + (resp.body?.code || '')) + DebugLog.mSaveWarning('ApiSessionRefreshAccount err=' + (resp.code || '') + ' ' + (resp.body?.code || ''), resp.body) if (showMessage) { message.error('刷新账号[' + token.user_name + '] session 失败') } @@ -54,84 +60,10 @@ export default class AliUser { return false } - static async ApiTokenRefreshAccountV2(token: ITokenInfo): Promise { - const postData = { - refresh_token: token.refresh_token_v2, - grant_type: 'refresh_token', - client_secret: '', - client_id: '' - } - return await AliHttp.Post(Config.accessTokenUrl, postData, '', '') - } - -// { -// "token_type": "Bearer", -// "access_token": "", -// "refresh_token": "", -// "expires_in": 7200 -// } - static async ApiTokenRefreshAccountV2_TMP(token: ITokenInfo): Promise { - const postData = { - refresh_token: token.refresh_token_v2, - grant_type: 'refresh_token' - } - return await AliHttp._Post(Config.tmpUrl, postData, '', '') - } - - static async ApiRefreshAccessTokenV2(token: ITokenInfo, showMessage: boolean, force: boolean = false): Promise { - if (!token.refresh_token_v2) return false - if (!force && token.expires_in_v2 && token.expires_in_v2 > Date.now()) { - return true - } - if (force) { - TokenLockMapV2.delete(token.user_id) - TokenReTimeMapV2.delete(token.user_id) - } - while (true) { - const lock = TokenLockMapV2.has(token.user_id) - if (lock) await Sleep(1000) - else break - } - TokenLockMapV2.set(token.user_id, Date.now()) - const time = TokenReTimeMapV2.get(token.user_id) || 0 - if (Date.now() - time < 1000 * 60 * 5) { - TokenLockMapV2.delete(token.user_id) - return true - } - - const resp = await this.ApiTokenRefreshAccountV2(token) - TokenLockMapV2.delete(token.user_id) - if (AliHttp.IsSuccess(resp.code)) { - TokenReTimeMapV2.set(token.user_id, Date.now()) - token.tokenfrom = 'account' - - token.refresh_token_v2 = resp.body.refresh_token - token.access_token_v2 = resp.body.access_token - token.expires_in_v2 = Date.now() + resp.body.expires_in * 1000 - token.token_type_v2 = resp.body.token_type - - UserDAL.SaveUserToken(token) - return true - } else { - if (resp.body?.code != 'InvalidParameter.RefreshToken') { - DebugLog.mSaveWarning('ApiTokenRefreshAccountV2 err=' + (resp.code || '') + ' ' + (resp.body?.code || '')) - } - if (showMessage) { - message.error('刷新账号[' + token.user_name + '] v2 token 失败,需要重新登录') - UserDAL.UserLogOff(token.user_id) - } else { - UserDAL.UserClearFromDB(token.user_id) - } - } - return false - } - - static async ApiRefreshAccessTokenV1(token: ITokenInfo, showMessage: boolean, force: boolean = false): Promise { + static async ApiTokenRefreshAccount(token: ITokenInfo, showMessage: boolean, forceRefresh: boolean = false): Promise { if (!token.refresh_token) return false - if (!force && token.expires_in && token.expires_in > Date.now()) { - return true - } - if (force) { + if (!forceRefresh && new Date(token.expire_time).getTime() >= Date.now()) return true + if (forceRefresh) { TokenLockMap.delete(token.user_id) TokenReTimeMap.delete(token.user_id) } @@ -147,8 +79,7 @@ export default class AliUser { return true } - const url = 'https://auth.aliyundrive.com/v2/account/token' - + const url = 'https://auth.alipan.com/v2/account/token' const postData = { refresh_token: token.refresh_token, grant_type: 'refresh_token' } const resp = await AliHttp.Post(url, postData, '', '') TokenLockMap.delete(token.user_id) @@ -157,12 +88,13 @@ export default class AliUser { token.tokenfrom = 'account' token.access_token = resp.body.access_token token.refresh_token = resp.body.refresh_token - token.expires_in = Date.now() + resp.body.expires_in * 1000 + token.expires_in = resp.body.expires_in token.token_type = resp.body.token_type token.user_id = resp.body.user_id token.user_name = resp.body.user_name token.avatar = resp.body.avatar token.nick_name = resp.body.nick_name + token.default_drive_id = resp.body.default_drive_id token.default_sbox_drive_id = resp.body.default_sbox_drive_id token.role = resp.body.role token.status = resp.body.status @@ -182,10 +114,10 @@ export default class AliUser { return true } else { if (resp.body?.code != 'InvalidParameter.RefreshToken') { - DebugLog.mSaveWarning('ApiTokenRefreshAccount err=' + (resp.code || '') + ' ' + (resp.body?.code || '')) + DebugLog.mSaveWarning('ApiTokenRefreshAccount err=' + (resp.code || '') + ' ' + (resp.body?.code || ''), resp.body) } if (showMessage) { - message.error('刷新账号[' + token.user_name + '] v1 token 失败,需要重新登录') + message.error('刷新账号[' + token.user_name + '] token 失败,需要重新登录') UserDAL.UserLogOff(token.user_id) } else { UserDAL.UserClearFromDB(token.user_id) @@ -195,40 +127,197 @@ export default class AliUser { } - static async ApiUserInfo(token: ITokenInfo): Promise { - if (!token.user_id || !token.access_token_v2) return false - const url = 'adrive/v1.0/user/getDriveInfo' - const spaceUrl = 'adrive/v1.0/user/getSpaceInfo' - const postData = '' - const resp = await AliHttp.Post(url, postData, token.user_id, '') - const spaceResp = await AliHttp.Post(spaceUrl, postData, token.user_id, '') - if (AliHttp.IsSuccess(spaceResp.code)) { - token.used_size = spaceResp.body.personal_space_info.used_size - token.total_size = spaceResp.body.personal_space_info.total_size - token.spaceinfo = humanSize(token.used_size) + ' / ' + humanSize(token.total_size) - } else { - DebugLog.mSaveWarning('getSpaceInfo err=' + (resp.code || '')) + static async OpenApiTokenRefreshAccount(token: ITokenInfo, showMessage: boolean, forceRefresh: boolean = false): Promise { + if (!token.open_api_refresh_token) return false + if (!forceRefresh && new Date(token.open_api_expires_in).getTime() >= Date.now()) return true + // 防止重复刷新 + if (forceRefresh) { + OpenApiTokenLockMap.delete(token.user_id) + OpenApiTokenReTimeMap.delete(token.user_id) } + while (true) { + const lock = OpenApiTokenLockMap.has(token.user_id) + if (lock) await Sleep(1000) + else break + } + OpenApiTokenLockMap.set(token.user_id, Date.now()) + const time = OpenApiTokenReTimeMap.get(token.user_id) || 0 + if (Date.now() - time < 1000 * 60 * 5) { + OpenApiTokenLockMap.delete(token.user_id) + return true + } + let { uiEnableOpenApiType, uiOpenApiClientId, uiOpenApiClientSecret } = useSettingStore() + let url = 'https://openapi.alipan.com/oauth/access_token' + let client_id = Config.client_id + let client_secret = Config.client_secret + if (uiEnableOpenApiType === 'custom') { + client_id = uiOpenApiClientId + client_secret = uiOpenApiClientSecret + } + const postData = { + refresh_token: token.open_api_refresh_token, + grant_type: 'refresh_token', + client_id: client_id, + client_secret: client_secret + } + const resp = await AliHttp.Post(url, postData, '', '') + OpenApiTokenLockMap.delete(token.user_id) if (AliHttp.IsSuccess(resp.code)) { - token.spu_id = '' - token.phone = resp.body.phone - token.backup_drive_id = resp.body.backup_drive_id || ''; - token.resource_drive_id = resp.body.resource_drive_id || '' - if (token.backup_drive_id === '') { - token.backup_drive_id = resp.body.default_drive_id - } - token.is_expires = resp.body.status === 'enabled' - token.name = resp.body.nick_name===''?resp.body.phone:resp.body.nick_name + const { access_token, refresh_token, token_type, expires_in } = resp.body + OpenApiTokenReTimeMap.set(token.user_id, Date.now()) + token.open_api_token_type = token_type + token.open_api_access_token = access_token + token.open_api_refresh_token = refresh_token + token.open_api_expires_in = Date.now() + expires_in * 1000 + window.WebUserToken({ + user_id: token.user_id, + name: token.user_name, + access_token: token.access_token, + open_api_access_token: token.open_api_access_token, + refresh: true + }) + UserDAL.SaveUserToken(token) return true } else { - DebugLog.mSaveWarning('ApiUserInfo err=' + (resp.code || '')) + DebugLog.mSaveWarning('OpenApiTokenRefreshAccount err=' + (resp.code || '') + ' ' + (resp.body?.code || ''), resp.body) + if (showMessage) { + message.error('刷新账号[' + token.user_name + '] OpenApiToken 失败, 请重新获取', 5) + } + } + return false + } + + static async OpenApiQrCodeUrl(client_id: string, client_secret: string, width: number = 348, height: number = 348): Promise { + const postData = { + client_id: client_id, + client_secret: client_secret, + scopes: ['user:base', 'file:all:read', 'file:all:write'], + width: width, + height: height + } + const url = 'https://openapi.alipan.com/oauth/authorize/qrcode' + const resp = await AliHttp.Post(url, postData, '', '') + if (AliHttp.IsSuccess(resp.code)) { + return resp.body.qrCodeUrl + } else { + message.error('获取二维码失败,请重新输入') + return '' + } + } + + static async OpenApiQrCodeStatus(qrCodeUrl: string): Promise { + const resp = await AliHttp.Get(qrCodeUrl + '/status', '') + const statusJudge = (status: string) => { + switch (status) { + case 'WaitLogin': + return { type: 'info', tips: '状态:等待扫码登录' } + case 'ScanSuccess': + return { type: 'warning', tips: '状态:扫码成功,点击允许' } + case 'LoginSuccess': + return { type: 'success', tips: '状态:登录成功' } + case 'QRCodeExpired': + return { type: 'error', tips: '状态:二维码过期,点击刷新' } + default: + return { type: 'error', tips: '状态:二维码过期,点击刷新' } + } + } + if (AliHttp.IsSuccess(resp.code)) { + let statusCode = resp.body.status + let statusData = statusJudge(statusCode) + return { + authCode: statusCode === 'LoginSuccess' ? resp.body.authCode : '', + statusCode: statusCode, + statusType: statusData.type || '', + statusTips: statusData.tips || '' + } + } else { + message.error('获取二维码状态失败[' + resp.body?.message + '],请检查配置') + } + return false + } + + static async OpenApiLoginByAuthCode(token: ITokenInfo, client_id: string, client_secret: string, authCode: string, issave: boolean = false): Promise { + // 构造请求体 + const postData: any = { + code: authCode, + grant_type: 'authorization_code', + client_id: client_id, + client_secret: client_secret + } + const url = 'https://openapi.alipan.com/oauth/access_token' + const resp = await AliHttp.Post(url, postData, '', '') + if (AliHttp.IsSuccess(resp.code)) { + const { access_token, refresh_token, token_type, expires_in } = resp.body + token.open_api_token_type = token_type + token.open_api_access_token = access_token + token.open_api_refresh_token = refresh_token + token.open_api_expires_in = Date.now() + expires_in * 1000 + if (issave) { + window.WebUserToken({ + user_id: token.user_id, + name: token.user_name, + access_token: token.access_token, + open_api_access_token: token.open_api_access_token, + refresh: true + }) + UserDAL.SaveUserToken(token) + } + return true + } else { + if (resp.body?.code === 'InvalidCode') { + message.error('授权码无效, 请检查配置') + } else { + message.error('登录失败[' + resp.body?.message + ']') + } + } + return false + } + + static async ApiUserInfo(token: ITokenInfo): Promise { + if (!token.user_id) return false + const url = 'v2/databox/get_personal_info' + const postData = '' + const resp = await AliHttp.Post(url, postData, token.user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + token.used_size = resp.body.personal_space_info.used_size + token.total_size = resp.body.personal_space_info.total_size + token.spu_id = resp.body.personal_rights_info.spu_id + token.is_expires = resp.body.personal_rights_info.is_expires + token.name = resp.body.personal_rights_info.name + token.spaceinfo = humanSize(token.used_size) + ' / ' + humanSize(token.total_size) + return true + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserInfo err=' + (resp.code || ''), resp.body) + } + return false + } + + static async ApiUserDriveInfo(token: ITokenInfo): Promise { + if (!token.user_id) return false + let url = '' + let need_open_api = false + if (need_open_api) { + url = 'https://openapi.alipan.com/adrive/v1.0/user/getDriveInfo' + } else { + url = 'https://user.alipan.com/v2/user/get' + } + const resp = await AliHttp.Post(url, {}, token.user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + token.default_drive_id = resp.body.default_drive_id + token.backup_drive_id = resp.body.backup_drive_id || resp.body.default_drive_id + token.resource_drive_id = resp.body.resource_drive_id + token.sbox_drive_id = resp.body.sbox_drive_id || '' + token.phone = resp.body.user_name.replace('***', '') + return true + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserDriveInfo err=' + (resp.code || ''), resp.body) } return false } static async ApiUserSign(token: ITokenInfo): Promise { if (!token.user_id) return -1 - const signUrl = 'https://member.aliyundrive.com/v1/activity/sign_in_list' + const signUrl = 'https://member.alipan.com/v1/activity/sign_in_list' const signResp = await AliHttp.Post(signUrl, {}, token.user_id, '') // console.log(JSON.stringify(resp)) if (AliHttp.IsSuccess(signResp.code)) { @@ -252,7 +341,7 @@ export default class AliUser { } let reward = '无奖励' if (!sign_data['isReward']) { - const rewardUrl = 'https://member.aliyundrive.com/v1/activity/sign_in_reward' + const rewardUrl = 'https://member.alipan.com/v1/activity/sign_in_reward' const rewardResp = await AliHttp.Post(rewardUrl, { signInDay: signInCount }, token.user_id, '') if (AliHttp.IsSuccess(rewardResp.code)) { if (!rewardResp.body || !rewardResp.body.result || !rewardResp.body.success) { @@ -260,10 +349,10 @@ export default class AliUser { return -1 } const result = rewardResp.body.result - reward = `获得【${result['name']}】 - ${result['description']}` + reward = `【${result['notice']}】` } } else { - reward = `获得【${sign_data['reward']['name']}】 - ${sign_data['reward']['description']}` + reward = `【${sign_data['reward']['notice']}】` } message.info(`【${token.nick_name || token.user_name}】本月累计签到${signInCount}次,本次签到 ${reward}`) return parseInt(sign_data['calendarDay']) @@ -274,65 +363,64 @@ export default class AliUser { } + static async ApiUserRewardSpace(user_id: string, gift_code: string) { + if (!user_id) return false + const url = 'https://member.alipan.com/v1/users/rewards' + const postData = { code: gift_code } + const resp = await AliHttp.Post(url, postData, user_id, '') + if (AliHttp.IsSuccess(resp.code)) { + if (!resp.body || !resp.body.result) { + return { status: false, message: resp.body?.message } + } + if (!resp.body.success) { + return { status: false, message: resp.body?.message } + } else { + return { status: true, message: resp.body.result?.message } + } + } else { + return { status: false, message: resp.body?.message } + } + } + static async ApiUserVip(token: ITokenInfo): Promise { if (!token.user_id) return false - const url = 'https://openapi.aliyundrive.com/v1.0/user/getVipInfo' + const url = 'business/v1.0/users/vip/info' + + const postData = {} const resp = await AliHttp.Post(url, postData, token.user_id, '') if (AliHttp.IsSuccess(resp.code)) { - - token.vipname = resp.body.identity - token.vipIcon = '' - if (resp.body.identity === 'member') { - token.vipexpire = '' + let vipList = resp.body.vipList || [] + vipList = vipList.sort((a: any, b: any) => b.expire - a.expire) + if (vipList.length > 0 && new Date(vipList[0].expire * 1000) > new Date()) { + token.vipname = vipList[0].name + token.vipIcon = resp.body.mediumIcon + token.vipexpire = humanDateTime(vipList[0].expire) } else { - token.viplevel = resp.body.level - token.vipexpire = humanDateTime(resp.body.expire) + token.vipname = '免费用户' + token.vipIcon = '' + token.vipexpire = '' } return true - } else { - DebugLog.mSaveWarning('ApiUserPic err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserPic err=' + (resp.code || ''), resp.body) } return false } - // static async ApiUserVip(token: ITokenInfo): Promise { - // if (!token.user_id) return false - // const url = 'business/v1.0/users/vip/info' - // - // - // const postData = {} - // const resp = await AliHttp.Post(url, postData, token.user_id, '') - // if (AliHttp.IsSuccess(resp.code)) { - // let vipList = resp.body.vipList || [] - // vipList = vipList.sort((a: any, b: any) => b.expire - a.expire) - // if (vipList.length > 0 && new Date(vipList[0].expire * 1000) > new Date()) { - // token.vipname = vipList[0].name - // token.vipIcon = resp.body.mediumIcon - // token.vipexpire = humanDateTime(vipList[0].expire) - // } else { - // token.vipname = '免费用户' - // token.vipIcon = '' - // token.vipexpire = '' - // } - // return true - // } else { - // DebugLog.mSaveWarning('ApiUserPic err=' + (resp.code || '')) - // } - // return false - // } - static async ApiUserPic(token: ITokenInfo): Promise { if (!token.user_id) return false const url = 'adrive/v1/user/albums_info' + + const postData = {} const resp = await AliHttp.Post(url, postData, token.user_id, '') if (AliHttp.IsSuccess(resp.code)) { token.pic_drive_id = resp.body.data.driveId return true - } else { - DebugLog.mSaveWarning('ApiUserPic err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserPic err=' + (resp.code || ''), resp.body) } return false } @@ -364,8 +452,8 @@ export default class AliUser { detail.resource_drive_used_size = resp.body.resource_drive_used_size || 0 detail.sbox_drive_used_size = resp.body.sbox_drive_used_size || 0 detail.share_album_drive_used_size = resp.body.share_album_drive_used_size || 0 - } else { - DebugLog.mSaveWarning('ApiUserDriveDetails err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserDriveDetails err=' + (resp.code || ''), resp.body) } return detail } @@ -374,9 +462,9 @@ export default class AliUser { if (!user_id) return 0 const token = await UserDAL.GetUserTokenFromDB(user_id) if (!token) return 0 - const url = 'adrive/v1.0/openFile/search' + const url = 'adrive/v3/file/search' const postData = { - drive_id: token?.backup_drive_id, + drive_id_list: [token.backup_drive_id, token.resource_drive_id], marker: '', limit: 1, all: false, @@ -389,8 +477,8 @@ export default class AliUser { try { if (AliHttp.IsSuccess(resp.code)) { return resp.body.total_count || 0 - } else { - DebugLog.mSaveWarning('ApiUserDriveFileCount err=' + category + ' ' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserDriveFileCount err=' + category + ' ' + (resp.code || ''), resp.body) } } catch (err: any) { DebugLog.mSaveDanger('ApiUserDriveFileCount' + category, err) @@ -432,8 +520,8 @@ export default class AliUser { } as IAliUserDriveCapacity) } result = result.sort((a, b) => a.latest_receive_time.localeCompare(b.latest_receive_time)) - } else { - DebugLog.mSaveWarning('ApiUserCapacityDetails err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiUserCapacityDetails err=' + (resp.code || ''), resp.body) } return result } diff --git a/aliyunpan/src/aliapi/utils.ts b/aliyunpan/src/aliapi/utils.ts index 8480bc70bc..e605cd1e06 100644 --- a/aliyunpan/src/aliapi/utils.ts +++ b/aliyunpan/src/aliapi/utils.ts @@ -1,4 +1,4 @@ -import { ITokenInfo, useFootStore } from '../store' +import { useFootStore, useSettingStore } from '../store' import UserDAL from '../user/userdal' import DebugLog from '../utils/debuglog' import { Sleep } from '../utils/format' @@ -9,53 +9,45 @@ import { IAliBatchResult } from './models' import { SHA256 } from 'crypto-js' import { ecdsaSign, publicKeyCreate } from 'secp256k1' +import { IAliFileItem, IAliGetFileModel } from './alimodels' +import { decodeName, encodeName } from '../module/flow-enc/utils' +import path from 'path' +import mime from 'mime-types' +import { getEncPassword, getEncType } from '../utils/proxyhelper' -export declare type Drive = 'pan' | 'pic' | 'safe' - - -export function GetDriveID(user_id: string, drive: Drive): string { +export function GetDriveID(user_id: string, drive: string): string { const token = UserDAL.GetUserToken(user_id) if (token) { - switch (drive) { - case 'pan': - return token.backup_drive_id - case 'pic': - return token.pic_drive_id - case 'safe': - return token.default_sbox_drive_id + if (drive.includes('backup')) { + return token.backup_drive_id + } else if (drive.includes('resource')) { + return token.resource_drive_id + } else if (drive.includes('pic')) { + return token.pic_drive_id + } else if (drive.includes('safe')) { + return token.default_sbox_drive_id } } return '' } - -export function GetDriveID2(token: ITokenInfo, driveName: string): string { - if (token) { - switch (driveName) { - case 'pan': - return token.backup_drive_id - case 'pic': - return token.pic_drive_id - case 'safe': - return token.default_sbox_drive_id - } - } - return driveName -} - export function GetDriveType(user_id: string, drive_id: string): any { const token = UserDAL.GetUserToken(user_id) if (token) { switch (drive_id) { case token.backup_drive_id: - return { title: '备份盘', key: 'backup_root' } + return { title: '备份盘', name: 'backup', key: 'backup_root' } case token.resource_drive_id: - return { title: '资源盘', key: 'resource_root' } + return { title: '资源盘', name: 'resource', key: 'resource_root' } + case token.pic_drive_id: + return { title: '全部相册', name: 'pic', key: 'pic_root' } case token.default_sbox_drive_id: - return { title: '安全盘', key: 'safe_root' } + return { title: '安全盘', name: 'safe', key: 'safe_root' } + default: + return { title: '备份盘', name: 'backup', key: 'backup_root' } } } - return { title: '', key: '' } + return { title: '备份盘', name: 'backup', key: 'backup_root' } } export function GetSignature(nonce: number, user_id: string, deviceId: string) { @@ -63,8 +55,8 @@ export function GetSignature(nonce: number, user_id: string, deviceId: string) { const hashArray = Array.from(bytes) // convert buffer to byte array // convert bytes to hex string return hashArray - .map((b) => b.toString(16).padStart(2, '0')) - .join('') + .map((b) => b.toString(16).padStart(2, '0')) + .join('') } const toU8 = (wordArray: CryptoJS.lib.WordArray) => { const words = wordArray.words @@ -83,6 +75,69 @@ export function GetSignature(nonce: number, user_id: string, deviceId: string) { return { signature, publicKey } } +export function EncodeEncName(user_id: string, name: string, isDir: boolean, encType: string, inputpassword: string = '') { + let settingStore = useSettingStore() + if (encType && settingStore.securityEncFileName) { + // 加密名称 + const splitFolder = name.split('/') + const securityPassword = getEncPassword(user_id, encType, inputpassword) + const securityEncType = settingStore.securityEncType + if (!isDir) { + return splitFolder.map(name => { + let plainName = '' + let basename = path.basename(name) + let extname = path.extname(name) + if (settingStore.securityEncFileNameHideExt) { + plainName = basename + extname = '' + } else { + plainName = basename.replace(extname, '') + } + return encodeName(securityPassword, securityEncType, plainName) + extname + }).join('/') + } else { + return splitFolder.map(name => encodeName(securityPassword, securityEncType, name)).join('/') + } + } else { + return name + } +} + +export function DecodeEncName(user_id: string, item: IAliFileItem | IAliGetFileModel, inputpassword: string = '') { + // 自动解密文件名 + let settingStore = useSettingStore() + const securityFileNameAutoDecrypt = settingStore.securityFileNameAutoDecrypt + const securityEncType = settingStore.securityEncType + let ext = '' + let mine_type = item.mime_type + if ('file_extension' in item) { + ext = item.file_extension || '' + } else if ('ext' in item) { + ext = item.ext + } + let name = item.name + let description = item.description + let need_decode = description && description.includes('xbyEncrypt') + if (need_decode && securityFileNameAutoDecrypt) { + let encType = getEncType(item) + let filename = item.name.replace(ext ? '.' + ext : '', '') + let password = getEncPassword(user_id, encType, inputpassword) + let realName = decodeName(password, securityEncType, filename) || item.name + if (ext) { + name = realName + '.' + ext + } else if (path.extname(realName)) { + // 修复加密后的扩展 + name = realName + ext = path.extname(realName).replace('.', '') + mine_type = mime.lookup(ext) || 'application/oct-stream' + } else { + name = realName + } + return { name: name, mine_type, ext } + } + return { name: item.name, mine_type, ext } +} + async function _ApiBatch(postData: string, user_id: string, share_token: string, result: IAliBatchResult): Promise { if (!user_id && !share_token) return const url = 'v2/batch' @@ -97,24 +152,46 @@ async function _ApiBatch(postData: string, user_id: string, share_token: string, if (respi.body && respi.body.async_task_id) { - result.async_task.push({ drive_id: respi.body.drive_id || '', file_id: respi.id, task_id: respi.body.async_task_id, newdrive_id: respi.body.drive_id || '', newfile_id: respi.body.file_id || '' }) + result.async_task.push({ + drive_id: respi.body.drive_id || '', + file_id: respi.id, + task_id: respi.body.async_task_id, + newdrive_id: respi.body.drive_id || '', + newfile_id: respi.body.file_id || '' + }) } else if (respi.body && respi.body.share_id && respi.body.share_msg) { - result.reslut.push({ id: respi.id, share_id: respi.body.share_id, share_pwd: respi.body.share_pwd || '', share_url: respi.body.share_url, expiration: respi.body.expiration || '', share_name: respi.body.share_name || '' }) + result.reslut.push({ + id: respi.id, + share_id: respi.body.share_id, + share_pwd: respi.body.share_pwd || '', + share_url: respi.body.share_url, + expiration: respi.body.expiration || '', + share_name: respi.body.share_name || '' + }) } else if (respi.body) { - result.reslut.push({ id: respi.id, file_id: respi.body.file_id, name: respi.body.name || '', body: respi.body }) + result.reslut.push({ + id: respi.id, + file_id: respi.body.file_id, + name: respi.body.name || '', + body: respi.body + }) } else if (respi.id) { result.reslut.push({ id: respi.id, file_id: respi.id }) } } else { const respi = responses[i] const logmsg = (respi.body.code || '') + ' ' + (respi.body.message || '') - if (logmsg.includes('File under sync control') == false) DebugLog.mSaveDanger(logmsg) - if (respi.body && respi.body.code) result.error.push({ id: respi.body.id || respi.id, code: respi.body.code, message: respi.body.message }) + if (!logmsg.includes('File under sync control')) DebugLog.mSaveDanger(logmsg) + if (respi.body && respi.body.code) result.error.push({ + id: respi.body.id || respi.id, + code: respi.body.code, + message: respi.body.message + }) } } - } else { - DebugLog.mSaveWarning('_ApiBatch err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('_ApiBatch err=' + (resp.code || ''), resp.body) } } @@ -142,8 +219,7 @@ export async function ApiBatch(title: string, batchList: string[], user_id: stri } if (allTask.length >= 3) { - - await Promise.all(allTask).catch(() => {}) + await Promise.all(allTask).catch() allTask = [] if (title != '') message.loading(title + ' 执行中...(' + result.count.toString() + ')', 60, loadingKey) } @@ -152,7 +228,8 @@ export async function ApiBatch(title: string, batchList: string[], user_id: stri postData += '],"resource":"file"}' allTask.push(_ApiBatch(postData, user_id, share_token, result)) } - if (allTask.length > 0) await Promise.all(allTask).catch(() => {}) + if (allTask.length > 0) await Promise.all(allTask).catch(() => { + }) if (result.async_task.length > 0) { if (title != '' || share_token != '') message.warning(title + ' 异步执行中(' + result.async_task.length + ')', 2, loadingKey) @@ -218,7 +295,13 @@ export function ApiBatchMaker(url: string, idList: string[], bodymake: (file_id: const id = idList[i] if (batchSet.has(id)) continue batchSet.add(id) - batchList.push(JSON.stringify({ body: bodymake(id), headers: { 'Content-Type': 'application/json' }, id: id, method: 'POST', url })) + batchList.push(JSON.stringify({ + body: bodymake(id), + headers: { 'Content-Type': 'application/json' }, + id: id, + method: 'POST', + url + })) } batchSet.clear() return batchList @@ -232,7 +315,13 @@ export function ApiBatchMaker2(url: string, idList: string[], namelist: string[] const id = idList[i] if (batchSet.has(id)) continue batchSet.add(id) - batchList.push(JSON.stringify({ body: bodymake(id, namelist[i]), headers: { 'Content-Type': 'application/json' }, id: id, method: 'POST', url })) + batchList.push(JSON.stringify({ + body: bodymake(id, namelist[i]), + headers: { 'Content-Type': 'application/json' }, + id: id, + method: 'POST', + url + })) } batchSet.clear() return batchList @@ -279,8 +368,8 @@ export async function ApiGetAsyncTask(user_id: string, async_task_id: string): P message.warning('操作部分成功 ' + resp.body.message?.replace('ErrQuotaExhausted', '网盘空间已满') || '', 5) return 'error' } - } else { - DebugLog.mSaveWarning('ApiGetAsyncTask err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiGetAsyncTask err=' + (resp.code || ''), resp.body) } return 'error' } @@ -299,8 +388,8 @@ export async function ApiGetAsyncTaskUnzip(user_id: string, drive_id: string, fi message.warning('操作部分成功 ' + resp.body.message?.replace('ErrQuotaExhausted', '网盘空间已满') || '', 5) return 'error' } - } else { - DebugLog.mSaveWarning('ApiGetAsyncTaskUnzip err=' + (resp.code || '')) + } else if (!AliHttp.HttpCodeBreak(resp.code)) { + DebugLog.mSaveWarning('ApiGetAsyncTaskUnzip err=' + (resp.code || ''), resp.body) } return 'error' } diff --git a/aliyunpan/src/assets/fileitem.css b/aliyunpan/src/assets/fileitem.css index 1887382a3d..2ae1edc652 100644 --- a/aliyunpan/src/assets/fileitem.css +++ b/aliyunpan/src/assets/fileitem.css @@ -285,11 +285,8 @@ body[arco-theme='dark'] .fileitem.focus { .cff5722 { color: #ff5722; } -.c5b89b8 { - color: #5b89b8; -} -.cvideo { - color: #5b89b8; +.ce74c3c { + color: #e74c3c; } .griditemparent { @@ -318,7 +315,7 @@ body[arco-theme='dark'] .fileitem.focus { } .griditem { - border-radius: 8px; + border-radius: 17.2px; position: relative; height: 100%; border: 1px dotted transparent; @@ -421,6 +418,126 @@ body[arco-theme='dark'] .griditem .select:hover { overflow: hidden; } +.griditem.movie { + width: 200px; + height: 320px; + flex-shrink: 0; + flex-grow: 0; +} + +.movieicon { + left: 2px; + right: 2px; + top: -10px; + position: absolute; + text-align: center; + height:100% !important; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; + flex-grow: 0; +} +.movieicon i { + font-size: 56px; + opacity: 0.75; +} +.movieicon .iconfile-folder { + color: #ffb74d !important; +} + +.movieicon img { + display: block; + width: 170px !important; + height: 240px !important; + border-radius: 20px; + position: relative; + border: 1px solid rgba(132, 133, 141, 0.16); +} + +.movieicon .playicon { + cursor: pointer; + font-size: 20px; + height: 28px; + width: 28px; + background: rgba(37, 38, 43, 0.36); + backdrop-filter: blur(10px); + border-radius: 50%; + color: rgb(255, 255, 255); +} +.movieicon .playicon svg { + width: 1em; + height: 1em; + fill: currentColor; + overflow: hidden; +} + +.movieprogress { + width: 100%; + text-align: center; + font-size: 14px; + line-height: 18px; + max-width: 100%; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + overflow-wrap: break-word; + /*margin-bottom: 2px;*/ + color: var(--color-text-1); + padding: 0 8px; + position: absolute; + bottom: 45px; + font-weight: bold; +} +.moviename { + width: 100%; + text-align: center; + font-size: 14px; + line-height: 18px; + max-width: 100%; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + /*overflow: hidden;*/ + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + overflow-wrap: normal; + white-space: nowrap; + /* margin-bottom: 2px; */ + color: var(--color-text-1); + padding: 0 8px; + position: absolute; + bottom: 25px; + font-weight: bold; +} +.moviename > div { + cursor: pointer; + text-align: center; + word-break: break-all; + width: 150px; + margin-left: 15px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.moviename > div:hover { + color: #3482f0; +} + +.movieinfo { + width: 100%; + text-align: center; + font-size: 12px; + color: var(--color-text-3); + position: absolute; + bottom: 0px; +} + + + .gridname { width: 100%; text-align: center; diff --git a/aliyunpan/src/assets/global.css b/aliyunpan/src/assets/global.css index 601b219e49..a02e55059b 100644 --- a/aliyunpan/src/assets/global.css +++ b/aliyunpan/src/assets/global.css @@ -1,149 +1,167 @@ html { - min-width: 100vw; - min-height: 100vh; - overflow: hidden; + min-width: 100vw; + min-height: 100vh; + overflow: hidden; } + body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif; - min-width: 100vw; - min-height: 100vh; - overflow: hidden; - background-color: transparent !important; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif, '黑体', '宋体', '方正黑体', '方正准圆'; + min-width: 100vw; + min-height: 100vh; + overflow: hidden; + background-color: transparent !important; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } + ::-webkit-scrollbar { - width: 10px; - height: 10px; - cursor: pointer; + width: 8px; + height: 8px; + background: transparent; } ::-webkit-scrollbar-thumb { - background: rgb(201, 201, 202); - border-radius: 5px; - cursor: default; -} -::-webkit-scrollbar-thumb:vertical { - width: 9px; -} -::-webkit-scrollbar-thumb:horizontal { - height: 9px; + background: var(--color-fill-3) content-box; + border: 2px solid transparent; + border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { - background: rgb(162, 162, 163); + background-color: var(--color-fill-4); +} + +::-webkit-scrollbar-thumb:vertical { + width: 9px; +} + +::-webkit-scrollbar-thumb:horizontal { + height: 9px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgb(162, 162, 163); } ::-webkit-scrollbar-track { - background-color: transparent; + background-color: transparent; } ::-webkit-scrollbar-corner { - background: transparent !important; + background: transparent !important; } ::-webkit-resizer { - background: transparent !important; + background: transparent !important; } ::-webkit-scrollbar-button { - display: none; + display: none; } + .flex { - display: flex; - flex: 100% 1 1; - flex-direction: row; + display: flex; + flex: 100% 1 1; + flex-direction: row; } + .flexauto { - flex-grow: 1 !important; + flex-grow: 1 !important; } + .flexnoauto { - flex-grow: 0 !important; - flex-shrink: 0 !important; - flex-basis: unset !important; + flex-grow: 0 !important; + flex-shrink: 0 !important; + flex-basis: unset !important; } + .q-electron-drag { - user-select: none; - -webkit-app-region: drag; + user-select: none; + -webkit-app-region: drag; } + .q-electron-drag--exception, .q-electron-drag button, .q-electron-drag ul, .q-electron-drag li, .q-electron-drag .arco-menu-item, .q-electron-drag .arco-avatar { - -webkit-app-region: no-drag; + -webkit-app-region: no-drag; } + * { - user-select: none; - -webkit-user-drag: none; - box-sizing: border-box; - letter-spacing: 0.1px; - -webkit-text-size-adjust: 100%; - -webkit-font-smoothing: antialiased; - font-feature-settings: 'tnum', 'zero', 'liga' 0; + user-select: none; + -webkit-user-drag: none; + box-sizing: border-box; + letter-spacing: 0.1px; + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + font-feature-settings: 'tnum', 'zero', 'liga' 0; } + #app { - height: 100vh; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; + height: 100vh; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; } + body { - --arcoblue-1: 232, 240, 255 !important; - --arcoblue-2: 205, 220, 255 !important; - --arcoblue-3: 179, 199, 255 !important; - --arcoblue-4: 152, 176, 255 !important; - --arcoblue-5: 126, 151, 255 !important; - --arcoblue-6: 99, 125, 255 !important; - --arcoblue-7: 61, 81, 210 !important; - --arcoblue-8: 32, 46, 166 !important; - --arcoblue-9: 12, 19, 121 !important; - --arcoblue-10: 0, 3, 77 !important; + --arcoblue-1: 232, 240, 255 !important; + --arcoblue-2: 205, 220, 255 !important; + --arcoblue-3: 179, 199, 255 !important; + --arcoblue-4: 152, 176, 255 !important; + --arcoblue-5: 126, 151, 255 !important; + --arcoblue-6: 99, 125, 255 !important; + --arcoblue-7: 61, 81, 210 !important; + --arcoblue-8: 32, 46, 166 !important; + --arcoblue-9: 12, 19, 121 !important; + --arcoblue-10: 0, 3, 77 !important; - --foot-bg: #3c86f5; - --foot-txt: rgb(245, 245, 246); - --topshadow: rgba(0, 0, 0, 0.1); - --leftshadow: rgba(29, 35, 41, 0.05); - --rightbg2: #f2f3f5; - --listhoverbg: rgba(247, 248, 250, 0.6); - --listselectbg: rgb(240, 245, 255); - --color-text-1: var(--color-neutral-9) !important; - --color-mask-bg: rgba(29, 33, 41, 0.3) !important; + --foot-bg: #3c86f5; + --foot-txt: rgb(245, 245, 246); + --topshadow: rgba(0, 0, 0, 0.1); + --leftshadow: rgba(29, 35, 41, 0.05); + --rightbg2: #f2f3f5; + --listhoverbg: rgba(247, 248, 250, 0.6); + --listselectbg: rgb(240, 245, 255); + --color-text-1: var(--color-neutral-9) !important; + --color-mask-bg: rgba(29, 33, 41, 0.3) !important; } + body[arco-theme='dark'] { - color-scheme: dark; - caret-color: rgba(255, 255, 255, 0.65); - background: #23232e !important; + color-scheme: dark; + caret-color: rgba(255, 255, 255, 0.65); + background: #23232e !important; - --color-text-2: rgba(255, 255, 255, 0.8) !important; - color: var(--color-text-2) !important; - --color-menu-light-bg: #30303d !important; - --color-bg-1: #23232e !important; - --color-bg-popup: #2d2d3b !important; - --color-spin-layer-bg: rgba(51, 51, 51, 0.3) !important; + --color-text-2: rgba(255, 255, 255, 0.8) !important; + color: var(--color-text-2) !important; + --color-menu-light-bg: #30303d !important; + --color-bg-1: #23232e !important; + --color-bg-popup: #2d2d3b !important; + --color-spin-layer-bg: rgba(51, 51, 51, 0.3) !important; - --foot-bg: #17171f; - --foot-txt: rgb(210, 210, 211); - --topshadow: rgba(0, 0, 0, 0.5); - --leftshadow: rgba(0, 0, 0, 0.2); - --rightbg2: rgba(255, 255, 255, 0.05); - --listhoverbg: rgba(76, 77, 97, 0.3); - --listselectbg: rgba(85, 86, 109, 0.7); - --color-text-1: rgba(255, 255, 255, 0.8) !important; - --color-mask-bg: rgba(29, 33, 41, 0.5) !important; + --foot-bg: #17171f; + --foot-txt: rgb(210, 210, 211); + --topshadow: rgba(0, 0, 0, 0.5); + --leftshadow: rgba(0, 0, 0, 0.2); + --rightbg2: rgba(255, 255, 255, 0.05); + --listhoverbg: rgba(76, 77, 97, 0.3); + --listselectbg: rgba(85, 86, 109, 0.7); + --color-text-1: rgba(255, 255, 255, 0.8) !important; + --color-mask-bg: rgba(29, 33, 41, 0.5) !important; } .arco-layout-sider .arco-menu-light .arco-menu-item.arco-menu-selected { - background: #f0f5ff; - color: var(--color-text-2); + background: #f0f5ff; + color: var(--color-text-2); } + .arco-layout-sider .arco-menu-light .arco-menu-item .arco-menu-icon, .arco-layout-sider .arco-menu-light .arco-menu-item:hover .arco-menu-icon, .arco-layout-sider .arco-menu-light .arco-menu-item.arco-menu-selected .arco-menu-icon { - color: var(--color-text-2); + color: var(--color-text-2); } body[arco-theme='dark'] .arco-layout-sider, @@ -151,416 +169,564 @@ body[arco-theme='dark'] .arco-layout-sider-light, body[arco-theme='dark'] .arco-layout-sider .arco-menu-light, body[arco-theme='dark'] .arco-layout-sider .arco-menu-light .arco-menu-item, body[arco-theme='dark'] .arco-card { - background: #23232e; + background: #23232e; } + body[arco-theme='dark'] .arco-layout-sider .arco-menu-light .arco-menu-item:hover { - background: var(--color-fill-2); + background: var(--color-fill-2); } + body[arco-theme='dark'] .arco-layout-sider .arco-menu-light .arco-menu-item.arco-menu-selected { - background: #444457; + background: #444457; } .arco-alert-error { - background-color: rgb(253, 237, 237) !important; + background-color: rgb(253, 237, 237) !important; } + .arco-alert-error .arco-alert-icon svg { - color: rgb(239, 83, 80) !important; + color: rgb(239, 83, 80) !important; } + .arco-alert-error .arco-alert-content { - color: rgb(112, 40, 39) !important; + color: rgb(112, 40, 39) !important; } .arco-alert-warning { - background-color: rgb(255, 244, 229) !important; + background-color: rgb(255, 244, 229) !important; } + .arco-alert-warning .arco-alert-icon svg { - color: rgb(255, 152, 0) !important; + color: rgb(255, 152, 0) !important; } + .arco-alert-warning .arco-alert-content { - color: rgb(117, 69, 1) !important; + color: rgb(117, 69, 1) !important; } .arco-alert-success { - background-color: rgb(237, 247, 237) !important; + background-color: rgb(237, 247, 237) !important; } + .arco-alert-success .arco-alert-icon svg { - color: rgb(76, 175, 80) !important; + color: rgb(76, 175, 80) !important; } + .arco-alert-success .arco-alert-content { - color: rgb(38, 87, 40) !important; + color: rgb(38, 87, 40) !important; } .arco-alert-info .arco-alert-content { - color: rgb(14, 22, 136) !important; + color: rgb(14, 22, 136) !important; } body[arco-theme='dark'] .arco-alert-error { - background-color: rgb(63, 32, 32) !important; + background-color: rgb(63, 32, 32) !important; } + body[arco-theme='dark'] .arco-alert-error .arco-alert-icon svg { - color: rgb(244, 67, 54) !important; + color: rgb(244, 67, 54) !important; } + body[arco-theme='dark'] .arco-alert-error .arco-alert-content { - color: rgb(244, 199, 199) !important; + color: rgb(244, 199, 199) !important; } body[arco-theme='dark'] .arco-alert-warning { - background-color: rgb(66, 49, 20) !important; + background-color: rgb(66, 49, 20) !important; } + body[arco-theme='dark'] .arco-alert-warning .arco-alert-icon svg { - color: rgb(255, 167, 38) !important; + color: rgb(255, 167, 38) !important; } + body[arco-theme='dark'] .arco-alert-warning .arco-alert-content { - color: rgb(255, 226, 183) !important; + color: rgb(255, 226, 183) !important; } body[arco-theme='dark'] .arco-alert-success { - background-color: rgb(39, 61, 42) !important; + background-color: rgb(39, 61, 42) !important; } + body[arco-theme='dark'] .arco-alert-success .arco-alert-icon svg { - color: rgb(102, 187, 106) !important; + color: rgb(102, 187, 106) !important; } + body[arco-theme='dark'] .arco-alert-success .arco-alert-content { - color: rgb(204, 232, 205) !important; + color: rgb(204, 232, 205) !important; } body[arco-theme='dark'] .arco-alert-info .arco-alert-content { - color: rgb(205, 220, 255) !important; + color: rgb(205, 220, 255) !important; } .arco-select, .arco-menu, .arco-radio-group-button { - user-select: none; - -webkit-user-drag: none; + user-select: none; + -webkit-user-drag: none; } .arco-spin:focus, .arco-list:focus { - outline: none; + outline: none; } button1.arco-btn:focus, button1:focus { - outline-style: dotted !important; - outline-width: 1px !important; - outline-color: rgb(var(--primary-6)) !important; - outline-offset: 2px !important; + outline-style: dotted !important; + outline-width: 1px !important; + outline-color: rgb(var(--primary-6)) !important; + outline-offset: 2px !important; } .arco-modal { - width: auto !important; + width: auto !important; } + .arco-modal.arco-modal-fullscreen { - width: 100vw !important; - height: 100vh !important; - border-radius: 0 !important; + width: 100vw !important; + height: 100vh !important; + border-radius: 0 !important; } + .arco-modal-wrapper { - overflow: hidden !important; + overflow: hidden !important; } body[arco-theme='dark'] .arco-modal { - background: #2d2d3b !important; - box-shadow: 0 2px 20px 0 rgb(0 0 0 / 45%) !important; + background: #2d2d3b !important; + box-shadow: 0 2px 20px 0 rgb(0 0 0 / 45%) !important; } + body[arco-theme='dark'] .arco-dropdown, body[arco-theme='dark'] .arco-select-dropdown, body[arco-theme='dark'] .arco-popover-popup-content { - box-shadow: 0 4px 20px rgb(0 0 0 / 45%) !important; - border: 1px solid transparent !important; + box-shadow: 0 4px 20px rgb(0 0 0 / 45%) !important; + border: 1px solid transparent !important; } .arco-radio-button.arco-radio-checked { - background-color: rgb(var(--primary-6)) !important; - color: var(--color-white) !important; + background-color: rgb(var(--primary-6)) !important; + color: var(--color-white) !important; } .arco-dropdown-option { - line-height: 32px !important; - width: calc(100% - 8px) !important; - margin: 0 4px !important; - border-radius: 3px !important; + line-height: 32px !important; + width: calc(100% - 8px) !important; + margin: 0 4px !important; + border-radius: 3px !important; } + .arco-dropdown-option.danger { - color: rgb(255, 77, 79) !important; + color: rgb(255, 77, 79) !important; } + .arco-dropdown-option.danger:hover, .arco-dropdown-option.danger:active { - color: #fff !important; - background: rgba(255, 77, 79, 0.85) !important; + color: #fff !important; + background: rgba(255, 77, 79, 0.85) !important; } .toppanbtn .arco-btn.danger:hover, .toppanbtn .arco-btn.danger:active, body[arco-theme='dark'] .toppanbtn .arco-btn.danger:hover, body[arco-theme='dark'] .toppanbtn .arco-btn.danger:active { - color: #fff !important; - background: rgba(255, 77, 79, 0.85) !important; - border-color: rgba(255, 77, 79, 0.2) !important; + color: #fff !important; + background: rgba(255, 77, 79, 0.85) !important; + border-color: rgba(255, 77, 79, 0.2) !important; } .arco-dropdown-option-suffix { - color: var(--color-text-3); - font-size: 12px; - user-select: none; + color: var(--color-text-3); + font-size: 12px; + user-select: none; } .arco-list .arco-empty { - margin-top: 15%; + margin-top: 15%; } .arco-modal { - z-index: 1001; + z-index: 1001; } + .arco-modal-close-btn { - -webkit-app-region: no-drag; - position: absolute; - right: 20px; + -webkit-app-region: no-drag; + position: absolute; + right: 20px; } .arco-modal-close-btn .arco-icon-hover { - width: 24px; - height: 24px; - line-height: 24px; - border-radius: 50%; + width: 24px; + height: 24px; + line-height: 24px; + border-radius: 50%; } + .arco-modal-close-btn .arco-icon-hover .arco-icon { - left: 6px; + left: 6px; } + .arco-modal-close-btn .arco-icon-hover::before { - display: none; + display: none; } + .arco-modal-close-btn .arco-icon-hover:hover { - background-color: var(--color-fill-2); + background-color: var(--color-fill-2); } .toppanbtns { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - padding: 0; - max-width: 100%; - min-width: 440px; - user-select: none; - padding-left: 6px; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + padding: 0; + max-width: 100%; + min-width: 440px; + user-select: none; + padding-left: 6px; } + .toppanbtn { - display: flex; - flex-direction: row; - flex-grow: 0; - flex-shrink: 0; - flex-wrap: nowrap; - border-radius: 4px; - margin-right: 12px; - user-select: none; + display: flex; + flex-direction: row; + flex-grow: 0; + flex-shrink: 0; + flex-wrap: nowrap; + border-radius: 4px; + margin-right: 12px; + user-select: none; } + .toppanbtn:last-child { - margin-right: 0; + margin-right: 0; } .toppanbtn .arco-btn { - display: inline-flex; - align-items: center; - height: 26px !important; - min-height: 26px !important; - padding: 0px 6px !important; + display: inline-flex; + align-items: center; + height: 26px !important; + min-height: 26px !important; + padding: 0px 6px !important; - font-size: 14px !important; - line-height: 18px !important; - white-space: nowrap !important; - word-break: keep-all !important; - color: rgb(var(--primary-6)); - border-color: rgba(var(--primary-6), 0.6) !important; - border-radius: 4px !important; + font-size: 14px !important; + line-height: 18px !important; + white-space: nowrap !important; + word-break: keep-all !important; + color: rgb(var(--primary-6)); + border-color: rgba(var(--primary-6), 0.6) !important; + border-radius: 4px !important; } .toppanbtn .arco-btn.iconbtn > .arco-btn-icon { - padding-right: 4px; + padding-right: 4px; } .toppanbtn .arco-btn:hover, .toppanbtn .arco-btn:active { - background: rgba(var(--primary-6), 0.1) !important; + background: rgba(var(--primary-6), 0.1) !important; } .toppanbtn .searchpan { - border: 1px solid rgb(var(--primary-6)) !important; - border-right: none !important; - border-radius: 4px !important; + border: 1px solid rgb(var(--primary-6)) !important; + border-right: none !important; + border-radius: 4px !important; } + .toppanbtn .searchpan .arco-btn.arco-input-search-btn { - border-radius: 4px !important; + border-radius: 4px !important; } + .toppanbtn .arco-input-search .arco-input-wrapper { - background: var(--color-bg-2); - border: none !important; + background: var(--color-bg-2); + border: none !important; } + .toppanbtn .arco-btn.arco-btn-primary { - border-radius: 0 !important; + border-radius: 0 !important; } + .toppanbtn .arco-btn.arco-btn-primary:hover { - background: rgb(var(--primary-5)) !important; + background: rgb(var(--primary-5)) !important; } + .toppanbtn .arco-btn.arco-btn-primary:active { - background: rgb(var(--primary-7)) !important; + background: rgb(var(--primary-7)) !important; } .toppanbtn > .arco-btn + .arco-btn { - border-left: 0 !important; + border-left: 0 !important; } + .toppanbtn > .arco-btn:not(:last-child) { - border-right: 0 !important; + border-right: 0 !important; } .toppanbtn > .arco-btn:not(:first-child) { - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; } .toppanbtn > .arco-btn:not(:last-child) { - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; } .toppanbtn .iconfont { - font-size: 18px; - line-height: 24px; + font-size: 18px; + line-height: 24px; } .toppanbtn .arco-btn > span { - line-height: 24px; + line-height: 24px; } .toppanbtn .arco-btn > .arco-btn-icon { - min-width: 18px; - margin-right: 0 !important; + min-width: 18px; + margin-right: 0 !important; } .toppanbtn .arco-btn.arco-btn-dangerous:hover, .toppanbtn .arco-btn.arco-btn-dangerous:focus { - color: #fff !important; - border-color: rgb(255, 77, 79) !important; - background: rgb(255, 77, 79) !important; + color: #fff !important; + border-color: rgb(255, 77, 79) !important; + background: rgb(255, 77, 79) !important; } .toppanbtn .arco-input-search { - width: 130px; - transition: width 0.3s; + width: 130px; + transition: width 0.3s; } + @media only screen and (max-width: 859px) { - .arco-input-wrapper { - padding-right: 6px !important; - padding-left: 6px !important; - } + .arco-input-wrapper { + padding-right: 6px !important; + padding-left: 6px !important; + } } + @media only screen and (min-width: 820px) { - .toppanbtn .arco-input-search { - width: 140px; - } + .toppanbtn .arco-input-search { + width: 140px; + } } + @media only screen and (min-width: 860px) { - .toppanbtn .arco-input-search { - width: 160px; - } + .toppanbtn .arco-input-search { + width: 160px; + } } + @media only screen and (min-width: 960px) { - .toppanbtn .arco-input-search { - width: 180px; - } - .toppanbtn .arco-input-search.arco-input-focus { - width: 200px; - } + .toppanbtn .arco-input-search { + width: 180px; + } + + .toppanbtn .arco-input-search.arco-input-focus { + width: 200px; + } } + @media only screen and (min-width: 1000px) { - .toppanbtn .arco-input-search.arco-input-focus { - width: 220px; - } + .toppanbtn .arco-input-search.arco-input-focus { + width: 220px; + } } + @media only screen and (min-width: 1200px) { - .toppanbtn .arco-input-search { - width: 200px; - } - .toppanbtn .arco-input-search.arco-input-focus { - width: 240px; - } + .toppanbtn .arco-input-search { + width: 200px; + } + + .toppanbtn .arco-input-search.arco-input-focus { + width: 240px; + } } body[arco-theme='dark'] .toppanbtn .arco-btn { - background: none !important; + background: none !important; } + body[arco-theme='dark'] .toppanbtn .arco-btn:hover, body[arco-theme='dark'] .toppanbtn .arco-btn:active { - background: #4c4c61 !important; + background: #4c4c61 !important; } + body[arco-theme='dark'] .toppanbtn .arco-btn { - color: #ffffffd9; - background: #353544; - border: 1px solid #444457; - border-color: #444457 !important; - box-shadow: 0 1px 5px rgb(0 0 0 / 20%), 0 2px 2px rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 12%) !important; + color: #ffffffd9; + background: #353544; + border: 1px solid #444457; + border-color: #444457 !important; + box-shadow: 0 1px 5px rgb(0 0 0 / 20%), 0 2px 2px rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 12%) !important; } + +body[arco-theme='dark'] .toppanarea .cell { + color: rgba(211, 216, 241, 0.45); +} + +.cell { + color: var(--color-text-3); + overflow: hidden; + text-align: center; + flex-grow: 0; + flex-shrink: 0; + display: inline-block; + line-height: 18px; + min-height: 18px; + padding: 0 4px; + justify-content: center; +} + +.cell.tiquma { + width: 60px; + font-size: 12px; +} + +.cell.filesize { + font-size: 16px; + width: 86px; + text-align: right; + flex-shrink: 0; + flex-grow: 0; + margin-right: 16px; +} + +.cell.count { + width: 70px; + font-size: 12px; + line-height: 14px; + text-align: center; + word-wrap: break-word; + word-break: keep-all; +} + +.cellcount { + align-items: center; + margin-right: 16px; +} + +.cellcount .arco-badge .arco-badge-status-text { + margin-left: 4px; + color: var(--color-text-3); + line-height: 26px; +} + +.cell.sharetime { + width: 80px; + font-size: 12px; + line-height: 14px; + text-align: right; + word-wrap: break-word; + word-break: keep-all; +} + +.cell.sharetime.active { + color: rgb(217, 48, 37); +} + +.cell.sharestate { + width: 60px; + font-size: 12px; + margin: 0 5px 0 5px; +} + +.cell.sharestate.active { + color: rgb(var(--primary-6)); +} + +.cell.sharestate.forbidden { + color: rgb(217, 48, 37); +} + +.cell.sharestate.deleted { + text-decoration: line-through; +} + +.cell.p5 { + width: 5px; +} + +.cell.pr { + width: 12px; +} + .toppanarea { - box-sizing: border-box; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - height: 40px; - color: var(--color-text-3); - line-height: 38px; - border-top: 1px solid #e5e8ed99; - border-bottom: 1px solid #e5e8ed99; + box-sizing: border-box; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + height: 40px; + color: var(--color-text-3); + line-height: 38px; + border-top: 1px solid #e5e8ed99; + border-bottom: 1px solid #e5e8ed99; } + body[arco-theme='dark'] .toppanarea { - border-top: 1px solid #e5e8ed22; - border-bottom: 1px solid #e5e8ed22; + border-top: 1px solid #e5e8ed22; + border-bottom: 1px solid #e5e8ed22; } .toppanarea > div { - display: flex; - align-items: center; - height: 38px; + display: flex; + align-items: center; + height: 38px; } + .toppanarea .selectInfo { - height: 38px; - line-height: 38px; - font-size: 14px; - overflow: hidden; - white-space: nowrap; - word-break: keep-all; + height: 38px; + line-height: 38px; + font-size: 14px; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; } + .toppanarea .cell { - font-size: 12px; + font-size: 12px; } + .toppanarea .cell.active, .toppanarea .cell.active .iconxia { - color: rgb(var(--primary-6)); + color: rgb(var(--primary-6)); } +.toppanarea .cell.order { + cursor: pointer; +} + +.toppanarea .cell.order:hover { + color: rgb(var(--primary-6)); +} + + .select, .select > button { - min-width: 34px !important; - height: 34px !important; - min-height: 34px !important; - padding: 0px !important; - color: rgb(var(--primary-6)) !important; - font-size: 14px; - line-height: 34px !important; - border: none !important; + min-width: 34px !important; + height: 34px !important; + min-height: 34px !important; + padding: 0px !important; + color: rgb(var(--primary-6)) !important; + font-size: 14px; + line-height: 34px !important; + border: none !important; } + .select:hover, .select:active, .select.active { - background: rgba(99, 125, 255, 0.1) !important; - color: rgb(var(--primary-6)) !important; + background: rgba(99, 125, 255, 0.1) !important; + color: rgb(var(--primary-6)) !important; } + .select .iconfont { - font-size: 24px; - line-height: 34px; - color: rgb(var(--primary-6)) !important; + font-size: 24px; + line-height: 34px; + color: rgb(var(--primary-6)) !important; } + .select .iconfont.iconrpic { - opacity: 0.8; + opacity: 0.8; } .vermodalhead { display: flex; @@ -568,8 +734,8 @@ body[arco-theme='dark'] .toppanarea { line-height: 48px; } .vermodal { - height: 200px; - width: 400px; + height: 50vh; + width: 520px; flex-direction: column; justify-content: center; align-items: center; @@ -590,137 +756,144 @@ body[arco-theme='dark'] .toppanarea { } .arco-tree-node-custom-icon { - margin-right: 4px !important; + margin-right: 4px !important; } .xbyleft { - box-shadow: 2px 0 8px 0 var(--leftshadow) !important; + box-shadow: 2px 0 8px 0 var(--leftshadow) !important; } + .xbyright { - padding-left: 16px; + padding-left: 16px; } + .headdesc { - box-sizing: border-box; - height: 40px; - padding: 0 0 0 12px; - color: #8a9ca5; - line-height: 40px; - user-select: none; - -webkit-user-drag: none; - white-space: nowrap; - word-break: keep-all; - overflow: hidden; + box-sizing: border-box; + height: 40px; + padding: 0 0 0 12px; + color: #8a9ca5; + line-height: 40px; + user-select: none; + -webkit-user-drag: none; + white-space: nowrap; + word-break: keep-all; + overflow: hidden; } .xbyleftmenu.arco-menu .arco-menu-inner { - padding: 0 2px 0 0 !important; + padding: 0 2px 0 0 !important; } + .xbyleftmenu.arco-menu .arco-menu-item { - padding: 0 8px; - margin-bottom: 2px; - line-height: 40px; - height: 40px; + padding: 0 8px; + margin-bottom: 2px; + line-height: 40px; + height: 40px; } .xbyleftmenu.arco-menu .arco-menu-item::after { - position: absolute; - top: 0; - left: 0; - bottom: 0; - transform: scaleY(0.0001); - opacity: 0; - transition: transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1); - content: ''; + position: absolute; + top: 0; + left: 0; + bottom: 0; + transform: scaleY(0.0001); + opacity: 0; + transition: transform 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), opacity 0.15s cubic-bezier(0.215, 0.61, 0.355, 1); + content: ''; } + .xbyleftmenu.arco-menu .arco-menu-item.arco-menu-selected::after { - transform: scaleY(1); - opacity: 1; - transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1); - border-right: 2px solid #637dff; + transform: scaleY(1); + opacity: 1; + transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1), opacity 0.15s cubic-bezier(0.645, 0.045, 0.355, 1); + border-right: 2px solid #637dff; } + .xbyleftmenu.arco-menu .arco-menu-item .arco-menu-icon { - width: 30px; - text-align: left; - margin-right: 0; - padding-left: 2px; - display: inline-block; - flex-shrink: 0; - flex-grow: 0; + width: 30px; + text-align: left; + margin-right: 0; + padding-left: 2px; + display: inline-block; + flex-shrink: 0; + flex-grow: 0; } + .xbyleftmenu.arco-menu .arco-menu-item .arco-menu-icon .iconfont { - font-size: 20px; + font-size: 20px; } .arco-select-dropdown .arco-select-option, .arco-autocomplete-popup .arco-select-popup .arco-select-option { - line-height: 28px !important; - height: 28px !important; + line-height: 28px !important; + height: 28px !important; } .arco-tabs { - position: static !important; + position: static !important; } .messagebadge { - background-color: #86909c; - line-height: 14px; - height: 14px; - position: static; - margin: 0 6px; - min-width: 20px; - padding: 0 6px; - color: var(--color-white); - font-weight: 500; - font-size: 12px; - box-shadow: 0 0 0 2px var(--color-bg-2); - box-sizing: border-box; - overflow: hidden; - text-align: center; - border-radius: 20px; - transform: translate(50%, -50%); - transform-origin: 100% 0%; + background-color: #86909c; + line-height: 14px; + height: 14px; + position: static; + margin: 0 6px; + min-width: 20px; + padding: 0 6px; + color: var(--color-white); + font-weight: 500; + font-size: 12px; + box-shadow: 0 0 0 2px var(--color-bg-2); + box-sizing: border-box; + overflow: hidden; + text-align: center; + border-radius: 20px; + transform: translate(50%, -50%); + transform-origin: 100% 0%; } .arco-modal { - border-radius: 0.75rem !important; - box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.3) !important; + border-radius: 0.75rem !important; + box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.3) !important; } .shortcut-key { - display: inline-flex; - font-family: 'Inter', sans-serif; - font-size: 12px; - background-color: #f3f4f6; - border-radius: 0.25rem; - margin-left: 0.5rem; - padding-left: 0.25rem; - padding-right: 0.25rem; + display: inline-flex; + font-family: 'Inter', sans-serif; + font-size: 12px; + background-color: #f3f4f6; + border-radius: 0.25rem; + margin-left: 0.5rem; + padding-left: 0.25rem; + padding-right: 0.25rem; } .arco-btn-text.arco-btn-status-warning:hover { - color: rgb(var(--warning-6)) !important; - background-color: var(--color-warning-light-1) !important; - border-color: var(--color-warning-light-2) !important; + color: rgb(var(--warning-6)) !important; + background-color: var(--color-warning-light-1) !important; + border-color: var(--color-warning-light-2) !important; } .arco-modal-header { - border-bottom: 1px solid transparent !important; + border-bottom: 1px solid transparent !important; } .arco-card { - border-radius: 6px !important; + border-radius: 6px !important; } + .arco-divider-text { - border-radius: 4px !important; + border-radius: 4px !important; } .workertitle { - margin-top: 64px; - color: rgb(var(--primary-6)); + margin-top: 64px; + color: rgb(var(--primary-6)); } .arco-scrollbar { - height: 100%; + height: 100%; } .settinghead { color: var(--color-text-2); diff --git a/aliyunpan/src/assets/style.css b/aliyunpan/src/assets/style.css index 312b7923d6..c8c2d4ff28 100644 --- a/aliyunpan/src/assets/style.css +++ b/aliyunpan/src/assets/style.css @@ -6,7 +6,9 @@ -ms-user-select: none; /*font-size: 16px;*/ } - +@tailwind base; +@tailwind components; +@tailwind utilities; @media screen and (max-width: 500px) { * { font-size: 17px; diff --git a/aliyunpan/src/axios.ts b/aliyunpan/src/axios.ts index 97dff065bb..636a052d37 100644 --- a/aliyunpan/src/axios.ts +++ b/aliyunpan/src/axios.ts @@ -1,50 +1,52 @@ -import axios, { AxiosInstance } from 'axios' +import axios from 'axios' +import { performance } from 'perf_hooks' -declare module '@vue/runtime-core' { - interface ComponentCustomProperties { - $axios: AxiosInstance; - } -} -let QPS = 30 -let OFFSET = 0 +let QPS = 5 +// 校准本地和服务端之间的时间差 +let OFFSET = 250 +// 间隔时间 let INTERVAL = 1000 -const qpsMap = new Map() +const qpsMap = new Map() const qpsController = () => async (config: any) => { - if (config.url.indexOf('api.aliyundrive.com') < 0 - && config.url.indexOf('openapi.aliyundrive.com') < 0) return config - if (config.url.indexOf('openapi.aliyundrive.com') < 0) { - QPS = 2 - OFFSET = 2500 - INTERVAL = 1000 + if (config.url.indexOf('aliyundrive') < 0 && config.url.indexOf('alipan') < 0) return config + const now = Math.trunc(performance.timeOrigin + performance.now()) + let { count, ts } = qpsMap.get(config.url) || { count: 1, ts: now } + // 通过位运算实现取整,提高效率 + if ((now / INTERVAL) >> 0 <= (ts / INTERVAL) >> 0) { + // 如果当前时间 ≤ Map中该接口的ts时间,说明前面已经有超过并发后在等待的请求了 + // 只比较秒,忽略毫秒,因为QPS是以秒为周期计算的,即每秒多少个请求数 + if (count < QPS) { + // 如果当前url的请求数没有达到QPS的限制,则计数器+1 + count++ } else { - QPS = 30 - OFFSET = 0 - INTERVAL = 1000 - } - const now = new Date().getTime() - let { count, ts } = qpsMap.get(config.url) || { count: 1, ts: now } - if ((now / INTERVAL) >> 0 <= (ts / INTERVAL) >> 0) { - if (count < QPS) { - count++ - } else { - ts = INTERVAL * Math.ceil(ts / INTERVAL + 1) - count = 1 - } - } else { - ts = now + // 否则,重置计数器,同时将时间戳设置为当前ts的下一整秒 + // 这里需要将ts设置为当前ts的下一秒,而不是当前时间,因为当前ts可能已经远大于当前时间了 + ts = INTERVAL * Math.ceil(ts / INTERVAL + 1) count = 1 } - qpsMap.set(config.url, { count, ts }) - let sleep = ts - now - sleep = sleep > 0 ? sleep + OFFSET : 0 - if (sleep > 0) { - await new Promise((resolve) => setTimeout(() => resolve(), sleep)) - } - return config + } else { + // 否则:当前时间大于ts,说明已经没有排队的请求了(可能有未完成的,但是都已经请求了) + // 则将当前ts重置 + ts = now + count = 1 } + qpsMap.set(config.url, { count, ts }) + // 计算休眠时间: + // 由于本地服务器和远程服务器之间可能存在时间差会发生这种情况: + // 前5个请求在10:00:00.200时发送过去后,此时本地时间可能到了10:00:00.900到来的第六请求由于超出了QPS=5的限制,会休眠100ms + // 但是由于本地和服务端时间差的问题,第六个休眠100ms后发送了请求,服务端的时间可能才是10:00:00.950,导致了QPS超限报错 + // 所以,这里添加一个OFFSET偏移值来纠正本地和服务端之间的时间差问题,默认为50ms,若出现QPS超限,请酌情增大此值 + let sleep = ts - now + sleep = sleep > 0 ? sleep + OFFSET : 0 + // 让当前的请求睡一会儿再请求 + if (sleep > 0) { + await new Promise(resolve => setTimeout(() => resolve(), sleep)) + } + return config +} + axios.interceptors.request.use(qpsController()) axios.defaults.withCredentials = false - export default axios \ No newline at end of file diff --git a/aliyunpan/src/config.ts b/aliyunpan/src/config.ts new file mode 100644 index 0000000000..7608e085d6 --- /dev/null +++ b/aliyunpan/src/config.ts @@ -0,0 +1,10 @@ +export default class Config { + static referer = 'https://www.alipan.com/drive' + static downAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4577.63 Safari/537.36' + static userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) aDrive/4.12.0 Chrome/108.0.5359.215 Electron/22.3.24 Safari/537.36' + 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' + 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' + static tmdbProxyUrl = 'http://api.themoviedb.org' + static client_id = '' + static client_secret = '' +} diff --git a/aliyunpan/src/down/DownDAL.ts b/aliyunpan/src/down/DownDAL.ts index 0686e1c55f..802bcad863 100644 --- a/aliyunpan/src/down/DownDAL.ts +++ b/aliyunpan/src/down/DownDAL.ts @@ -17,6 +17,8 @@ import { humanSize, humanSizeSpeed } from '../utils/format' import { Howl } from 'howler' import DBDown from '../utils/dbdown' import fsPromises from 'fs/promises' +import { DecodeEncName } from '../aliapi/utils' +import { getEncType } from '../utils/proxyhelper' export interface IStateDownFile { DownID: string @@ -59,10 +61,14 @@ export interface IStateDownInfo { sizestr: string icon: string isDir: boolean + encType: string sha1: string crc64: string + + m3u8_total_file_nums?:number + m3u8_parent_file_name?:string } export interface IAriaDownProgress { @@ -77,13 +83,8 @@ export interface IAriaDownProgress { /** 存盘的时机:默认 10 时进行 */ let SaveTimeWait = 0 -/** 下载正在执行中的数据 */ -export let DownInExeMap = new Map() -/** 下载正在队列中的数据 */ -export let DownInQueues: IStateDownFile[] = [] - const sound = new Howl({ - src: ['./audio/down_finished.mp3'], // 音频文件路径 + src: ['./audio/download_finished.mp3'], // 音频文件路径 autoplay: false, // 是否自动播放 volume: 1.0 // 音量,范围 0.0 ~ 1.0 }) @@ -159,7 +160,7 @@ export default class DownDAL { const sep = settingStore.ariaSavePath.indexOf('/') >= 0 ? '/' : '\\' for (let f = 0; f < fileList.length; f++) { const file = fileList[f] - const name = ClearFileName(file.name) + const name = ClearFileName(DecodeEncName(userID, file).name) let fullPath = savePath if (needPanPath) { if (cPath != '' && cPid == file.parent_file_id) fullPath = cPath @@ -168,7 +169,7 @@ export default class DownDAL { const plist = TreeStore.GetDirPath(file.drive_id, file.parent_file_id) for (let p = 0; p < plist.length; p++) { const pName = ClearFileName(plist[p].name) - if (pName == '根目录') continue + if (plist[p].file_id.includes('root')) continue if (path.join(cPath2, pName, name).length > 250) break cPath2 = path.join(cPath2, pName) } @@ -188,7 +189,6 @@ export default class DownDAL { let downloadurl = '' let crc64 = '' - const downitem: IStateDownFile = { DownID: userID + '|' + file.file_id, Info: { @@ -203,6 +203,7 @@ export default class DownDAL { sizestr: file.sizeStr, isDir: file.isDir, icon: file.icon, + encType: getEncType(file), sha1: '', crc64: crc64 }, @@ -240,9 +241,8 @@ export default class DownDAL { const isOnline = await AriaConnect() if (isOnline && downingStore.ListDataRaw.length) { await AriaGetDowningList() - const ariaRemote = IsAria2cRemote() - const DowningList: IStateDownFile[] = useDowningStore().ListDataRaw + const DowningList: IStateDownFile[] = downingStore.ListDataRaw const timeThreshold = Date.now() - 60 * 1000 const downFileMax = settingStore.downFileMax const shouldSkipDown = (Down: any) => { @@ -253,7 +253,7 @@ export default class DownDAL { (Down.IsFailed && timeThreshold <= Down.AutoTry) ) } - let downingCount = DowningList.filter((down: any) => down.Down.IsDowning).length + let addDowningCount = 0 for (let i = 0; i < DowningList.length; i++) { const DownItem = DowningList[i] const { DownID, Info, Down } = DownItem @@ -263,20 +263,20 @@ export default class DownDAL { const completedDownId = `${Date.now()}_${Down.DownTime}` // 删除已完成的下载并更新数据库 DowningList.splice(i, 1) - DBDown.deleteDowning(DownID) + await DBDown.deleteDowning(DownID) // 将已完成的下载添加到下载文件列表中 const downedData = JSON.parse(JSON.stringify({ DownID: completedDownId, Down, Info })) downedStore.ListDataRaw.unshift({ DownID: completedDownId, Down, Info }) downedStore.mRefreshListDataShow(true) - DBDown.saveDowned(completedDownId, downedData) + await DBDown.saveDowned(completedDownId, downedData) if (downedStore.ListSelected.has(completedDownId)) { downedStore.ListSelected.delete(completedDownId) } // 移除Aria2已完成的任务 await AriaDeleteList([Info.GID]) i-- - } else if (downingCount < downFileMax && !shouldSkipDown(Down)) { - downingCount++ + } else if ((addDowningCount + downingStore.ListDataDowningCount) < downFileMax && !shouldSkipDown(Down)) { + addDowningCount++ downingStore.mUpdateDownState(DownItem, 'start') let state = await AriaAddUrl(DownItem) downingStore.mUpdateDownState(DownItem, state) @@ -379,7 +379,7 @@ export default class DownDAL { // 处理待删除文件 if (!isAll) { const downIDList = deleteList.map(item => item.DownID) - console.log('deleteDowning', deleteList) + // console.log('deleteDowning', deleteList) await DBDown.deleteDownings(JSON.parse(JSON.stringify(downIDList))) } else { await DBDown.deleteDowningAll() @@ -423,12 +423,6 @@ export default class DownDAL { } static QueryIsDowning() { - const downingList = useDowningStore().ListDataRaw - for (let i = 0, maxi = downingList.length; i < maxi; i++) { - if (!downingList[i].Down.IsDowning) { - return true - } - } - return false + return useDowningStore().ListDataDowningCount > 0 } -} \ No newline at end of file +} diff --git a/aliyunpan/src/down/DownDowned.vue b/aliyunpan/src/down/DownDowned.vue index 6486481038..233e0b7df7 100644 --- a/aliyunpan/src/down/DownDowned.vue +++ b/aliyunpan/src/down/DownDowned.vue @@ -1,6 +1,14 @@ - diff --git a/aliyunpan/src/down/DownUploaded.vue b/aliyunpan/src/down/DownUploaded.vue index fa0846297e..988a5bc4c4 100644 --- a/aliyunpan/src/down/DownUploaded.vue +++ b/aliyunpan/src/down/DownUploaded.vue @@ -1,8 +1,17 @@ diff --git a/aliyunpan/src/layout/PageAudio.vue b/aliyunpan/src/layout/PageAudio.vue new file mode 100644 index 0000000000..9bd0bac693 --- /dev/null +++ b/aliyunpan/src/layout/PageAudio.vue @@ -0,0 +1,679 @@ + + + + + + diff --git a/aliyunpan/src/layout/PageCode.vue b/aliyunpan/src/layout/PageCode.vue index ee4347d29b..a0e6388ad6 100644 --- a/aliyunpan/src/layout/PageCode.vue +++ b/aliyunpan/src/layout/PageCode.vue @@ -1,66 +1,88 @@ -