├── p115client ├── py.typed ├── __init__.py ├── fs │ └── __init__.py ├── tool │ ├── __init__.py │ └── auth.py └── exception.py ├── modules ├── @p115 │ ├── p115 │ │ ├── py.typed │ │ ├── @pending │ │ │ ├── act.py │ │ │ ├── app.py │ │ │ ├── captcha.py │ │ │ ├── chat.py │ │ │ ├── desc.py │ │ │ ├── extract.py │ │ │ ├── forum.py │ │ │ ├── group.py │ │ │ ├── life.py │ │ │ ├── login.py │ │ │ ├── media.py │ │ │ ├── message.py │ │ │ ├── note.py │ │ │ ├── photo.py │ │ │ ├── qrcode.py │ │ │ ├── upload.py │ │ │ ├── user.py │ │ │ ├── download.py │ │ │ └── usershare.py │ │ └── __init__.py │ ├── LICENSE │ └── pyproject.toml ├── p115dav │ ├── p115dav │ │ ├── py.typed │ │ ├── __init__.py │ │ ├── static │ │ │ └── images │ │ │ │ ├── subtitle.svg │ │ │ │ ├── close.svg │ │ │ │ ├── fancybox.svg │ │ │ │ ├── plyr.svg │ │ │ │ ├── fileball.svg │ │ │ │ ├── favicon.svg │ │ │ │ ├── vlc.svg │ │ │ │ ├── iina.svg │ │ │ │ ├── nplayer.svg │ │ │ │ ├── mpv.svg │ │ │ │ ├── infuse.svg │ │ │ │ ├── mxplayer.svg │ │ │ │ ├── mxplayer-pro.svg │ │ │ │ ├── stellarplayer.svg │ │ │ │ └── potplayer.svg │ │ └── views │ │ │ └── search.html │ └── pyproject.toml ├── p115ftp │ ├── p115ftp │ │ ├── py.typed │ │ └── __main__.py │ ├── pyproject.toml │ └── readme.md ├── p115oss │ ├── p115oss │ │ └── py.typed │ ├── readme.md │ └── pyproject.toml ├── p115cipher │ ├── p115cipher │ │ └── py.typed │ ├── readme.md │ └── pyproject.toml ├── p115fuse │ ├── p115fuse │ │ ├── py.typed │ │ ├── __main__.py │ │ └── __init__.py │ ├── pyproject.toml │ └── readme.md ├── p115qrcode │ ├── p115qrcode │ │ ├── py.typed │ │ ├── cmd │ │ │ ├── __init__.py │ │ │ ├── init.py │ │ │ └── cmd.py │ │ └── __main__.py │ ├── pyproject.toml │ ├── readme.md │ └── @qrcode.py ├── p115sftp │ ├── p115sftp │ │ ├── py.typed │ │ └── __main__.py │ ├── pyproject.toml │ └── readme.md ├── p115nano302 │ ├── p115nano302 │ │ ├── py.typed │ │ └── __init__.py │ └── pyproject.toml ├── p115open302 │ ├── p115open302 │ │ ├── py.typed │ │ └── __init__.py │ └── pyproject.toml ├── p115pickcode │ ├── p115pickcode │ │ └── py.typed │ ├── readme.md │ └── pyproject.toml ├── p115servedb │ ├── p115servedb │ │ ├── py.typed │ │ ├── __init__.py │ │ ├── static │ │ │ └── images │ │ │ │ ├── subtitle.svg │ │ │ │ ├── close.svg │ │ │ │ ├── fancybox.svg │ │ │ │ ├── plyr.svg │ │ │ │ ├── fileball.svg │ │ │ │ ├── favicon.svg │ │ │ │ ├── vlc.svg │ │ │ │ ├── iina.svg │ │ │ │ ├── nplayer.svg │ │ │ │ ├── mpv.svg │ │ │ │ ├── infuse.svg │ │ │ │ ├── mxplayer.svg │ │ │ │ ├── mxplayer-pro.svg │ │ │ │ ├── stellarplayer.svg │ │ │ │ └── potplayer.svg │ │ ├── init.py │ │ ├── __main__.py │ │ ├── component │ │ │ ├── log.py │ │ │ ├── __fuse_monkey_patch.py │ │ │ └── db.py │ │ └── views │ │ │ └── list.jinja │ ├── readme.md │ └── pyproject.toml ├── p115tiny302 │ ├── p115tiny302 │ │ ├── py.typed │ │ └── __init__.py │ └── pyproject.toml ├── p115tinydav │ ├── p115tinydav │ │ ├── py.typed │ │ ├── __init__.py │ │ └── __main__.py │ ├── pyproject.toml │ └── readme.md ├── p115updatedb │ ├── p115updatedb │ │ ├── py.typed │ │ ├── __init__.py │ │ ├── init-without-event.sql │ │ ├── __main__.py │ │ └── init.sql │ ├── pyproject.toml │ └── readme.md ├── p115wsgidav │ ├── p115wsgidav │ │ ├── py.typed │ │ └── __main__.py │ ├── pyproject.toml │ └── readme.md ├── p115rsacipher │ ├── p115rsacipher │ │ ├── py.typed │ │ └── __init__.py │ ├── readme.md │ └── pyproject.toml ├── @p115checkin │ └── check.py └── @p115captcha │ └── tool.py ├── docs ├── example │ ├── 06.拉取操作事件.md │ ├── 07.拉取历史记录.md │ ├── 08.对上传的封装.md │ ├── 09.对下载的封装.md │ ├── 14.开发ftp服务.md │ ├── 10.接口的替代和分流.md │ ├── 11.开发网页版文件列表.md │ ├── 12.开发webdav服务.md │ ├── 13.开发fuse虚拟挂载服务.md │ └── index.md ├── requirements.txt ├── reference │ ├── module │ │ ├── const.md │ │ ├── index.md │ │ ├── util.md │ │ ├── fs.md │ │ ├── type.md │ │ ├── client.md │ │ └── exception.md │ └── tool │ │ ├── index.md │ │ ├── auth.md │ │ ├── edit.md │ │ ├── attr.md │ │ ├── life.md │ │ ├── pool.md │ │ ├── upload.md │ │ ├── xys.md │ │ ├── download.md │ │ ├── iterdir.md │ │ ├── offline.md │ │ ├── fs_files.md │ │ ├── history.md │ │ ├── updatedb.md │ │ ├── __init__.md │ │ └── export_dir.md ├── index.md ├── Makefile ├── make.bat └── conf.py ├── .gitignore ├── .readthedocs.yaml ├── LICENSE └── pyproject.toml /p115client/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115ftp/p115ftp/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115oss/p115oss/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/act.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/app.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/captcha.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/chat.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/desc.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/extract.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/forum.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/group.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/life.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/login.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/media.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/message.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/note.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/photo.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/qrcode.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/upload.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/user.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115cipher/p115cipher/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115fuse/p115fuse/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115qrcode/p115qrcode/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115sftp/p115sftp/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/download.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/@p115/p115/@pending/usershare.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115nano302/p115nano302/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115open302/p115open302/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115pickcode/p115pickcode/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115tiny302/p115tiny302/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115tinydav/p115tinydav/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115updatedb/p115updatedb/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115wsgidav/p115wsgidav/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115rsacipher/p115rsacipher/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/example/06.拉取操作事件.md: -------------------------------------------------------------------------------- 1 | # 06.拉取操作事件 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/07.拉取历史记录.md: -------------------------------------------------------------------------------- 1 | # 07.拉取历史记录 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/08.对上传的封装.md: -------------------------------------------------------------------------------- 1 | # 08.对上传的封装 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/09.对下载的封装.md: -------------------------------------------------------------------------------- 1 | # 09.对下载的封装 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/14.开发ftp服务.md: -------------------------------------------------------------------------------- 1 | # 14.开发ftp服务 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/10.接口的替代和分流.md: -------------------------------------------------------------------------------- 1 | # 10.接口的替代和分流 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/11.开发网页版文件列表.md: -------------------------------------------------------------------------------- 1 | # 11.开发网页版文件列表 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/12.开发webdav服务.md: -------------------------------------------------------------------------------- 1 | # 12.开发webdav服务 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/example/13.开发fuse虚拟挂载服务.md: -------------------------------------------------------------------------------- 1 | # 13.开发fuse虚拟挂载服务 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | furo 2 | myst_parser 3 | sphinx_copybutton 4 | -------------------------------------------------------------------------------- /docs/example/index.md: -------------------------------------------------------------------------------- 1 | # 实用案例 2 | 3 | 展示了一些实际的例子,借以抛砖引玉 4 | 5 | ```{toctree} 6 | :maxdepth: 1 7 | :glob: 8 | 9 | * 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/reference/module/const.md: -------------------------------------------------------------------------------- 1 | # `const` 2 | 3 | 此模块包含了一些重要的常量 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.const 7 | :members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/reference/tool/index.md: -------------------------------------------------------------------------------- 1 | # 工具函数 2 | 3 | 罗列了所有的工具函数的文档,并且可以查看源代码 4 | 5 | ```{toctree} 6 | :maxdepth: 1 7 | :glob: 8 | 9 | * 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/reference/module/index.md: -------------------------------------------------------------------------------- 1 | # 主模块 2 | 3 | 罗列了本模块的目录结构和各种接口的文档,并可查看源代码 4 | 5 | ```{toctree} 6 | :maxdepth: 1 7 | :glob: 8 | 9 | * 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/reference/module/util.md: -------------------------------------------------------------------------------- 1 | # `util` 2 | 3 | 一些工具函数 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.util 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/auth.md: -------------------------------------------------------------------------------- 1 | # `auth` 2 | 3 | 账号状况 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.auth 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/edit.md: -------------------------------------------------------------------------------- 1 | # `edit` 2 | 3 | 编辑信息 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.edit 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/module/fs.md: -------------------------------------------------------------------------------- 1 | # `fs` 2 | 3 | 此模块包含了客户端操作的文件系统封装 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.fs 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/module/type.md: -------------------------------------------------------------------------------- 1 | # `type` 2 | 3 | 此模块包含了一些数据类型 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.type 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/attr.md: -------------------------------------------------------------------------------- 1 | # `attr` 2 | 3 | 属性 4 | --- 5 | 6 | ```{eval-rst} 7 | .. automodule:: p115client.tool.attr 8 | :show-inheritance: 9 | :members: 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/reference/tool/life.md: -------------------------------------------------------------------------------- 1 | # `life` 2 | 3 | 115 生活事件 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.life 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/pool.md: -------------------------------------------------------------------------------- 1 | # `pool` 2 | 3 | cookies 池 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.pool 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/upload.md: -------------------------------------------------------------------------------- 1 | # `upload` 2 | 3 | 上传 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.upload 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/xys.md: -------------------------------------------------------------------------------- 1 | # `xys` 2 | 3 | 许愿树 4 | --- 5 | 6 | ```{eval-rst} 7 | .. automodule:: p115client.tool.xys 8 | :show-inheritance: 9 | :members: 10 | ``` 11 | -------------------------------------------------------------------------------- /docs/reference/tool/download.md: -------------------------------------------------------------------------------- 1 | # `download` 2 | 3 | 下载 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.download 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/iterdir.md: -------------------------------------------------------------------------------- 1 | # `iterdir` 2 | 3 | 迭代目录 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.iterdir 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/offline.md: -------------------------------------------------------------------------------- 1 | # `offline` 2 | 3 | 离线下载 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.offline 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/module/client.md: -------------------------------------------------------------------------------- 1 | # `client` 2 | 3 | 此模块包含了客户端操作的基本封装 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.client 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/fs_files.md: -------------------------------------------------------------------------------- 1 | # `fs_files` 2 | 3 | 罗列文件列表 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.fs_files 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/history.md: -------------------------------------------------------------------------------- 1 | # `history` 2 | 3 | 115 历史记录 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.history 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/updatedb.md: -------------------------------------------------------------------------------- 1 | # `updatedb` 2 | 3 | 更新数据库 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.updatedb 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/module/exception.md: -------------------------------------------------------------------------------- 1 | # `exception` 2 | 3 | 此模块包含了所有的异常类 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.exception 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/__init__.md: -------------------------------------------------------------------------------- 1 | # `__init__` 2 | 3 | 此模块包含了一些工具函数 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.__init__ 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/reference/tool/export_dir.md: -------------------------------------------------------------------------------- 1 | # `export_dir` 2 | 3 | 导出目录树 4 | 5 | ```{eval-rst} 6 | .. automodule:: p115client.tool.export_dir 7 | :show-inheritance: 8 | :members: 9 | ``` 10 | -------------------------------------------------------------------------------- /modules/p115qrcode/p115qrcode/cmd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = ["parser"] 6 | 7 | from .init import parser 8 | from . import cmd, web 9 | -------------------------------------------------------------------------------- /modules/p115oss/readme.md: -------------------------------------------------------------------------------- 1 | # 115 oss 上传 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115oss/) 安装 6 | 7 | ```console 8 | pip install -U p115oss 9 | ``` 10 | 11 | ## 用法 12 | 13 | ```python 14 | import p115oss 15 | ``` 16 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 6) 6 | __license__ = "GPLv3 " 7 | -------------------------------------------------------------------------------- /modules/p115pickcode/readme.md: -------------------------------------------------------------------------------- 1 | # 115 的 pickcode 和 id 的相互转换 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115pickcode/) 安装 6 | 7 | ```console 8 | pip install -U p115pickcode 9 | ``` 10 | 11 | ## 用法 12 | 13 | ```python 14 | import p115pickcode 15 | ``` 16 | -------------------------------------------------------------------------------- /modules/p115updatedb/p115updatedb/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 12) 6 | __license__ = "GPLv3 " 7 | 8 | from .updatedb import * 9 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 10) 6 | __license__ = "GPLv3 " 7 | 8 | from .app import * 9 | from .db import * 10 | -------------------------------------------------------------------------------- /p115client/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 8) 6 | 7 | from .client import * 8 | from .const import * 9 | from .exception import * 10 | from .fs import * 11 | from .type import * 12 | from . import util 13 | -------------------------------------------------------------------------------- /modules/p115cipher/readme.md: -------------------------------------------------------------------------------- 1 | # 115 cipher module 2 | 3 | ## Installation 4 | 5 | You can install via [pypi](https://pypi.org/project/p115cipher/) 6 | 7 | ```console 8 | pip install -U p115cipher 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```python 14 | from p115cipher.fast import * 15 | from p115cipher.normal import * 16 | ``` 17 | -------------------------------------------------------------------------------- /modules/p115rsacipher/readme.md: -------------------------------------------------------------------------------- 1 | # 115 webdisk RSA encryption and decryption. 2 | 3 | ## Installation 4 | 5 | You can install via [pypi](https://pypi.org/project/p115rsacipher/) 6 | 7 | ```console 8 | pip install -U p115rsacipher 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```python 14 | from p115rsacipher import encrypt, decrypt 15 | ``` 16 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/subtitle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/subtitle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /p115client/fs/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | from .fs_base import * 5 | from .fs import * 6 | from .fs_share import * 7 | from .fs_zip import * 8 | 9 | def __getattr__(attr, /): 10 | if attr == "__all__": 11 | from . import fs_base, fs, fs_share, fs_zip 12 | return fs_base.__all__ + fs.__all__ + fs_share.__all__ + fs_zip.__all__ 13 | raise AttributeError(attr) 14 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __all__ = ["parser", "subparsers"] 5 | 6 | from argparse import ArgumentParser, RawTextHelpFormatter 7 | 8 | parser = ArgumentParser( 9 | description="servedb 命令行工具集", 10 | formatter_class=RawTextHelpFormatter, 11 | ) 12 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 13 | parser.set_defaults(func=None) 14 | subparsers = parser.add_subparsers() 15 | 16 | from . import dav, fuse 17 | 18 | -------------------------------------------------------------------------------- /modules/p115qrcode/p115qrcode/cmd/init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __all__ = ["parser", "subparsers"] 5 | 6 | from argparse import ArgumentParser, RawTextHelpFormatter 7 | 8 | parser = ArgumentParser( 9 | description="115 网盘扫码登录", 10 | formatter_class=RawTextHelpFormatter, 11 | ) 12 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 13 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 14 | parser.set_defaults(func=None) 15 | subparsers = parser.add_subparsers() 16 | 17 | -------------------------------------------------------------------------------- /p115client/tool/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | 6 | from p115client.util import * 7 | 8 | from .attr import * 9 | from .auth import * 10 | from .download import * 11 | from .edit import * 12 | from .export_dir import * 13 | from .fs_files import * 14 | from .history import * 15 | from .iterdir import * 16 | from .life import * 17 | from .offline import * 18 | from .pool import * 19 | from .updatedb import * 20 | from .upload import * 21 | from .xys import * 22 | -------------------------------------------------------------------------------- /modules/p115servedb/readme.md: -------------------------------------------------------------------------------- 1 | # 115 网盘数据库的挂载服务 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115servedb/) 安装 6 | 7 | ```console 8 | pip install -U p115servedb 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 命令行使用 14 | 15 | #### 开启 webdav 16 | 17 | 挂载地址为 `/ -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide-toc: true 3 | --- 4 | 5 | ```{include} ../readme.md 6 | :relative-docs: docs/ 7 | :relative-images: 8 | ``` 9 | 10 | ```{toctree} 11 | :hidden: 12 | 13 | reference/module/index 14 | reference/tool/index 15 | ``` 16 | 17 | ```{toctree} 18 | :caption: 其它资源 19 | :hidden: 20 | 21 | example/index 22 | ``` 23 | 24 | ```{toctree} 25 | :caption: Development 26 | :hidden: 27 | 28 | License: MIT 29 | Bug Reports 30 | Source Code 31 | ``` 32 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/fancybox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | def main(): 5 | from p115servedb.init import parser 6 | 7 | args = parser.parse_args() 8 | if args.version: 9 | from p115servedb import __version__ 10 | print(".".join(map(str, __version__))) 11 | raise SystemExit(0) 12 | if args.func: 13 | args.func(args) 14 | else: 15 | parser.parse_args(["-h"]) 16 | 17 | if __name__ == "__main__": 18 | from pathlib import Path 19 | from sys import path 20 | 21 | path[0] = str(Path(__file__).parents[1]) 22 | main() 23 | 24 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/plyr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115nano302/p115nano302/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 1, 2) 6 | __license__ = "GPLv3 " 7 | 8 | __FALSE = False 9 | if __FALSE: 10 | from .app import * 11 | 12 | def __getattr__(attr, /): 13 | from importlib import import_module 14 | 15 | app = import_module('.app', package=__package__) 16 | all = {"__all__": app.__all__} 17 | for name in app.__all__: 18 | all[name] = getattr(app, name) 19 | globals().update(all) 20 | del globals()["__getattr__"] 21 | return getattr(app, attr) 22 | -------------------------------------------------------------------------------- /modules/p115open302/p115open302/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 5) 6 | __license__ = "GPLv3 " 7 | 8 | __FALSE = False 9 | if __FALSE: 10 | from .app import * 11 | 12 | def __getattr__(attr, /): 13 | from importlib import import_module 14 | 15 | app = import_module('.app', package=__package__) 16 | all = {"__all__": app.__all__} 17 | for name in app.__all__: 18 | all[name] = getattr(app, name) 19 | globals().update(all) 20 | del globals()["__getattr__"] 21 | return getattr(app, attr) 22 | -------------------------------------------------------------------------------- /modules/p115tiny302/p115tiny302/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 2, 2) 6 | __license__ = "GPLv3 " 7 | 8 | __FALSE = False 9 | if __FALSE: 10 | from .app import * 11 | 12 | def __getattr__(attr, /): 13 | from importlib import import_module 14 | 15 | app = import_module('.app', package=__package__) 16 | all = {"__all__": app.__all__} 17 | for name in app.__all__: 18 | all[name] = getattr(app, name) 19 | globals().update(all) 20 | del globals()["__getattr__"] 21 | return getattr(app, attr) 22 | -------------------------------------------------------------------------------- /modules/p115tinydav/p115tinydav/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 1) 6 | __license__ = "GPLv3 " 7 | 8 | __FALSE = False 9 | if __FALSE: 10 | from .app import * 11 | 12 | def __getattr__(attr, /): 13 | from importlib import import_module 14 | 15 | app = import_module('.app', package=__package__) 16 | all = {"__all__": app.__all__} 17 | for name in app.__all__: 18 | all[name] = getattr(app, name) 19 | globals().update(all) 20 | del globals()["__getattr__"] 21 | return getattr(app, attr) 22 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/plyr.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python: 2 | *.py[codz] 3 | *.so 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | _build 9 | 10 | # Compiled C++ files 11 | *.out 12 | 13 | # Temp File 14 | *.sw[aop] 15 | 16 | # github merge file 17 | *.orig 18 | 19 | #vscode 20 | .vscode 21 | 22 | # Folder view configuration files 23 | .DS_Store 24 | 25 | # Thumbnail cache files 26 | ._* 27 | Thumbs.db 28 | 29 | # Files that might appear on external disks 30 | .Spotlight-V100 31 | .Trashes 32 | 33 | # cache path 34 | .idea/ 35 | __pycache__/ 36 | 37 | # Log file 38 | *.log 39 | 40 | # Zipped file 41 | *.zip 42 | *.7z 43 | *.rar 44 | 45 | # config file 46 | config.json 47 | 48 | # nodejs modules 49 | node_modules/ 50 | 51 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the version of Python and other tools you might need 8 | build: 9 | os: ubuntu-24.04 10 | tools: 11 | python: "3.12" 12 | 13 | # Build documentation in the docs/ directory with Sphinx 14 | sphinx: 15 | configuration: docs/conf.py 16 | 17 | # We recommend specifying your dependencies to enable reproducible builds: 18 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 19 | python: 20 | install: 21 | - method: pip 22 | path: . 23 | - requirements: docs/requirements.txt 24 | 25 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /modules/p115qrcode/p115qrcode/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | 6 | def main(): 7 | from p115qrcode.cmd import parser 8 | 9 | args = parser.parse_args() 10 | if args.version: 11 | from p115qrcode import __version__ 12 | print(".".join(map(str, __version__))) 13 | raise SystemExit(0) 14 | elif args.license: 15 | from p115qrcode import __license__ 16 | print(__license__) 17 | raise SystemExit(0) 18 | if args.func: 19 | args.func(args) 20 | else: 21 | parser.parse_args(["-h"]) 22 | 23 | if __name__ == "__main__": 24 | from pathlib import Path 25 | from sys import path 26 | 27 | path[0] = str(Path(__file__).parents[1]) 28 | main() 29 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /modules/@p115/p115/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 9, 9) 6 | 7 | from warnings import filterwarnings 8 | 9 | filterwarnings("ignore", category=DeprecationWarning) 10 | filterwarnings("ignore", category=SyntaxWarning) 11 | 12 | from p115client import * 13 | 14 | from .client import * 15 | from .fs_base import * 16 | from .fs import * 17 | from .fs_share import * 18 | from .fs_zip import * 19 | from .labellist import * 20 | from .offline import * 21 | from .recyclebin import * 22 | from .sharing import * 23 | 24 | # TODO 所有方法都支持听噢那个不和异步 25 | # TODO upload_tree 多线程和进度条,并且为每一个上传返回一个 task,可重试 26 | # TODO 能及时处理文件已不存在 27 | # TODO 为各个fs接口添加额外的请求参数 28 | # TODO 115中多个文件可以在同一目录下同名,如何处理 29 | # TODO 提供一个新的上传函数,上传如果失败,因为名字问题,则尝试用uuid名字,上传成功后,再进行改名,如果成功,删除原来的文件,不成功,则删掉上传的文件(如果上传成功了的话) 30 | # TODO 如果压缩包尚未解压,则使用 zipfile 之类的模块,去模拟文件系统 31 | # TODO: 为上传进度进行封装,创建 UploadTask 类 32 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/fileball.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/fileball.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ChenyangGao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /modules/@p115/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 ChenyangGao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /modules/p115cipher/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115cipher" 3 | version = "0.0.4" 4 | description = "115 cipher module." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115cipher" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115cipher" 10 | keywords = ["115", "webdisk", "cipher"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | lz4 = "*" 31 | pycryptodome = "*" 32 | 33 | [build-system] 34 | requires = ["poetry-core"] 35 | build-backend = "poetry.core.masonry.api" 36 | 37 | [[tool.poetry.packages]] 38 | include = "p115cipher" 39 | -------------------------------------------------------------------------------- /modules/p115pickcode/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115pickcode" 3 | version = "0.0.5.4" 4 | description = "115 transcoding between pickcode and id." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115pickcode" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115pickcode" 10 | keywords = ["python-115", "pickcode", "id"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.10", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.10" 30 | 31 | [build-system] 32 | requires = ["poetry-core"] 33 | build-backend = "poetry.core.masonry.api" 34 | 35 | [[tool.poetry.packages]] 36 | include = "p115pickcode" 37 | -------------------------------------------------------------------------------- /modules/p115rsacipher/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115rsacipher" 3 | version = "0.0.1" 4 | description = "115 webdisk RSA encryption and decryption." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115rsacipher" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115rsacipher" 10 | keywords = ["python-115", "RSA", "cipher"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | 31 | [build-system] 32 | requires = ["poetry-core"] 33 | build-backend = "poetry.core.masonry.api" 34 | 35 | [[tool.poetry.packages]] 36 | include = "p115rsacipher" 37 | -------------------------------------------------------------------------------- /modules/p115ftp/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115ftp" 3 | version = "0.0.5.1" 4 | description = "Python 115 FTP Server." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115ftp" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115ftp" 10 | keywords = ["python", "115", "FTP"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | cachedict = ">=0.0.7" 31 | errno2 = ">=0.0.3" 32 | p115client = ">=0.0.8.2" 33 | pyftpdlib = "*" 34 | richlog_fs = ">=0.0.3" 35 | 36 | [tool.poetry.scripts] 37 | p115ftp = "p115ftp.__main__:main" 38 | 39 | [build-system] 40 | requires = ["poetry-core"] 41 | build-backend = "poetry.core.masonry.api" 42 | 43 | [[tool.poetry.packages]] 44 | include = "p115ftp" 45 | -------------------------------------------------------------------------------- /modules/p115fuse/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115fuse" 3 | version = "0.0.3.2" 4 | description = "Python 115 FUSE mount" 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115fuse" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115fuse" 10 | keywords = ["python", "115", "FUSE"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | cachedict = ">=0.0.7" 31 | mfusepy = "*" 32 | orjson = "*" 33 | p115client = ">=0.0.8.2" 34 | richlog_fs = ">=0.0.3" 35 | 36 | [tool.poetry.scripts] 37 | p115fuse = "p115fuse.__main__:main" 38 | 39 | [build-system] 40 | requires = ["poetry-core"] 41 | build-backend = "poetry.core.masonry.api" 42 | 43 | [[tool.poetry.packages]] 44 | include = "p115fuse" 45 | -------------------------------------------------------------------------------- /modules/p115wsgidav/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115wsgidav" 3 | version = "0.0.3.4" 4 | description = "Python 115 WsgiDAV" 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115wsgidav" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115wsgidav" 10 | keywords = ["python", "115", "WsgiDAV"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | cachedict = ">=0.0.7" 31 | p115client = ">=0.0.7.7" 32 | pyyaml = "*" 33 | wsgidav = "*" 34 | yarl = "*" 35 | 36 | [tool.poetry.scripts] 37 | p115wsgidav = "p115wsgidav.__main__:main" 38 | 39 | [build-system] 40 | requires = ["poetry-core"] 41 | build-backend = "poetry.core.masonry.api" 42 | 43 | [[tool.poetry.packages]] 44 | include = "p115wsgidav" 45 | -------------------------------------------------------------------------------- /modules/p115sftp/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115sftp" 3 | version = "0.0.1" 4 | description = "Python 115 SFTP Server." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115sftp" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115sftp" 10 | keywords = ["python", "115", "FTP"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | cachedict = ">=0.0.7" 31 | errno2 = ">=0.0.3" 32 | p115client = ">=0.0.8.2" 33 | python-decotools = ">=0.0.4" 34 | paramiko = "*" 35 | richlog_fs = ">=0.0.3" 36 | 37 | [tool.poetry.scripts] 38 | p115sftp = "p115sftp.__main__:main" 39 | 40 | [build-system] 41 | requires = ["poetry-core"] 42 | build-backend = "poetry.core.masonry.api" 43 | 44 | [[tool.poetry.packages]] 45 | include = "p115sftp" 46 | -------------------------------------------------------------------------------- /modules/p115qrcode/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115qrcode" 3 | version = "0.0.5.1" 4 | description = "115 网盘二维码扫码登录" 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115qrcode" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115qrcode" 10 | keywords = ["python-115", "115", "qrcode"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | flask = "*" 31 | orjson = "*" 32 | httpx = "*" 33 | httpx_request = ">=0.1" 34 | python-iterutils = ">=0.2.1" 35 | python-startfile = ">=0.0.2" 36 | qrcode = "*" 37 | 38 | [tool.poetry.scripts] 39 | p115qrcode = "p115qrcode.__main__:main" 40 | 41 | [build-system] 42 | requires = ["poetry-core"] 43 | build-backend = "poetry.core.masonry.api" 44 | 45 | [[tool.poetry.packages]] 46 | include = "p115qrcode" 47 | -------------------------------------------------------------------------------- /modules/p115updatedb/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115updatedb" 3 | version = "0.0.12.3" 4 | description = "把 115 网盘的文件列表导出到数据库" 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115updatedb" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115updatedb" 10 | keywords = ["python-115", "updatedb"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | orjson = "*" 31 | p115client = ">=0.0.7.2" 32 | posixpatht = ">=0.0.4" 33 | python-concurrenttools = ">=0.1.0" 34 | python-iterutils = ">=0.2.5.4" 35 | sqlitetools = ">=0.0.3.2" 36 | 37 | [tool.poetry.scripts] 38 | p115updatedb = "p115updatedb.__main__:main" 39 | 40 | [build-system] 41 | requires = ["poetry-core"] 42 | build-backend = "poetry.core.masonry.api" 43 | 44 | [[tool.poetry.packages]] 45 | include = "p115updatedb" 46 | -------------------------------------------------------------------------------- /modules/p115open302/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115open302" 3 | version = "0.0.5.4" 4 | description = "115 open 302 backend." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115open302" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115open302" 10 | keywords = ["python-115", "302", "backend"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | blacksheep = "*" 31 | blacksheep-rich-log = ">=0.0.1" 32 | cachedict = ">=0.0.6" 33 | orjson = "*" 34 | p115client = ">=0.0.7.1" 35 | p115pickcode = ">=0.0.5.4" 36 | pyyaml = "*" 37 | python-dicttools = ">=0.0.4" 38 | uvicorn = "*" 39 | 40 | [tool.poetry.scripts] 41 | p115open302 = "p115open302.__main__:main" 42 | 43 | [build-system] 44 | requires = ["poetry-core"] 45 | build-backend = "poetry.core.masonry.api" 46 | 47 | [[tool.poetry.packages]] 48 | include = "p115open302" 49 | -------------------------------------------------------------------------------- /modules/p115tinydav/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115tinydav" 3 | version = "0.0.1" 4 | description = "115 tiny WebDAV." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115tinydav" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115tinydav" 10 | keywords = ["python-115", "webdav"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | blacksheep = "*" 31 | blacksheep-rich-log = ">=0.0.1" 32 | cachedict = ">=0.0.6" 33 | datefmt = ">=0.0.1" 34 | errno2 = ">=0.0.5" 35 | p115client = ">=0.0.7.7.1" 36 | python-sqlitedict = ">=0.0.3" 37 | pyyaml = "*" 38 | uvicorn = "*" 39 | yarl = "*" 40 | 41 | [tool.poetry.scripts] 42 | p115tinydav = "p115tinydav.__main__:main" 43 | 44 | [build-system] 45 | requires = ["poetry-core"] 46 | build-backend = "poetry.core.masonry.api" 47 | 48 | [[tool.poetry.packages]] 49 | include = "p115tinydav" 50 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115tiny302/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115tiny302" 3 | version = "0.2.2.1" 4 | description = "115 tiny 302 backend." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115tiny302" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115tiny302" 10 | keywords = ["python-115", "302", "backend"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | blacksheep = "*" 31 | blacksheep-rich-log = ">=0.0.1" 32 | cachedict = ">=0.0.6" 33 | orjson = "*" 34 | p115client = ">=0.0.7.1" 35 | p115pickcode = ">=0.0.5.4" 36 | posixpatht = ">=0.0.4" 37 | pyyaml = "*" 38 | python-dicttools = ">=0.0.4" 39 | uvicorn = "*" 40 | 41 | [tool.poetry.scripts] 42 | p115tiny302 = "p115tiny302.__main__:main" 43 | 44 | [build-system] 45 | requires = ["poetry-core"] 46 | build-backend = "poetry.core.masonry.api" 47 | 48 | [[tool.poetry.packages]] 49 | include = "p115tiny302" 50 | -------------------------------------------------------------------------------- /modules/p115nano302/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115nano302" 3 | version = "0.1.2.5" 4 | description = "115 nano 302 backend." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115nano302" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115nano302" 10 | keywords = ["python-115", "302", "backend"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | blacksheep = "*" 31 | blacksheep_client_request = ">=0.1.2" 32 | blacksheep-rich-log = ">=0.0.2" 33 | cachedict = ">=0.0.6" 34 | orjson = "*" 35 | p115rsacipher = ">=0.0.1" 36 | p115pickcode = ">=0.0.5.4" 37 | python-dicttools = ">=0.0.5" 38 | pyyaml = "*" 39 | uvicorn = "*" 40 | 41 | [tool.poetry.scripts] 42 | p115nano302 = "p115nano302.__main__:main" 43 | 44 | [build-system] 45 | requires = ["poetry-core"] 46 | build-backend = "poetry.core.masonry.api" 47 | 48 | [[tool.poetry.packages]] 49 | include = "p115nano302" 50 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/component/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = ["logger", "ColoredLevelNameFormatter"] 6 | 7 | import logging 8 | 9 | 10 | logger = logging.getLogger("alist_fuse") 11 | 12 | 13 | class ColoredLevelNameFormatter(logging.Formatter): 14 | 15 | def format(self, record): 16 | match record.levelno: 17 | case logging.DEBUG: 18 | # blue 19 | record.levelname = f"\x1b[1;34m{record.levelname}\x1b[0m" 20 | case logging.INFO: 21 | # green 22 | record.levelname = f"\x1b[1;32m{record.levelname}\x1b[0m" 23 | case logging.WARNING: 24 | # yellow 25 | record.levelname = f"\x1b[1;33m{record.levelname}\x1b[0m" 26 | case logging.ERROR: 27 | # red 28 | record.levelname = f"\x1b[1;31m{record.levelname}\x1b[0m" 29 | case logging.CRITICAL: 30 | # magenta 31 | record.levelname = f"\x1b[1;35m{record.levelname}\x1b[0m" 32 | case _: 33 | # dark grey 34 | record.levelname = f"\x1b[1;2m{record.levelname}\x1b[0m" 35 | return super().format(record) 36 | 37 | 38 | handler = logging.StreamHandler() 39 | formatter = ColoredLevelNameFormatter( 40 | "[\x1b[1m%(asctime)s\x1b[0m] (%(levelname)s) \x1b[1;36m%(instance)s.\x1b[0m\x1b[1;3;32m%(funcName)s\x1b[0m" 41 | " @ \x1b[1;34m%(name)s\x1b[0m \x1b[5;31m➜\x1b[0m %(message)s" 42 | ) 43 | handler.setFormatter(formatter) 44 | logger.addHandler(handler) 45 | 46 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/vlc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/vlc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115oss/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115oss" 3 | version = "0.0.9" 4 | description = "115 oss upload." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115oss" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115oss" 10 | keywords = ["python-115", "oss", "upload"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | http_client_request = ">=0.0.8" 31 | http_response = ">=0.0.9" 32 | httpcore_request = ">=0.0.5" 33 | integer_tool = ">=0.0.6" 34 | orjson = "*" 35 | p115cipher = ">=0.0.4" 36 | p115pickcode = ">=0.0.5.4" 37 | python-asynctools = ">=0.1.3.4" 38 | python-dicttools = ">=0.0.4" 39 | python-filewrap = ">=0.2.9" 40 | python-hashtools = ">=0.0.6" 41 | python-http_request = ">=0.1.6" 42 | python-httpfile = ">=0.0.5.2" 43 | python-iterutils = ">=0.2.10" 44 | yarl = "*" 45 | 46 | [build-system] 47 | requires = ["poetry-core"] 48 | build-backend = "poetry.core.masonry.api" 49 | 50 | [[tool.poetry.packages]] 51 | include = "p115oss" 52 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/iina.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/iina.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115dav/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115dav" 3 | version = "0.0.10.4" 4 | description = "115 网盘 WebDAV 和 302 直链程序." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115dav" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115dav" 10 | keywords = ["python-115", "webdav"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | a2wsgi = ">=1.10.7" 31 | blacksheep = "*" 32 | cachedict = ">=0.0.6" 33 | httpagentparser = "*" 34 | orjson = "*" 35 | p115client = ">=0.0.6.5" 36 | p115pickcode = ">=0.0.5.4" 37 | path_predicate = ">=0.0.1.1" 38 | posixpatht = ">=0.0.3" 39 | pysubs2 = "*" 40 | python-dictattr = ">=0.0.5" 41 | python-encode_uri = ">=0.0.3" 42 | python-property = ">=0.0.3" 43 | python-sqlitedict = ">=0.0.1.2" 44 | python-texttools = ">=0.0.3" 45 | pyyaml = "*" 46 | rich = "*" 47 | uvicorn = "*" 48 | 49 | [tool.poetry.scripts] 50 | p115dav = "p115dav.__main__:main" 51 | 52 | [build-system] 53 | requires = ["poetry-core"] 54 | build-backend = "poetry.core.masonry.api" 55 | 56 | [[tool.poetry.packages]] 57 | include = "p115dav" 58 | -------------------------------------------------------------------------------- /modules/@p115/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "python-115" 3 | version = "0.0.9.9" 4 | description = "Python 115 client and filesystem." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/python-115" 9 | repository = "https://github.com/ChenyangGao/python-115" 10 | keywords = ["115", "client"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 4 - Beta", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | glob_pattern = ">=0.0.2" 31 | httpcore_request = ">=0.0.6.4" 32 | magnet2torrent = "*" 33 | orjson = "*" 34 | p115client = ">=0.0.7.5" 35 | posixpatht = ">=0.0.4" 36 | python-asynctools = ">=0.0.7" 37 | python-concurrenttools = ">=0.0.3" 38 | python-dictattr = ">=0.0.4" 39 | python-download = ">=0.0.3" 40 | python-filewrap = ">=0.2.6" 41 | python-hashtools = ">=0.0.3.3" 42 | python-httpfile = ">=0.0.4" 43 | python-http_request = ">=0.0.6" 44 | python-iterutils = ">=0.2.4.1" 45 | python-texttools = ">=0.0.2" 46 | python-urlopen = ">=0.0.6" 47 | python-undefined = "*" 48 | rich = "*" 49 | yarl = "*" 50 | 51 | [tool.poetry.scripts] 52 | p115 = "p115.__main__:main" 53 | 54 | [build-system] 55 | requires = ["poetry-core"] 56 | build-backend = "poetry.core.masonry.api" 57 | 58 | [[tool.poetry.packages]] 59 | include = "p115" 60 | -------------------------------------------------------------------------------- /modules/@p115checkin/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = ["main"] 6 | __doc__ = "115 签到 (check in)" 7 | 8 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 9 | from pathlib import Path 10 | 11 | if __name__ == "__main__": 12 | from sys import path 13 | 14 | path[0] = str(Path(__file__).parents[2]) 15 | parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) 16 | else: 17 | from .init import subparsers 18 | 19 | parser = subparsers.add_parser("check", description=__doc__, formatter_class=RawTextHelpFormatter) 20 | 21 | 22 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 23 | args = parser.parse_args(argv) 24 | if args.version: 25 | from p115 import __version__ 26 | print(".".join(map(str, __version__))) 27 | raise SystemExit(0) 28 | return args 29 | 30 | 31 | def main(argv: None | list[str] | Namespace = None, /): 32 | if isinstance(argv, Namespace): 33 | args = argv 34 | else: 35 | args = parse_args(argv) 36 | 37 | from p115 import P115Client 38 | 39 | if not (cookies := args.cookies): 40 | if cookies_path := args.cookies_path: 41 | cookies = Path(cookies_path) 42 | else: 43 | cookies = Path("115-cookies.txt") 44 | client = P115Client(cookies, check_for_relogin=True) 45 | print(client.user_points_sign_post()) 46 | 47 | 48 | parser.add_argument("-c", "--cookies", help="115 登录 cookies,优先级高于 -cp/--cookies-path") 49 | parser.add_argument("-cp", "--cookies-path", help="cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt") 50 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 51 | parser.set_defaults(func=main) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | 57 | -------------------------------------------------------------------------------- /p115client/tool/auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = ["deauth_open"] 6 | __doc__ = "这个模块提供了一些和账号状况有关的函数" 7 | 8 | from collections.abc import Callable, Coroutine 9 | from os import PathLike 10 | from typing import overload, Any, Literal 11 | 12 | from iterutils import run_gen_step 13 | 14 | from ..client import check_response, P115Client 15 | 16 | 17 | @overload 18 | def deauth_open( 19 | client: str | PathLike | P115Client, 20 | predicate: None | Callable = None, 21 | *, 22 | async_: Literal[False] = False, 23 | **request_kwargs, 24 | ) -> None: 25 | ... 26 | @overload 27 | def deauth_open( 28 | client: str | PathLike | P115Client, 29 | predicate: None | Callable = None, 30 | *, 31 | async_: Literal[True], 32 | **request_kwargs, 33 | ) -> Coroutine[Any, Any, None]: 34 | ... 35 | def deauth_open( 36 | client: str | PathLike | P115Client, 37 | predicate: None | Callable = None, 38 | *, 39 | async_: Literal[False, True] = False, 40 | **request_kwargs, 41 | ) -> None | Coroutine[Any, Any, None]: 42 | """批量解绑开放应用 43 | 44 | :param client: 115 客户端或 cookies 45 | :param predicate: 筛选条件 46 | :param async_: 是否异步 47 | :param request_kwargs: 其它请求参数 48 | """ 49 | if isinstance(client, (str, PathLike)): 50 | client = P115Client(client, check_for_relogin=True) 51 | def gen_step(): 52 | resp = yield client.login_open_auth_list( 53 | async_=async_, 54 | **request_kwargs, 55 | ) 56 | check_response(resp) 57 | for info in filter(predicate, resp["data"]): 58 | yield client.login_open_deauth( 59 | info["auth_id"], 60 | async_=async_, 61 | **request_kwargs, 62 | ) 63 | return run_gen_step(gen_step, async_) 64 | 65 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/nplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/nplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115servedb" 3 | version = "0.0.6.1" 4 | description = "115 网盘数据库的挂载服务" 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115servedb" 9 | repository = "https://github.com/ChenyangGao/p115client/tree/main/modules/p115servedb" 10 | keywords = ["python-115", "servedb"] 11 | classifiers = [ 12 | "License :: OSI Approved :: MIT License", 13 | "Development Status :: 5 - Production/Stable", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3", 16 | "Programming Language :: Python :: 3.12", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development", 21 | "Topic :: Software Development :: Libraries", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ] 24 | include = [ 25 | "LICENSE", 26 | ] 27 | 28 | [tool.poetry.dependencies] 29 | python = "^3.12" 30 | a2wsgi = ">=1.10.7" 31 | blacksheep = "*" 32 | blacksheep-rich-log = ">=0.0.1" 33 | cachedict = ">=0.0.6" 34 | fusepy = "*" 35 | http_response = ">=0.0.9" 36 | httpagentparser = "*" 37 | orjson = "*" 38 | p115client = ">=0.0.6.10" 39 | p115pickcode = ">=0.0.5.4" 40 | path_predicate = ">=0.0.1.1" 41 | posixpatht = ">=0.0.4" 42 | pysubs2 = "*" 43 | python-encode_uri = ">=0.0.3" 44 | python-httpfile = ">=0.0.5" 45 | python-property = ">=0.0.3" 46 | pyyaml = "*" 47 | urllib3 = "*" 48 | uvicorn = "*" 49 | wsgidav = "*" 50 | 51 | [tool.poetry.scripts] 52 | p115servedb = "p115servedb.__main__:main" 53 | 115servedb = "p115servedb.__main__:main" 54 | 115servedb-dav = "p115servedb.dav:main" 55 | 115servedb-fuse = "p115servedb.fuse:main" 56 | 57 | [build-system] 58 | requires = ["poetry-core"] 59 | build-backend = "poetry.core.masonry.api" 60 | 61 | [[tool.poetry.packages]] 62 | include = "p115servedb" 63 | -------------------------------------------------------------------------------- /modules/p115qrcode/readme.md: -------------------------------------------------------------------------------- 1 | # 115 网盘二维码扫码登录. 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115qrcode/) 安装 6 | 7 | ```console 8 | pip install -U p115qrcode 9 | ``` 10 | 11 | ## 使用 12 | 13 | ### 作为模块 14 | 15 | 详情请查看具体函数的文档 16 | 17 | ```python 18 | import p115qrcode 19 | ``` 20 | 21 | ### 作为命令行 22 | 23 | ```console 24 | $ p115qrcode -h 25 | usage: p115qrcode [-h] [-l] [-v] {cmd,web} ... 26 | 27 | 115 网盘扫码登录 28 | 29 | positional arguments: 30 | {cmd,web} 31 | 32 | options: 33 | -h, --help show this help message and exit 34 | -l, --license 输出授权信息 35 | -v, --version 输出版本号 36 | 37 | $ p115qrcode cmd -h 38 | usage: p115qrcode cmd [-h] [-o OUTPUT_FILE] [-oq] [-c COOKIES] [-cp COOKIES_PATH] [-l] [-v] 39 | [{web,ios,115ios,android,115android,115ipad,tv,qandroid,wechatmini,alipaymini,harmony}] 40 | 41 | 115 网盘扫码登录(命令行版) 42 | 43 | positional arguments: 44 | {web,ios,115ios,android,115android,115ipad,tv,qandroid,wechatmini,alipaymini,harmony} 45 | 选择一个 app 进行登录,默认值 'alipaymini' 46 | 47 | options: 48 | -h, --help show this help message and exit 49 | -o OUTPUT_FILE, --output-file OUTPUT_FILE 50 | 保存到文件,未指定时输出到 stdout 51 | -oq, --open-qrcode 在浏览器中打开二维码,而不是在命令行输出 52 | -c COOKIES, --cookies COOKIES 53 | 115 登录 cookies 或二维码的 uid,使用后可以自动扫码,优先级高于 -cp/--cookies-path 54 | -cp COOKIES_PATH, --cookies-path COOKIES_PATH 55 | cookies 文件保存路径,使用后可以自动扫码 56 | -l, --license 输出授权信息 57 | -v, --version 输出版本号 58 | 59 | $ p115qrcode web -h 60 | usage: p115qrcode web [-h] [-H HOST] [-P PORT] [-c] [-d] [-l] [-v] 61 | 62 | 115 网盘扫码登录(网页版) 63 | 64 | options: 65 | -h, --help show this help message and exit 66 | -H HOST, --host HOST ip 或 hostname,默认值:'localhost' 67 | -P PORT, --port PORT 端口号,默认值:8000 68 | -c, --cors 标识浏览器已经使用 CORS 插件,因此不需要后台代理接口的调用 69 | -d, --debug 启用 debug 模式 70 | -l, --license 输出授权信息 71 | -v, --version 输出版本号 72 | ``` 73 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/mpv.svg: -------------------------------------------------------------------------------- 1 | image/svg+xmlLogo of mpv -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/mpv.svg: -------------------------------------------------------------------------------- 1 | image/svg+xmlLogo of mpv -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/infuse.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /modules/p115ftp/readme.md: -------------------------------------------------------------------------------- 1 | # Python 115 FTP Server. 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115ftp/) 安装 6 | 7 | ```console 8 | pip install -U p115ftp 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 模块 14 | 15 | ```python 16 | from p115ftp import P115FS 17 | 18 | P115FS.run_forever() 19 | ``` 20 | 21 | ### 命令行 22 | 23 | ```console 24 | $ p115ftp -h 25 | usage: p115ftp [-h] [-H HOST] [-P PORT] [-cp COOKIES_PATH] [-cl] [-ut] [-ll LOG_LEVEL] [-l] [-v] 26 | 27 | 🕸️ Python 115 FTP Server 🕷️ 28 | 29 | 88 88 8888888888 ad88 30 | ,d88 ,d88 88 d8" ,d 31 | 888888 888888 88 ____ 88 88 32 | 8b,dPPYba, 88 88 88a8PPPP8b, MM88MMM MM88MMM 8b,dPPYba, 33 | 88P' "8a 88 88 PP" `8b 88 88 88P' "8a 34 | 88 d8 88 88 d8 88 88 88 d8 35 | 88b, ,a8" 88 88 Y8a a8P 88 88, 88b, ,a8" 36 | 88`YbbdP"' 88 88 "Y88888P" 88 "Y888 88`YbbdP"' 37 | 88 88 38 | 88 88 39 | 40 | options: 41 | -h, --help show this help message and exit 42 | -H, --host HOST ip 或 hostname,默认值:'0.0.0.0' 43 | -P, --port PORT 端口号,默认值:7115 44 | -cp, --cookies-path COOKIES_PATH 45 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 46 | 如果你需要直接传入一个 cookies 字符串,需要这样写 47 | 48 | .. code:: shell 49 | 50 | COOKIES='UID=...; CID=..., SEID=...' 51 | p115dav --cookies-path <(echo "$COOKIES") 52 | 53 | -cl, --check-for-relogin 54 | 当风控时,自动重新扫码登录 55 | -ut, --use-thumbs 为请求图片链接提供缩略图 CDN 链接 56 | -ll, --log-level LOG_LEVEL 57 | 指定日志级别,可以是数字或名称,不传此参数则不输出日志,默认值: 'ERROR' 58 | -l, --license 输出授权信息 59 | -v, --version 输出版本号 60 | ``` 61 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/infuse.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /modules/p115updatedb/readme.md: -------------------------------------------------------------------------------- 1 | # 把 115 网盘的文件列表导出到数据库 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115updatedb/) 安装 6 | 7 | ```console 8 | pip install -U p115updatedb 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 模块 14 | 15 | ```python 16 | from p115updatedb import updatedb_life_iter, updatedb_life, updatedb, updatedb_one, updatedb_tree 17 | ``` 18 | 19 | 另外也提供了一些工具函数,封装了一些数据库查询 20 | 21 | ```python 22 | from p115updatedb.query import * 23 | ``` 24 | 25 | ### 命令行 26 | 27 | ```console 28 | $ p115updatedb -h 29 | usage: p115updatedb [-h] [-cp COOKIES_PATH] [-f DBFILE] [-i INTERVAL] 30 | [-st AUTO_SPLITTING_THRESHOLD] 31 | [-sst AUTO_SPLITTING_STATISTICS_TIMEOUT] [-nm] [-nr] [-de] 32 | [-v] [-l] 33 | [dir ...] 34 | 35 | 遍历 115 网盘的目录,并把信息导出到数据库 36 | 37 | positional arguments: 38 | dir 115 目录,可以传入多个,如果不传默认为 0 39 | 允许 3 种类型的目录 40 | 1. 整数,视为目录的 id 41 | 2. 形如 "/名字/名字/..." 的路径,最前面的 "/" 可以省略,本程序会尝试获取对应的 id 42 | 3. 形如 "根目录 > 名字 > 名字 > ..." 的路径,来自点击文件的【显示属性】,在【位置】这部分看到的路径,本程序会尝试获取对应的 id 43 | 44 | options: 45 | -h, --help show this help message and exit 46 | -cp COOKIES_PATH, --cookies-path COOKIES_PATH 47 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 48 | -f DBFILE, --dbfile DBFILE 49 | sqlite 数据库文件路径,默认为在当前工作目录下的 f'115-{user_id}.db' 50 | -i INTERVAL, --interval INTERVAL 51 | 两个任务之间的睡眠时间,如果 <= 0,则不睡眠 52 | -st AUTO_SPLITTING_THRESHOLD, --auto-splitting-threshold AUTO_SPLITTING_THRESHOLD 53 | 自动拆分的文件数阈值,大于此值时,自动进行拆分,如果 = 0,则总是拆分,如果 < 0,则总是不拆分,默认值 100,000(10 万) 54 | -sst AUTO_SPLITTING_STATISTICS_TIMEOUT, --auto-splitting-statistics-timeout AUTO_SPLITTING_STATISTICS_TIMEOUT 55 | 自动拆分前的执行文件数统计的超时时间(秒),大于此值时,视为文件数无穷大,如果 <= 0,视为永不超时,默认值 3 56 | -nm, --no-dir-moved 声明没有目录被移动或改名(但可以有目录被新增或删除),这可以加快批量拉取时的速度 57 | -nr, --not-recursive 不遍历目录树:只拉取顶层目录,不递归子目录 58 | -de, --disable-event 关闭 event 表的数据收集 59 | -v, --version 输出版本号 60 | -l, --license 输出开源协议 61 | ``` 62 | -------------------------------------------------------------------------------- /modules/p115updatedb/p115updatedb/init-without-event.sql: -------------------------------------------------------------------------------- 1 | DROP TRIGGER IF EXISTS trg_data_insert; 2 | CREATE TRIGGER trg_data_insert 3 | AFTER INSERT ON data 4 | FOR EACH ROW 5 | BEGIN 6 | INSERT OR IGNORE INTO dirlen(id) SELECT NEW.id WHERE NEW.is_dir; 7 | UPDATE dirlen SET 8 | dir_count = dir_count + NEW.is_dir, 9 | file_count = file_count + NOT NEW.is_dir, 10 | tree_dir_count = tree_dir_count + NEW.is_dir, 11 | tree_file_count = tree_file_count + NOT NEW.is_dir 12 | WHERE id = NEW.parent_id; 13 | END; 14 | 15 | DROP TRIGGER IF EXISTS trg_data_update; 16 | CREATE TRIGGER trg_data_update 17 | AFTER UPDATE ON data 18 | FOR EACH ROW 19 | WHEN NOT NEW._triggered 20 | BEGIN 21 | -- 更新时间 22 | UPDATE data SET updated_at = strftime('%Y-%m-%dT%H:%M:%f+08:00', 'now', '+8 hours'), _triggered=1 WHERE id = NEW.id; 23 | -- 移除文件 24 | UPDATE dirlen SET 25 | file_count = file_count - 1, 26 | tree_file_count = tree_file_count - 1 27 | WHERE OLD.is_alive AND NOT OLD.is_dir AND NOT (NEW.is_alive AND OLD.parent_id = NEW.parent_id) AND id = OLD.parent_id; 28 | -- 移除目录 29 | UPDATE dirlen SET 30 | dir_count = dir_count - 1, 31 | tree_dir_count = tree_dir_count - 1 - (SELECT tree_dir_count FROM dirlen WHERE id = OLD.id), 32 | tree_file_count = tree_file_count - (SELECT tree_file_count FROM dirlen WHERE id = OLD.id) 33 | WHERE OLD.is_alive AND OLD.is_dir AND NOT (NEW.is_alive AND OLD.parent_id = NEW.parent_id) AND id = OLD.parent_id; 34 | -- 移入文件 35 | UPDATE dirlen SET 36 | file_count = file_count + 1, 37 | tree_file_count = tree_file_count + 1 38 | WHERE NEW.is_alive AND NOT OLD.is_dir AND NOT (OLD.is_alive AND OLD.parent_id = NEW.parent_id) AND id = NEW.parent_id; 39 | -- 移入目录 40 | UPDATE dirlen SET 41 | dir_count = dir_count + 1, 42 | tree_dir_count = tree_dir_count + 1 + (SELECT tree_dir_count FROM dirlen WHERE id = OLD.id), 43 | tree_file_count = tree_file_count + (SELECT tree_file_count FROM dirlen WHERE id = OLD.id) 44 | WHERE NEW.is_alive AND OLD.is_dir AND NOT (OLD.is_alive AND OLD.parent_id = NEW.parent_id) AND id = NEW.parent_id; 45 | -- 更新 is_alive 标记 46 | UPDATE dirlen SET is_alive = NEW.is_alive WHERE id = NEW.id; 47 | END; 48 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "p115client" 3 | version = "0.0.8.2" 4 | description = "Python 115 webdisk client." 5 | authors = ["ChenyangGao "] 6 | license = "MIT" 7 | readme = "readme.md" 8 | homepage = "https://github.com/ChenyangGao/p115client" 9 | repository = "https://github.com/ChenyangGao/p115client" 10 | documentation = "https://p115client.readthedocs.io" 11 | keywords = ["115", "webdisk", "client"] 12 | classifiers = [ 13 | "License :: OSI Approved :: MIT License", 14 | "Development Status :: 4 - Beta", 15 | "Programming Language :: Python", 16 | "Programming Language :: Python :: 3", 17 | "Programming Language :: Python :: 3.12", 18 | "Programming Language :: Python :: 3 :: Only", 19 | "Operating System :: OS Independent", 20 | "Intended Audience :: Developers", 21 | "Topic :: Software Development", 22 | "Topic :: Software Development :: Libraries", 23 | "Topic :: Software Development :: Libraries :: Python Modules", 24 | ] 25 | include = [ 26 | "LICENSE", 27 | ] 28 | 29 | [tool.poetry.dependencies] 30 | python = "^3.12" 31 | cachedict = ">=0.0.7" 32 | ed2k = ">=0.0.2.2" 33 | errno2 = ">=0.0.5" 34 | glob_pattern = ">=0.0.2" 35 | http_client_request = ">=0.1.1" 36 | http_response = ">=0.0.9" 37 | httpcore_request = ">=0.0.6.4" 38 | id2dirnode = ">=0.0.2" 39 | integer_tool = ">=0.0.6" 40 | iter_collect = ">=0.0.6" 41 | iterdir = ">=0.0.10" 42 | orjson = "*" 43 | p115cipher = ">=0.0.4" 44 | p115oss = ">=0.0.9" 45 | p115pickcode = ">=0.0.5" 46 | posixpatht = ">=0.0.3" 47 | python-argtools = ">=0.0.1" 48 | python-asynctools = ">=0.1.3" 49 | python-concurrenttools = ">=0.1.4" 50 | python-cookietools = ">=0.1.4" 51 | python-dictattr = ">=0.0.4" 52 | python-dicttools = ">=0.0.4" 53 | python-download = ">=0.0.4" 54 | python-encode_uri = ">=0.0.3" 55 | python-ensure = ">=0.0.1" 56 | python-filewrap = ">=0.2.9" 57 | python-hashtools = ">=0.0.6" 58 | python-httpfile = ">=0.0.5.4" 59 | python-http_request = ">=0.1.4" 60 | python-iterutils = ">=0.2.10" 61 | python-property = ">=0.0.3" 62 | python-startfile = ">=0.0.2" 63 | python-temporary = ">=0.0.1" 64 | python-undefined = ">=0.0.4" 65 | qrcode = "*" 66 | yarl = "*" 67 | 68 | [build-system] 69 | requires = ["poetry-core"] 70 | build-backend = "poetry.core.masonry.api" 71 | 72 | [[tool.poetry.packages]] 73 | include = "p115client" 74 | -------------------------------------------------------------------------------- /modules/p115wsgidav/readme.md: -------------------------------------------------------------------------------- 1 | # Python 115 WsgiDAV. 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115wsgidav/) 安装 6 | 7 | ```console 8 | pip install -U p115wsgidav 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 模块 14 | 15 | ```python 16 | from p115wsgidav import P115FileSystemProvider 17 | 18 | P115FileSystemProvider().run_forever() 19 | ``` 20 | 21 | ### 命令行 22 | 23 | ```console 24 | $ p115wsgidav -h 25 | usage: p115wsgidav [-h] [-H HOST] [-P PORT] [-cp COOKIES_PATH] [-cl] [-nt] [-o ORIGIN_302] [-wc WSGIDAV_CONFIG_PATH] [-l] [-v] 26 | 27 | 🕸️ Python 115 WsgiDAV 🕷️ 28 | 29 | ██████╗ ██╗ ██╗███████╗██╗ ██╗███████╗██████╗ ██████╗ █████╗ ██╗ ██╗ 30 | ██╔══██╗███║███║██╔════╝██║ ██║██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██║ 31 | ██████╔╝╚██║╚██║███████╗██║ █╗ ██║█████╗ ██████╔╝██║ ██║███████║██║ ██║ 32 | ██╔═══╝ ██║ ██║╚════██║██║███╗██║██╔══╝ ██╔══██╗██║ ██║██╔══██║╚██╗ ██╔╝ 33 | ██║ ██║ ██║███████║╚███╔███╔╝███████╗██████╔╝██████╔╝██║ ██║ ╚████╔╝ 34 | ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚══╝╚══╝ ╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ 35 | 36 | options: 37 | -h, --help show this help message and exit 38 | -H, --host HOST ip 或 hostname,默认值:'0.0.0.0' 39 | -P, --port PORT 端口号,默认值:8115 40 | -cp, --cookies-path COOKIES_PATH 41 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 42 | 如果你需要直接传入一个 cookies 字符串,需要这样写 43 | 44 | .. code:: shell 45 | 46 | COOKIES='UID=...; CID=..., SEID=...' 47 | p115dav --cookies-path <(echo "$COOKIES") 48 | 49 | -cl, --check-for-relogin 50 | 当风控时,自动重新扫码登录 51 | -nt, --no-thumbs 不要为请求图片链接提供缩略图 52 | -o, --origin-302 ORIGIN_302 53 | 设置 302 请求转发。如果为空,则由此模块提供;特别的,如果缺省此参数,则视为缓存链接;如果为空字符串 '',则不缓存 54 | -wc, --wsgidav-config-path WSGIDAV_CONFIG_PATH 55 | WsgiDAV 启动时的配置文件路径,支持 JSON、YAML 或 TOML 格式,会根据扩展名确定,不能确定时视为 JSON 56 | 如需样板文件,请阅读: 57 | 58 | https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html#sample-wsgidav-yaml 59 | 60 | -l, --license 输出授权信息 61 | -v, --version 输出版本号 62 | ``` 63 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/component/__fuse_monkey_patch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | import errno 5 | from fuse import fuse_exit, log, FUSE # type: ignore 6 | 7 | def _wrapper(func, *args, **kwargs): 8 | 'Decorator for the methods that follow' 9 | func_name = getattr(func, "__name__", "") 10 | try: 11 | if func_name == "init": 12 | # init may not fail, as its return code is just stored as 13 | # private_data field of struct fuse_context 14 | return func(*args, **kwargs) or 0 15 | else: 16 | try: 17 | return func(*args, **kwargs) or 0 18 | except OSError as e: 19 | if e.errno is None: 20 | log.error("Uncaught OSError from FUSE operation %s, " 21 | "returning errno.EIO.", 22 | func_name, exc_info=True) 23 | print(f"{type(e).__qualname__}: {e}") 24 | return -errno.EIO 25 | if e.errno > 0: 26 | log.debug( 27 | "FUSE operation %s raised a %s, returning errno %s.", 28 | func_name, type(e), e.errno, exc_info=True) 29 | return -e.errno 30 | else: 31 | log.error( 32 | "FUSE operation %s raised an OSError with negative " 33 | "errno %s, returning errno.EINVAL.", 34 | func_name, e.errno, exc_info=True) 35 | return -errno.EINVAL 36 | except Exception: 37 | log.error("Uncaught exception from FUSE operation %s, " 38 | "returning errno.EINVAL.", 39 | func_name, exc_info=True) 40 | return -errno.EINVAL 41 | except BaseException as e: 42 | #self.__critical_exception = e 43 | log.critical( 44 | "Uncaught critical exception from FUSE operation %s, aborting.", 45 | func_name, exc_info=True) 46 | # the raised exception (even SystemExit) will be caught by FUSE 47 | # potentially causing SIGSEGV, so tell system to stop/interrupt FUSE 48 | fuse_exit() 49 | return -errno.EFAULT 50 | 51 | FUSE._wrapper = staticmethod(_wrapper) 52 | -------------------------------------------------------------------------------- /modules/p115tinydav/readme.md: -------------------------------------------------------------------------------- 1 | # 115 tiny WebDAV. 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115tinydav/) 安装 6 | 7 | ```console 8 | pip install -U p115tinydav 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 作为模块 14 | 15 | ```python 16 | from p115tinydav import make_application 17 | from uvicorn import run 18 | 19 | run( 20 | make_application(debug=True), 21 | host="0.0.0.0", 22 | port=8000, 23 | proxy_headers=True, 24 | server_header=False, 25 | forwarded_allow_ips="*", 26 | timeout_graceful_shutdown=1, 27 | access_log=False, 28 | ) 29 | ``` 30 | 31 | ### 作为命令 32 | 33 | ```console 34 | $ p115tinydav -h 35 | usage: p115tinydav [-h] [-c COOKIES] [-cp COOKIES_PATH] [-H HOST] [-P PORT] [-cu] [-d] 36 | [-uc UVICORN_RUN_CONFIG_PATH] [-v] [-l] 37 | 38 | ╭───────────────────────── Welcome to 115 tiny dav ────────────────────────────╮ 39 | │ │ 40 | │ maintained by ❤ ChenyangGao https://chenyanggao.github.io │ 41 | │ │ 42 | │ Github https://github.com/ChenyangGao/p115client/ │ 43 | │ │ 44 | │ license https://www.gnu.org/licenses/gpl-3.0.txt │ 45 | │ │ 46 | │ version 0.0.1 │ 47 | │ │ 48 | ╰──────────────────────────────────────────────────────────────────────────────╯ 49 | 50 | options: 51 | -h, --help show this help message and exit 52 | -c, --cookies COOKIES 53 | cookies 字符串,优先级高于 -cp/--cookies-path 54 | -cp, --cookies-path COOKIES_PATH 55 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 56 | -H, --host HOST ip 或 hostname,默认值:'0.0.0.0' 57 | -P, --port PORT 端口号,默认值:8000,如果为 0 则自动确定 58 | -cu, --cache-url 缓存下载链接 59 | -d, --debug 启用调试,会输出更详细信息 60 | -uc, --uvicorn-run-config-path UVICORN_RUN_CONFIG_PATH 61 | uvicorn 启动时的配置文件路径,会作为关键字参数传给 `uvicorn.run`,支持 JSON、YAML 或 TOML 格式,会根据扩展名确定,不能确定时视为 JSON 62 | -v, --version 输出版本号 63 | -l, --license 输出授权信息 64 | ``` 65 | -------------------------------------------------------------------------------- /modules/p115sftp/readme.md: -------------------------------------------------------------------------------- 1 | # Python 115 SFTP Server. 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115sftp/) 安装 6 | 7 | ```console 8 | pip install -U p115sftp 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 模块 14 | 15 | ```python 16 | from p115sftp import P115RequestHandler 17 | 18 | P115RequestHandler.run_forever() 19 | ``` 20 | 21 | ### 命令行 22 | 23 | ```console 24 | $ p115sftp -h 25 | usage: p115sftp [-h] [-H HOST] [-P PORT] [-cp COOKIES_PATH] [-cl] [-ll LOG_LEVEL] [-k KEY_FILE] [-l] [-v] 26 | 27 | 🕸️ Python 115 SFTP Server 🕷️ 28 | 29 | .-. ___ 30 | / \ ( ) 31 | .-.. .--. .--. ,-----. .--. | .`. ; | |_ .-.. 32 | / \ (_ | (_ | | ___) / _ \ | |(___) ( __) / \ 33 | ' .-, ; | | | | | | . .' `. ; | |_ | | ' .-, ; 34 | | | . | | | | | | '-. | ' | | ( __) | | ___ | | . | 35 | | | | | | | | | '---. . _\_`.(___) | | | |( ) | | | | 36 | | | | | | | | | ___ ` \ ( ). '. | | | | | | | | | | 37 | | | ' | | | | | ( ) | | | | `\ | | | | ' | | | | ' | 38 | | `-' ' | | | | ; `-' / ; '._,' ' | | ' `-' ; | `-' ' 39 | | \__.' (___) (___) '.__.' '.___.' (___) `.__. | \__.' 40 | | | | | 41 | (___) (___) 42 | 43 | options: 44 | -h, --help show this help message and exit 45 | -H, --host HOST ip 或 hostname,默认值:'0.0.0.0' 46 | -P, --port PORT 端口号,默认值:6115 47 | -cp, --cookies-path COOKIES_PATH 48 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 49 | 如果你需要直接传入一个 cookies 字符串,需要这样写 50 | 51 | .. code:: shell 52 | 53 | COOKIES='UID=...; CID=..., SEID=...' 54 | p115dav --cookies-path <(echo "$COOKIES") 55 | 56 | -cl, --check-for-relogin 57 | 当风控时,自动重新扫码登录 58 | -ll, --log-level LOG_LEVEL 59 | 指定日志级别,可以是数字或名称,不传此参数则不输出日志,默认值: 'INFO' 60 | -k, --key-file KEY_FILE 61 | 服务器私钥文件,如果不提供则随机生成 62 | -l, --license 输出授权信息 63 | -v, --version 输出版本号 64 | ``` 65 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/views/search.html: -------------------------------------------------------------------------------- 1 | 33 | 34 | 39 | 40 | 86 | -------------------------------------------------------------------------------- /modules/p115qrcode/@qrcode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = ["main"] 6 | __doc__ = "扫码获取 115 cookies" 7 | 8 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 9 | from pathlib import Path 10 | 11 | if __name__ == "__main__": 12 | from sys import path 13 | 14 | path[0] = str(Path(__file__).parents[2]) 15 | parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) 16 | else: 17 | from .init import subparsers 18 | 19 | parser = subparsers.add_parser("qrcode", description=__doc__, formatter_class=RawTextHelpFormatter) 20 | 21 | 22 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 23 | args = parser.parse_args(argv) 24 | if args.version: 25 | from p115 import __version__ 26 | print(".".join(map(str, __version__))) 27 | raise SystemExit(0) 28 | return args 29 | 30 | 31 | def main(argv: None | list[str] | Namespace = None, /): 32 | if isinstance(argv, Namespace): 33 | args = argv 34 | else: 35 | args = parse_args(argv) 36 | 37 | from typing import TextIO 38 | from p115 import P115Client 39 | 40 | if not (cookies := args.cookies): 41 | try: 42 | if cookies_path := args.cookies_path: 43 | cookies = open(cookies_path).read() 44 | else: 45 | cookies = open("115-cookies.txt").read() 46 | except FileNotFoundError: 47 | cookies = None 48 | app = args.app 49 | if app == "desktop": 50 | app = "web" 51 | client = P115Client(cookies, check_for_relogin=True, ensure_cookies=True, app=app) 52 | if client.login_app() != app: 53 | client = client.login_another_app(app) 54 | if outfile := args.output_file: 55 | try: 56 | file: TextIO = open(outfile, "w") 57 | except OSError as e: 58 | print(f"error occured: {e!r}") 59 | from sys import stdout as file 60 | else: 61 | from sys import stdout as file 62 | print(client.cookies_str, file=file) 63 | 64 | 65 | from p115 import AVAILABLE_APPS 66 | 67 | parser.add_argument("app", nargs="?", default="alipaymini", choices=AVAILABLE_APPS, 68 | help="选择一个 app 进行登录,默认值 'alipaymini'") 69 | parser.add_argument("-o", "--output-file", help="保存到文件,未指定时输出到 stdout") 70 | parser.add_argument("-c", "--cookies", help="115 登录 cookies,使用后可以自动扫码,优先级高于 -cp/--cookies-path") 71 | parser.add_argument("-cp", "--cookies-path", help="cookies 文件保存路径,使用后可以自动扫码") 72 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 73 | parser.set_defaults(func=main) 74 | 75 | 76 | if __name__ == "__main__": 77 | main() 78 | 79 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/mxplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/mxplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/mxplayer-pro.svg: -------------------------------------------------------------------------------- 1 | PRO -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/mxplayer-pro.svg: -------------------------------------------------------------------------------- 1 | PRO -------------------------------------------------------------------------------- /modules/p115fuse/readme.md: -------------------------------------------------------------------------------- 1 | # Python 115 FUSE mount. 2 | 3 | ## 安装 4 | 5 | 你可以通过 [pypi](https://pypi.org/project/p115fuse/) 安装 6 | 7 | ```console 8 | pip install -U p115fuse 9 | ``` 10 | 11 | ## 用法 12 | 13 | ### 模块 14 | 15 | ```python 16 | from p115fuse import P115FuseOperations 17 | 18 | P115FuseOperations().run_forever( 19 | "p115fuse", 20 | foreground=True, 21 | max_readahead=0, 22 | noauto_cache=True, 23 | ) 24 | ``` 25 | 26 | ### 命令行 27 | 28 | ```console 29 | $ p115fuse -h 30 | usage: p115fuse [-h] [-cp COOKIES_PATH] [-cl] [-fo option [option ...]] [-ll LOG_LEVEL] [-l] [-v] [mount_point] 31 | 32 | 🕸️ Python 115 FUSE mount 🕷️ 33 | 34 | ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ 35 | ▐░░░░░░░░░░░▌ ▄█░░░░▌ ▄█░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ 36 | ▐░█▀▀▀▀▀▀▀█░▌▐░░▌▐░░▌ ▐░░▌▐░░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ 37 | ▐░▌ ▐░▌ ▀▀ ▐░░▌ ▀▀ ▐░░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ 38 | ▐░█▄▄▄▄▄▄▄█░▌ ▐░░▌ ▐░░▌ ▐░░░░░░░░░░░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ 39 | ▐░░░░░░░░░░░▌ ▐░░▌ ▐░░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ 40 | ▐░█▀▀▀▀▀▀▀▀▀ ▐░░▌ ▐░░▌ ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ 41 | ▐░▌ ▐░░▌ ▐░░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ 42 | ▐░▌ ▄▄▄▄█░░█▄▄▄ ▄▄▄▄█░░█▄▄▄ ▄▄▄▄▄▄▄▄▄█░▌▐░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ 43 | ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ 44 | ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ 45 | 46 | positional arguments: 47 | mount_point 挂载路径 48 | 49 | options: 50 | -h, --help show this help message and exit 51 | -cp, --cookies-path COOKIES_PATH 52 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 53 | 如果你需要直接传入一个 cookies 字符串,需要这样写 54 | 55 | .. code:: shell 56 | 57 | COOKIES='UID=...; CID=..., SEID=...' 58 | p115dav --cookies-path <(echo "$COOKIES") 59 | 60 | -cl, --check-for-relogin 61 | 当风控时,自动重新扫码登录 62 | -fo, --fuse-option option [option ...] 63 | fuse 挂载选项,支持如下几种格式: 64 | - name 设置 name 选项 65 | - name= 取消 name 选项 66 | - name=value 设置 name 选项,值为 value 67 | 参考资料: 68 | - https://man7.org/linux/man-pages/man8/mount.fuse3.8.html 69 | - https://code.google.com/archive/p/macfuse/wikis/OPTIONS.wiki 70 | -ll, --log-level LOG_LEVEL 71 | 指定日志级别,可以是数字或名称,不传此参数则不输出日志,默认值: 'ERROR' 72 | -l, --license 输出授权信息 73 | -v, --version 输出版本号 74 | ``` 75 | -------------------------------------------------------------------------------- /modules/p115ftp/p115ftp/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | # NOTE: https://patorjk.com/software/taag/#p=display&f=Univers&t=p115ftp 6 | __doc__ = """ 7 | 🕸️ Python 115 FTP Server 🕷️ 8 | 9 | 88 88 8888888888 ad88 10 | ,d88 ,d88 88 d8" ,d 11 | 888888 888888 88 ____ 88 88 12 | 8b,dPPYba, 88 88 88a8PPPP8b, MM88MMM MM88MMM 8b,dPPYba, 13 | 88P' "8a 88 88 PP" `8b 88 88 88P' "8a 14 | 88 d8 88 88 d8 88 88 88 d8 15 | 88b, ,a8" 88 88 Y8a a8P 88 88, 88b, ,a8" 16 | 88`YbbdP"' 88 88 "Y88888P" 88 "Y888 88`YbbdP"' 17 | 88 88 18 | 88 88 19 | 20 | """ 21 | 22 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 23 | from pathlib import Path 24 | 25 | parser = ArgumentParser(formatter_class=RawTextHelpFormatter, description=__doc__) 26 | parser.add_argument("-H", "--host", help="ip 或 hostname,默认值:'0.0.0.0'") 27 | parser.add_argument("-P", "--port", type=int, help="端口号,默认值:7115") 28 | parser.add_argument("-cp", "--cookies-path", help="""\ 29 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 30 | 如果你需要直接传入一个 cookies 字符串,需要这样写 31 | 32 | .. code:: shell 33 | 34 | COOKIES='UID=...; CID=..., SEID=...' 35 | p115dav --cookies-path <(echo "$COOKIES") 36 | 37 | """) 38 | parser.add_argument("-cl", "--check-for-relogin", action="store_true", help="当风控时,自动重新扫码登录") 39 | parser.add_argument("-ll", "--log-level", default="ERROR", help=f"指定日志级别,可以是数字或名称,不传此参数则不输出日志,默认值: 'ERROR'") 40 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 41 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 42 | 43 | 44 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 45 | args = parser.parse_args(argv) 46 | if args.version: 47 | from p115ftp import __version__ 48 | print(".".join(map(str, __version__))) 49 | raise SystemExit(0) 50 | elif args.license: 51 | from p115ftp import __license__ 52 | print(__license__) 53 | raise SystemExit(0) 54 | return args 55 | 56 | 57 | def main(argv: None | list[str] | Namespace = None, /): 58 | if isinstance(argv, Namespace): 59 | args = argv 60 | else: 61 | args = parse_args(argv) 62 | 63 | import logging 64 | 65 | from p115client import P115Client 66 | from p115ftp import P115FS, logger 67 | 68 | if log_level := args.log_level: 69 | if log_level.isascii() and log_level.isdecimal(): 70 | log_level = int(log_level) 71 | else: 72 | log_level = getattr(logging, log_level.upper(), 0) 73 | if log_level: 74 | logger.setLevel(log_level) 75 | 76 | host = args.host or "0.0.0.0" 77 | port = args.port or 7115 78 | cookies_path = Path(args.cookies_path or "115-cookies.txt") 79 | client = P115Client(cookies_path, check_for_relogin=args.check_for_relogin) 80 | P115FS.run_forever(client, host, port) 81 | 82 | 83 | if __name__ == "__main__": 84 | from sys import path 85 | 86 | path[0] = str(Path(__file__).parents[1]) 87 | main() 88 | 89 | -------------------------------------------------------------------------------- /modules/p115updatedb/p115updatedb/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __doc__ = "遍历 115 网盘的目录,并把信息导出到数据库" 6 | 7 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 8 | from pathlib import Path 9 | 10 | parser = ArgumentParser( 11 | formatter_class=RawTextHelpFormatter, 12 | description=__doc__, 13 | ) 14 | parser.add_argument("top_dirs", metavar="dir", nargs="*", help="""\ 15 | 115 目录,可以传入多个,如果不传默认为 0 16 | 允许 3 种类型的目录 17 | 1. 整数,视为目录的 id 18 | 2. 形如 "/名字/名字/..." 的路径,最前面的 "/" 可以省略,本程序会尝试获取对应的 id 19 | 3. 形如 "根目录 > 名字 > 名字 > ..." 的路径,来自点击文件的【显示属性】,在【位置】这部分看到的路径,本程序会尝试获取对应的 id 20 | """) 21 | parser.add_argument("-cp", "--cookies-path", default="", help="cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt") 22 | parser.add_argument("-f", "--dbfile", default="", help="sqlite 数据库文件路径,默认为在当前工作目录下的 f'115-{user_id}.db'") 23 | parser.add_argument("-i", "--interval", type=float, default=0.5, help="两个批处理任务至少需要间隔的时间(以启动前那一刻作为计算依据),默认值: 0.5") 24 | parser.add_argument("-st", "--auto-splitting-threshold", type=int, default=300_000, help="自动拆分的文件数阈值,大于此值时,自动进行拆分,如果 = 0,则总是拆分,如果 < 0,则总是不拆分,默认值 300,000(30 万)") 25 | parser.add_argument("-sst", "--auto-splitting-statistics-timeout", type=float, default=5.0, help="自动拆分前的执行文件数统计的超时时间(秒),大于此值时,视为文件数无穷大,如果 <= 0,视为永不超时,默认值 5.0") 26 | parser.add_argument("-nm", "--no-dir-moved", action="store_true", help="声明没有目录被移动或改名(但可以有目录被新增或删除),这可以加快批量拉取时的速度") 27 | parser.add_argument("-r", "--refresh", action="store_true", help="是否强制刷新") 28 | parser.add_argument("-nr", "--not-recursive", action="store_true", help="不遍历目录树:只拉取顶层目录,不递归子目录") 29 | parser.add_argument("-de", "--disable-event", action="store_true", help="关闭 event 表的数据收集") 30 | parser.add_argument("-cl", "--check-for-relogin", action="store_true", help="当风控时,自动重新扫码登录") 31 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 32 | parser.add_argument("-l", "--license", action="store_true", help="输出开源协议") 33 | 34 | 35 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 36 | args = parser.parse_args(argv) 37 | if args.version: 38 | from p115updatedb import __version__ 39 | print(".".join(map(str, __version__))) 40 | raise SystemExit(0) 41 | elif args.license: 42 | from p115updatedb import __license__ 43 | print(__license__) 44 | raise SystemExit(0) 45 | return args 46 | 47 | 48 | def main(argv: None | list[str] | Namespace = None, /): 49 | if isinstance(argv, Namespace): 50 | args = argv 51 | else: 52 | args = parse_args(argv) 53 | 54 | from p115client import P115Client 55 | from p115updatedb import updatedb 56 | 57 | if cookies_path := args.cookies_path: 58 | cookies = Path(cookies_path) 59 | else: 60 | cookies = Path("115-cookies.txt") 61 | client = P115Client(cookies, check_for_relogin=args.check_for_relogin, ensure_cookies=True, app="alipaymini") 62 | updatedb( 63 | client, 64 | dbfile=args.dbfile, 65 | top_dirs=args.top_dirs or 0, 66 | auto_splitting_threshold=args.auto_splitting_threshold, 67 | auto_splitting_statistics_timeout=args.auto_splitting_statistics_timeout, 68 | no_dir_moved=args.no_dir_moved, 69 | refresh=args.refresh, 70 | recursive=not args.not_recursive, 71 | interval=args.interval, 72 | disable_event=args.disable_event, 73 | ) 74 | 75 | 76 | if __name__ == "__main__": 77 | from sys import path 78 | 79 | path[0] = str(Path(__file__).parents[1]) 80 | main() 81 | 82 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/component/db.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = [ 6 | "attr_to_path", "get_id_from_db", "get_pickcode_from_db", "get_sha1_from_db", 7 | "get_path_from_db", "get_ancestors_from_db", "get_attr_from_db", "get_children_from_db", 8 | ] 9 | 10 | from collections.abc import Mapping, Iterable, Sequence 11 | from sqlite3 import Connection, Cursor 12 | from typing import Any 13 | 14 | from dictattr import AttrDict 15 | from encode_uri import encode_uri_component_loose 16 | from p115client.tool import P115QueryDB 17 | 18 | 19 | def normattr(m: Mapping | Iterable[tuple[str, Any]], /) -> AttrDict: 20 | attr: AttrDict = AttrDict(m) 21 | attr["id"] = str(attr["id"]) 22 | attr["parent_id"] = str(attr["parent_id"]) 23 | name = encode_uri_component_loose(attr["name"]) 24 | if attr["is_dir"]: 25 | attr["url"] = f"/{name}?file=false&id={attr['id']}" 26 | attr["ico"] = "folder" 27 | else: 28 | attr["url"] = f"/{name}?file=true&pickcode={attr['pickcode']}" 29 | if attr.get("is_collect", False) and attr["size"] < 1024 * 1024 * 115: 30 | attr["url"] += "&web=true" 31 | attr["ico"] = attr["name"].rpartition(".")[-1].lower() 32 | if "ctime" not in attr: 33 | attr["ctime"] = attr.get("created_at", 0) 34 | if "mtime" not in attr: 35 | attr["mtime"] = attr.get("updated_at", 0) 36 | return attr 37 | 38 | 39 | def attr_to_path( 40 | con: Connection | Cursor, 41 | /, 42 | path: str | Sequence[str] = "", 43 | ensure_file: None | bool = None, 44 | parent_id: int = 0, 45 | ) -> AttrDict: 46 | id = P115QueryDB(con).id_to_path(path, ensure_file=ensure_file, parent_id=parent_id) 47 | return get_attr_from_db(con, id) 48 | 49 | 50 | def get_id_from_db( 51 | con: Connection | Cursor, 52 | /, 53 | pickcode: str = "", 54 | sha1: str = "", 55 | path: str = "", 56 | ) -> int: 57 | return P115QueryDB(con).get_id(pickcode, sha1, path) 58 | 59 | 60 | def get_pickcode_from_db( 61 | con: Connection | Cursor, 62 | /, 63 | id: int = -1, 64 | sha1: str = "", 65 | path: str = "", 66 | ) -> str: 67 | return P115QueryDB(con).get_pickcode(id, sha1, path) 68 | 69 | 70 | def get_sha1_from_db( 71 | con: Connection | Cursor, 72 | /, 73 | id: int = -1, 74 | pickcode: str = "", 75 | path: str = "", 76 | ) -> str: 77 | return P115QueryDB(con).get_sha1(id, pickcode, path) 78 | 79 | 80 | def get_path_from_db( 81 | con: Connection | Cursor, 82 | id: int = 0, 83 | /, 84 | ) -> str: 85 | return P115QueryDB(con).get_path(id) 86 | 87 | 88 | def get_ancestors_from_db( 89 | con: Connection | Cursor, 90 | id: int = 0, 91 | /, 92 | ) -> list[dict]: 93 | ancestors = P115QueryDB(con).get_ancestors(id) 94 | for a in ancestors: 95 | a["id"] = str(a["id"]) 96 | a["parent_id"] = str(a["parent_id"]) 97 | return ancestors 98 | 99 | 100 | def get_attr_from_db( 101 | con: Connection | Cursor, 102 | id: int = 0, 103 | /, 104 | ) -> AttrDict: 105 | return normattr(P115QueryDB(con).get_attr(id)) 106 | 107 | 108 | def get_children_from_db( 109 | con: Connection | Cursor, 110 | id: int = 0, 111 | /, 112 | ) -> list[AttrDict]: 113 | ls = list(map(normattr, P115QueryDB(con).iter_children(id))) 114 | ls.sort(key=lambda a: (1 - a["is_dir"], a["name"])) 115 | return ls 116 | 117 | -------------------------------------------------------------------------------- /modules/p115updatedb/p115updatedb/init.sql: -------------------------------------------------------------------------------- 1 | -- 修改日志模式为 WAL (write-ahead-log) 2 | PRAGMA journal_mode = WAL; 3 | 4 | -- 允许触发器递归触发 5 | PRAGMA recursive_triggers = ON; 6 | 7 | -- data 表,用来保存数据 8 | CREATE TABLE IF NOT EXISTS data ( 9 | id INTEGER NOT NULL PRIMARY KEY, -- id 10 | parent_id INTEGER NOT NULL, -- 上级目录 id 11 | name TEXT NOT NULL, -- 名字 12 | sha1 TEXT NOT NULL DEFAULT '', -- 文件的 sha1 散列值 13 | size INTEGER NOT NULL DEFAULT 0, -- 文件大小 14 | pickcode TEXT NOT NULL DEFAULT '', -- 提取码 15 | type INTEGER NOT NULL DEFAULT 0, -- 文件类型,目录的 type 总是 0 16 | ctime INTEGER NOT NULL DEFAULT 0, -- 创建时间戳,一旦设置就不会更新 17 | mtime INTEGER NOT NULL DEFAULT 0, -- 更新时间戳,如果名字、备注被设置(即使值没变),或者(如果自己是目录)进出回收站或增删直接子节点或设置封面,会更新此值,但移动并不更新 18 | is_dir INTEGER NOT NULL CHECK(is_dir IN (0, 1)), -- 是否目录 19 | is_collect INTEGER NOT NULL DEFAULT 0, -- 是否已被标记为违规 20 | is_alive INTEGER NOT NULL DEFAULT 1 CHECK(is_alive IN (0, 1)), -- 是否存活中(未被移除) 21 | extra BLOB DEFAULT NULL, -- 额外的数据 22 | updated_at DATETIME DEFAULT (strftime('%Y-%m-%dT%H:%M:%f+08:00', 'now', '+8 hours')), -- 最近一次更新时间 23 | _triggered INTEGER NOT NULL DEFAULT 0 -- 是否执行过触发器 24 | ); 25 | 26 | -- life 表,用来收集 115 生活事件 27 | CREATE TABLE IF NOT EXISTS life ( 28 | id INTEGER NOT NULL PRIMARY KEY, -- 事件 id 29 | data JSON NOT NULL, -- 事件日志数据 30 | create_time INTEGER NOT NULL -- 事件时间 31 | ); 32 | 33 | -- event 表,用于记录 data 表上发生的变更事件 34 | CREATE TABLE IF NOT EXISTS event ( 35 | _id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键 36 | id INTEGER NOT NULL, -- 文件或目录的 id 37 | old JSON DEFAULT NULL, -- 更新前的值 38 | diff JSON NOT NULL, -- 将更新的值 39 | fs JSON DEFAULT NULL, -- 发生的文件系统事件:add:新增,remove:移除,revert:还原,move:移动,rename:重名 40 | created_at DATETIME DEFAULT (strftime('%Y-%m-%dT%H:%M:%f+08:00', 'now', '+8 hours')) -- 创建时间 41 | ); 42 | 43 | -- dirlen 表,用于记录 data 表中每个目录的节点数 44 | CREATE TABLE IF NOT EXISTS dirlen ( 45 | id INTEGER NOT NULL PRIMARY KEY, -- 目录 id 46 | dir_count INTEGER NOT NULL DEFAULT 0, -- 直属目录数 47 | file_count INTEGER NOT NULL DEFAULT 0, -- 直属文件数 48 | tree_dir_count INTEGER NOT NULL DEFAULT 0, -- 子目录树目录数 49 | tree_file_count INTEGER NOT NULL DEFAULT 0, -- 子目录树文件数 50 | is_alive INTEGER NOT NULL DEFAULT 1 CHECK(is_alive IN (0, 1)) -- 是否存活中(未被移除) 51 | ); 52 | 53 | -- dirlen 表插入根节点 54 | INSERT OR IGNORE INTO dirlen(id) VALUES (0); 55 | 56 | -- 触发器,用来更新 dirlen 表 57 | CREATE TRIGGER IF NOT EXISTS trg_dirlen_update 58 | AFTER UPDATE ON dirlen 59 | FOR EACH ROW 60 | WHEN OLD.id AND OLD.is_alive AND (OLD.tree_dir_count != NEW.tree_dir_count OR OLD.tree_file_count != NEW.tree_file_count) 61 | BEGIN 62 | UPDATE dirlen SET 63 | tree_dir_count = tree_dir_count + NEW.tree_dir_count - OLD.tree_dir_count, 64 | tree_file_count = tree_file_count + NEW.tree_file_count - OLD.tree_file_count 65 | WHERE 66 | id = (SELECT parent_id FROM data WHERE id = OLD.id); 67 | END; 68 | 69 | -- 触发器,用来丢弃 mtime 较早的更新 70 | CREATE TRIGGER IF NOT EXISTS trg_data_before_update 71 | BEFORE UPDATE ON data 72 | FOR EACH ROW 73 | BEGIN 74 | SELECT CASE 75 | WHEN NEW.mtime < OLD.mtime THEN RAISE(IGNORE) 76 | END; 77 | END; 78 | 79 | -- 索引 80 | CREATE INDEX IF NOT EXISTS idx_data_pid ON data(parent_id); 81 | CREATE INDEX IF NOT EXISTS idx_data_pc ON data(pickcode); 82 | CREATE INDEX IF NOT EXISTS idx_data_sha1 ON data(sha1); 83 | CREATE INDEX IF NOT EXISTS idx_data_name ON data(name); 84 | CREATE INDEX IF NOT EXISTS idx_data_utime ON data(updated_at); 85 | CREATE INDEX IF NOT EXISTS idx_life_create ON life(create_time); 86 | CREATE INDEX IF NOT EXISTS idx_event_create ON event(created_at); 87 | -------------------------------------------------------------------------------- /modules/p115rsacipher/p115rsacipher/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 1) 6 | __all__ = ["encrypt", "decrypt"] 7 | 8 | from collections.abc import Buffer, Iterator, Sized 9 | from base64 import b64decode, b64encode 10 | from typing import Final 11 | 12 | G_kts: Final = b"\xf0\xe5i\xae\xbf\xdc\xbf\x8a\x1aE\xe8\xbe}\xa6s\xb8\xde\x8f\xe7\xc4E\xda\x86\xc4\x9bd\x8b\x14j\xb4\xf1\xaa8\x015\x9e&i,\x86\x00kO\xa564b\xa6*\x96h\x18\xf2J\xfd\xbdk\x97\x8fM\x8f\x89\x13\xb7l\x8e\x93\xed\x0e\rH>\xd7/\x88\xd8\xfe\xfe~\x86P\x95O\xd1\xeb\x83&4\xdbf{\x9c~\x9dz\x812\xea\xb63\xde:\xa9Y4f;\xaa\xba\x81`H\xb9\xd5\x81\x9c\xf8l\x84w\xffTx&_\xbe\xe8\x1e6\x9f4\x80\\E,\x9bv\xd5\x1b\x8f\xcc\xc3\xb8\xf5" 13 | RSA_e: Final = 0x8686980c0f5a24c4b9d43020cd2c22703ff3f450756529058b1cf88f09b8602136477198a6e2683149659bd122c33592fdb5ad47944ad1ea4d36c6b172aad6338c3bb6ac6227502d010993ac967d1aef00f0c8e038de2e4d3bc2ec368af2e9f10a6f1eda4f7262f136420c07c331b871bf139f74f3010e3c4fe57df3afb71683 14 | RSA_n: Final = 0x10001 15 | 16 | to_bytes = int.to_bytes 17 | from_bytes = int.from_bytes 18 | 19 | 20 | def acc_step(start: int, stop: int, step: int = 1) -> Iterator[tuple[int, int, int]]: 21 | for i in range(start + step, stop, step): 22 | yield start, i, step 23 | start = i 24 | if start != stop: 25 | yield start, stop, stop - start 26 | 27 | 28 | def bytes_xor(v1: Buffer, v2: Buffer, /) -> bytes: 29 | return to_bytes( 30 | from_bytes(v1) ^ from_bytes(v2), 31 | len(v1 if isinstance(v1, Sized) else memoryview(v1)), 32 | ) 33 | 34 | 35 | def gen_key(rand_key: Buffer, sk_len: int, /) -> bytearray: 36 | xor_key = bytearray(sk_len) 37 | length = sk_len * (sk_len - 1) 38 | index = 0 39 | if not isinstance(rand_key, (bytes, bytearray, memoryview)): 40 | rand_key = memoryview(rand_key) 41 | for i in range(sk_len): 42 | x = (rand_key[i] + G_kts[index]) & 0xff 43 | xor_key[i] = G_kts[length] ^ x 44 | length -= sk_len 45 | index += sk_len 46 | return xor_key 47 | 48 | 49 | def pad_pkcs1_v1_5(message: Buffer, /) -> int: 50 | length = len(message if isinstance(message, Sized) else memoryview(message)) 51 | return from_bytes(b"\x00" + b"\x02" * (126 - length) + b"\x00" + message) 52 | 53 | 54 | def xor(src: Buffer, key: Buffer, /) -> bytearray: 55 | src = memoryview(src) 56 | key = memoryview(key) 57 | secret = bytearray() 58 | i = len(src) & 0b11 59 | if i: 60 | secret += bytes_xor(src[:i], key[:i]) 61 | for i, j, s in acc_step(i, len(src), len(key)): 62 | secret += bytes_xor(src[i:j], key[:s]) 63 | return secret 64 | 65 | 66 | def encrypt(data: str | Buffer, /) -> bytes: 67 | if isinstance(data, str): 68 | data = bytes(data, "utf-8") 69 | xor_text = bytearray(16) 70 | tmp = memoryview(xor(data, b"\x8d\xa5\xa5\x8d"))[::-1] 71 | xor_text += xor(tmp, b"x\x06\xadL3\x86]\x18L\x01?F") 72 | cipher_data = bytearray() 73 | view = memoryview(xor_text) 74 | for l, r, _ in acc_step(0, len(view), 117): 75 | cipher_data += to_bytes(pow(pad_pkcs1_v1_5(view[l:r]), RSA_n, RSA_e), 128) 76 | return b64encode(cipher_data) 77 | 78 | 79 | def decrypt(cipher_data: str | Buffer, /) -> bytearray: 80 | cipher_data = memoryview(b64decode(cipher_data)) 81 | data = bytearray() 82 | for l, r, _ in acc_step(0, len(cipher_data), 128): 83 | p = pow(from_bytes(cipher_data[l:r]), RSA_n, RSA_e) 84 | b = to_bytes(p, (p.bit_length() + 0b111) >> 3) 85 | data += memoryview(b)[b.index(0)+1:] 86 | m = memoryview(data) 87 | key_l = gen_key(m[:16], 12) 88 | tmp = memoryview(xor(m[16:], key_l))[::-1] 89 | return xor(tmp, b"\x8d\xa5\xa5\x8d") 90 | 91 | -------------------------------------------------------------------------------- /modules/p115qrcode/p115qrcode/cmd/cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __all__ = ["parser", "main"] 6 | __doc__ = "115 网盘扫码登录(命令行版)" 7 | 8 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 9 | from pathlib import Path 10 | 11 | if __name__ == "__main__": 12 | from sys import path 13 | 14 | path[0] = str(Path(__file__).parents[2]) 15 | parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) 16 | else: 17 | from .init import subparsers 18 | 19 | parser = subparsers.add_parser("cmd", description=__doc__, formatter_class=RawTextHelpFormatter) 20 | 21 | 22 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 23 | args = parser.parse_args(argv) 24 | if args.version: 25 | from p115qrcode import __version__ 26 | print(".".join(map(str, __version__))) 27 | raise SystemExit(0) 28 | elif args.license: 29 | from p115qrcode import __license__ 30 | print(__license__) 31 | raise SystemExit(0) 32 | return args 33 | 34 | 35 | def main(argv: None | list[str] | Namespace = None, /): 36 | if isinstance(argv, Namespace): 37 | args = argv 38 | else: 39 | args = parse_args(argv) 40 | 41 | from string import hexdigits 42 | from typing import cast, TextIO 43 | 44 | from p115qrcode import qrcode_result, qrcode_scan, qrcode_scan_confirm, qrcode_token, scan_qrcode 45 | 46 | app: str = args.app 47 | cookies: str = args.cookies.strip() 48 | if not cookies: 49 | cookies_path = args.cookies_path 50 | if not cookies_path: 51 | cookies_path = Path("~/115-cookies.txt").expanduser() 52 | try: 53 | cookies = open(cookies_path, "r", encoding="utf-8").read().strip() 54 | except OSError: 55 | pass 56 | resp: None | dict = None 57 | try: 58 | if cookies: 59 | if len(cookies) == 40 and not cookies.strip(hexdigits): 60 | uid = cookies 61 | elif all(k in cookies for k in ("UID=", "CID=", "SEID=")): 62 | uid = qrcode_token()["uid"] 63 | qrcode_scan(uid, cookies) 64 | qrcode_scan_confirm(uid, cookies) 65 | else: 66 | raise OSError 67 | resp = qrcode_result(uid, app) 68 | except OSError: 69 | pass 70 | if not resp: 71 | future = scan_qrcode(app, console_qrcode=not args.open_qrcode, show_message=True) 72 | uid = future.uid 73 | resp = cast(dict, future.result()) 74 | cookies = "; ".join(f"{k}={v}" for k, v in resp["cookie"].items()) 75 | cookies += f"; uid={uid}" 76 | if outfile := args.output_file: 77 | try: 78 | file: TextIO = open(outfile, "w", encoding="utf-8") 79 | except OSError as e: 80 | print(f"error occured: {e!r}") 81 | from sys import stdout as file 82 | else: 83 | from sys import stdout as file 84 | print(cookies, file=file) 85 | 86 | 87 | parser.add_argument( 88 | "app", nargs="?", default="alipaymini", 89 | choices=("web", "ios", "115ios", "android", "115android", "115ipad", "tv", "qandroid", "wechatmini", "alipaymini", "harmony"), 90 | help="选择一个 app 进行登录,默认值 'alipaymini'", 91 | ) 92 | parser.add_argument("-o", "--output-file", help="保存到文件,未指定时输出到 stdout") 93 | parser.add_argument("-oq", "--open-qrcode", action="store_true", help="在浏览器中打开二维码,而不是在命令行输出") 94 | parser.add_argument("-c", "--cookies", default="", help="115 登录 cookies 或二维码的 uid,使用后可以自动扫码,优先级高于 -cp/--cookies-path") 95 | parser.add_argument("-cp", "--cookies-path", help="cookies 文件保存路径,使用后可以自动扫码") 96 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 97 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 98 | parser.set_defaults(func=main) 99 | 100 | 101 | if __name__ == "__main__": 102 | main() 103 | 104 | -------------------------------------------------------------------------------- /modules/p115sftp/p115sftp/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | # NOTE: https://patorjk.com/software/taag/#p=display&f=Sweet&t=p115sftp 6 | __doc__ = r""" 🕸️ Python 115 SFTP Server 🕷️ 7 | 8 | .-. ___ 9 | / \ ( ) 10 | .-.. .--. .--. ,-----. .--. | .`. ; | |_ .-.. 11 | / \ (_ | (_ | | ___) / _ \ | |(___) ( __) / \ 12 | ' .-, ; | | | | | | . .' `. ; | |_ | | ' .-, ; 13 | | | . | | | | | | '-. | ' | | ( __) | | ___ | | . | 14 | | | | | | | | | '---. . _\_`.(___) | | | |( ) | | | | 15 | | | | | | | | | ___ ` \ ( ). '. | | | | | | | | | | 16 | | | ' | | | | | ( ) | | | | `\ | | | | ' | | | | ' | 17 | | `-' ' | | | | ; `-' / ; '._,' ' | | ' `-' ; | `-' ' 18 | | \__.' (___) (___) '.__.' '.___.' (___) `.__. | \__.' 19 | | | | | 20 | (___) (___) 21 | """ 22 | 23 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 24 | from pathlib import Path 25 | 26 | parser = ArgumentParser(formatter_class=RawTextHelpFormatter, description=__doc__) 27 | parser.add_argument("-H", "--host", default="0.0.0.0", help="ip 或 hostname,默认值:'0.0.0.0'") 28 | parser.add_argument("-P", "--port", type=int, default=6115, help="端口号,默认值:6115") 29 | parser.add_argument("-cp", "--cookies-path", help="""\ 30 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 31 | 如果你需要直接传入一个 cookies 字符串,需要这样写 32 | 33 | .. code:: shell 34 | 35 | COOKIES='UID=...; CID=..., SEID=...' 36 | p115dav --cookies-path <(echo "$COOKIES") 37 | 38 | """) 39 | parser.add_argument("-cl", "--check-for-relogin", action="store_true", help="当风控时,自动重新扫码登录") 40 | parser.add_argument("-ll", "--log-level", default="INFO", help=f"指定日志级别,可以是数字或名称,不传此参数则不输出日志,默认值: 'INFO'") 41 | parser.add_argument("-k", "--key-file", help="服务器私钥文件,如果不提供则随机生成") 42 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 43 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 44 | 45 | 46 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 47 | args = parser.parse_args(argv) 48 | if args.version: 49 | from p115ftp import __version__ 50 | print(".".join(map(str, __version__))) 51 | raise SystemExit(0) 52 | elif args.license: 53 | from p115ftp import __license__ 54 | print(__license__) 55 | raise SystemExit(0) 56 | return args 57 | 58 | 59 | def main(argv: None | list[str] | Namespace = None, /): 60 | if isinstance(argv, Namespace): 61 | args = argv 62 | else: 63 | args = parse_args(argv) 64 | 65 | import logging 66 | 67 | from p115client import P115Client 68 | from p115sftp import logger, P115RequestHandler 69 | 70 | if log_level := args.log_level: 71 | if log_level.isascii() and log_level.isdecimal(): 72 | log_level = int(log_level) 73 | else: 74 | log_level = getattr(logging, log_level.upper(), 0) 75 | if log_level: 76 | logger.setLevel(log_level) 77 | 78 | logging.basicConfig(level=log_level) 79 | if server_key := args.key_file: 80 | from paramiko import RSAKey 81 | server_key = RSAKey.from_private_key_file(server_key) 82 | 83 | cookies_path = Path(args.cookies_path or "115-cookies.txt") 84 | client = P115Client(cookies_path, check_for_relogin=args.check_for_relogin) 85 | P115RequestHandler.serve_forever( 86 | host=args.host, 87 | port=args.port, 88 | client=client, 89 | server_key=server_key, 90 | ) 91 | 92 | 93 | if __name__ == "__main__": 94 | from sys import path 95 | 96 | path[0] = str(Path(__file__).parents[1]) 97 | main() 98 | 99 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | 13 | import sys 14 | from pathlib import Path 15 | 16 | sys.path.insert(0, str(Path(__file__).parent.parent)) 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | author = "ChenyangGao" 21 | copyright = "2024-2025, ChenyangGao @ https://chenyanggao.github.io" 22 | project = "p115client" 23 | 24 | # -- General configuration --------------------------------------------------- 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be 27 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 28 | # ones. 29 | extensions = [ 30 | "myst_parser", 31 | "sphinx.ext.intersphinx", 32 | "sphinx.ext.autodoc", 33 | "sphinx.ext.napoleon", 34 | "sphinx.ext.todo", 35 | "sphinx.ext.viewcode", 36 | "sphinx.ext.autosectionlabel", 37 | # external 38 | "sphinx_copybutton", 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The language for content autogenerated by Sphinx. Refer to documentation 45 | # for a list of supported languages. 46 | # 47 | # This is also used if you do content translation via gettext catalogs. 48 | # Usually you set "language" from the command line for these cases. 49 | language = 'zh-CN' 50 | 51 | # List of patterns, relative to source directory, that match files and 52 | # directories to ignore when looking for source files. 53 | # This pattern also affects html_static_path and html_extra_path. 54 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 55 | 56 | todo_include_todos = True 57 | 58 | # -- Options for HTML output ------------------------------------------------- 59 | 60 | # The theme to use for HTML and HTML Help pages. See the documentation for 61 | # a list of builtin themes. 62 | # 63 | html_theme = 'furo' 64 | html_title = "p115client" 65 | language = "zh-CN" 66 | 67 | # Add any paths that contain custom static files (such as style sheets) here, 68 | # relative to this directory. They are copied after the builtin static files, 69 | # so a file named "default.css" will overwrite the builtin "default.css". 70 | html_static_path = ['_static'] 71 | 72 | intersphinx_mapping = { 73 | "python": ("https://docs.python.org/", None), 74 | } 75 | 76 | copybutton_prompt_text = ( 77 | r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " 78 | ) 79 | copybutton_prompt_is_regexp = True 80 | autoclass_content = "both" 81 | 82 | html_theme_options = { 83 | "footer_icons": [ 84 | { 85 | "name": "GitHub", 86 | "url": "https://github.com/ChenyangGao/p115client/", 87 | "html": """ 88 | 89 | 90 | 91 | """, 92 | "class": "", 93 | }, 94 | ], 95 | "source_repository": "https://github.com/ChenyangGao/p115client/", 96 | "source_branch": "main", 97 | "source_directory": "docs/", 98 | } 99 | -------------------------------------------------------------------------------- /modules/@p115captcha/tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | from __future__ import annotations 5 | 6 | __author__ = "ChenyangGao " 7 | __all__ = ["crack_captcha"] 8 | 9 | from collections import defaultdict 10 | from collections.abc import Callable 11 | from time import sleep, time 12 | from typing import cast 13 | 14 | from concurrenttools import thread_pool_batch 15 | from p115.component.client import P115Client 16 | 17 | 18 | CAPTCHA_CRACK: Callable[[bytes], str] 19 | 20 | 21 | def crack_captcha( 22 | client: str | P115Client, 23 | sample_count: int = 16, 24 | crack: None | Callable[[bytes], str] = None, 25 | ) -> bool: 26 | """破解 115 的图片验证码。如果返回 True,则说明破解成功,否则失败。如果失败,就不妨多运行这个函数几次。 27 | 28 | :param client: 115 客户端或 cookies 29 | :param sample_count: 单个文字的采样次数,共会执行 10 * sample_count 次识别 30 | :param crack: 破解验证码图片,输入图片的二进制数据,输出识别的字符串 31 | 32 | :return: 是否破解成功 33 | 34 | 你可以反复尝试,直到破解成功,代码如下 35 | 36 | while not crack_captcha(client): 37 | pass 38 | 39 | 如果你需要检测是否存在验证码,然后进行破解,代码如下 40 | 41 | resp = client.download_url_web("a") 42 | if not resp["state"] and resp["code"] == 911: 43 | print("出现验证码,尝试破解") 44 | while not crack_captcha(client): 45 | print("破解失败,再次尝试") 46 | """ 47 | global CAPTCHA_CRACK 48 | if crack is None: 49 | try: 50 | crack = CAPTCHA_CRACK 51 | except NameError: 52 | try: 53 | # https://pypi.org/project/ddddocr/ 54 | from ddddocr import DdddOcr 55 | except ImportError: 56 | from subprocess import run 57 | from sys import executable 58 | run([executable, "-m", "pip", "install", "-U", "ddddocr==1.4.11"], check=True) 59 | from ddddocr import DdddOcr # type: ignore 60 | crack = CAPTCHA_CRACK = cast(Callable[[bytes], str], DdddOcr(show_ad=False).classification) 61 | if not isinstance(client, P115Client): 62 | client = P115Client(client) 63 | while True: 64 | captcha = crack(client.captcha_code()) 65 | if len(captcha) == 4 and all("\u4E00" <= char <= "\u9FFF" for char in captcha): 66 | break 67 | ls: list[defaultdict[str, int]] = [defaultdict(int) for _ in range(10)] 68 | def crack_single(i, submit): 69 | try: 70 | char = crack(client.captcha_single(i)) 71 | if len(char) == 1 and "\u4E00" <= char <= "\u9FFF": 72 | ls[i][char] += 1 73 | else: 74 | submit(i) 75 | except: 76 | submit(i) 77 | thread_pool_batch(crack_single, (i for i in range(10) for _ in range(sample_count))) 78 | l: list[str] = [max(d, key=lambda k: d[k]) for d in ls] 79 | try: 80 | code = "".join(str(l.index(char)) for char in captcha) 81 | except ValueError: 82 | return False 83 | resp = client.captcha_verify(code) 84 | return resp["state"] 85 | 86 | 87 | # TODO: 实现一个函数,用来实现选择自定义的 request 88 | 89 | # TODO 删除文件,如果文件数过多,会尝试拆分后再执行任务,从回收站删除 90 | # TODO 会等待目录被删除完成,采取回收站清除它 91 | # def remove( 92 | # client: str | P115Client, 93 | # id: int, 94 | # /, 95 | # password: str = "", 96 | # ): 97 | # """删除文件或目录,如果提供密码,则会从回收站把它删除 98 | 99 | # :param client: 115 客户端或 cookies 100 | # :param id: 文件或目录的 id 101 | # :param password: 回收站密码(即 安全密钥,是 6 位数字) 102 | 103 | # :return: 返回一个 Future,可以被关停 104 | # """ 105 | # if not isinstance(client, P115Client): 106 | # client = P115Client(client) 107 | # fs = client.fs 108 | # try: 109 | # attr = fs.attr("/我的接收", ensure_dir=True) 110 | # except FileNotFoundError: 111 | # return 112 | # now = str(int(time())) 113 | # fs.rmtree(attr) 114 | # recyclebin = client.recyclebin 115 | # while True: 116 | # for i, info in enumerate(recyclebin): 117 | # # NOTE: 因为删除后,并不能立即在回收站看到被删除的文件夹,所以看情况需要等一等 118 | # if not i and info["dtime"] < now: 119 | # sleep(0.1) 120 | # break 121 | # if info["cid"] == 0 and info["file_name"] == "我的接收": 122 | # recyclebin.remove(info["id"], password) 123 | # return 124 | 125 | -------------------------------------------------------------------------------- /modules/p115wsgidav/p115wsgidav/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | # NOTE: https://patorjk.com/software/taag/#p=display&f=ANSI+Shadow&t=p115webdav 6 | __doc__ = """ 7 | 🕸️ Python 115 WsgiDAV 🕷️ 8 | 9 | ██████╗ ██╗ ██╗███████╗██╗ ██╗███████╗██████╗ ██████╗ █████╗ ██╗ ██╗ 10 | ██╔══██╗███║███║██╔════╝██║ ██║██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██║ 11 | ██████╔╝╚██║╚██║███████╗██║ █╗ ██║█████╗ ██████╔╝██║ ██║███████║██║ ██║ 12 | ██╔═══╝ ██║ ██║╚════██║██║███╗██║██╔══╝ ██╔══██╗██║ ██║██╔══██║╚██╗ ██╔╝ 13 | ██║ ██║ ██║███████║╚███╔███╔╝███████╗██████╔╝██████╔╝██║ ██║ ╚████╔╝ 14 | ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚══╝╚══╝ ╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═══╝ 15 | 16 | """ 17 | 18 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 19 | from pathlib import Path 20 | 21 | parser = ArgumentParser(formatter_class=RawTextHelpFormatter, description=__doc__) 22 | parser.add_argument("-H", "--host", help="ip 或 hostname,默认值:'0.0.0.0'") 23 | parser.add_argument("-P", "--port", type=int, help="端口号,默认值:8115") 24 | parser.add_argument("-cp", "--cookies-path", help="""\ 25 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 26 | 如果你需要直接传入一个 cookies 字符串,需要这样写 27 | 28 | .. code:: shell 29 | 30 | COOKIES='UID=...; CID=..., SEID=...' 31 | p115dav --cookies-path <(echo "$COOKIES") 32 | 33 | """) 34 | parser.add_argument("-cl", "--check-for-relogin", action="store_true", help="当风控时,自动重新扫码登录") 35 | parser.add_argument("-nt", "--no-thumbs", action="store_true", help="不要为请求图片链接提供缩略图") 36 | parser.add_argument("-o", "--origin-302", help="设置 302 请求转发。如果为空,则由此模块提供;特别的,如果缺省此参数,则视为缓存链接;如果为空字符串 '',则不缓存") 37 | parser.add_argument("-wc", "--wsgidav-config-path", help="""WsgiDAV 启动时的配置文件路径,支持 JSON、YAML 或 TOML 格式,会根据扩展名确定,不能确定时视为 JSON 38 | 如需样板文件,请阅读: 39 | 40 | https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html#sample-wsgidav-yaml 41 | 42 | """) 43 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 44 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 45 | 46 | 47 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 48 | args = parser.parse_args(argv) 49 | if args.version: 50 | from p115wsgidav import __version__ 51 | print(".".join(map(str, __version__))) 52 | raise SystemExit(0) 53 | elif args.license: 54 | from p115wsgidav import __license__ 55 | print(__license__) 56 | raise SystemExit(0) 57 | return args 58 | 59 | 60 | def main(argv: None | list[str] | Namespace = None, /): 61 | if isinstance(argv, Namespace): 62 | args = argv 63 | else: 64 | args = parse_args(argv) 65 | 66 | from p115client import P115Client 67 | from p115wsgidav import P115FileSystemProvider 68 | 69 | wsgidav_config_path = args.wsgidav_config_path 70 | if wsgidav_config_path: 71 | file = open(wsgidav_config_path, "rb") 72 | match Path(wsgidav_config_path).suffix.lower(): 73 | case ".yml" | ".yaml": 74 | from yaml import load as yaml_load, Loader 75 | run_config = yaml_load(file, Loader=Loader) 76 | case ".toml": 77 | from tomllib import load as toml_load 78 | run_config = toml_load(file) 79 | case _: 80 | from json import load 81 | run_config = load(file) 82 | else: 83 | run_config = {} 84 | if host := args.host: 85 | run_config["host"] = host 86 | else: 87 | run_config.setdefault("host", "0.0.0.0") 88 | if port := args.port: 89 | run_config["port"] = port 90 | else: 91 | run_config.setdefault(port, 8115) 92 | cookies_path = Path(args.cookies_path or "115-cookies.txt") 93 | origin_302 = args.origin_302 94 | if not origin_302: 95 | origin_302 = origin_302 is None 96 | provider = P115FileSystemProvider( 97 | P115Client(cookies_path, check_for_relogin=args.check_for_relogin), 98 | origin_302=origin_302, 99 | use_thumbs=not args.no_thumbs, 100 | check_for_relogin=args.check_for_relogin, 101 | ) 102 | provider.run_forever(run_config) 103 | 104 | 105 | if __name__ == "__main__": 106 | from sys import path 107 | 108 | path[0] = str(Path(__file__).parents[1]) 109 | main() 110 | 111 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/stellarplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/stellarplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115fuse/p115fuse/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | # NOTE: https://patorjk.com/software/taag/#p=display&f=Electronic&t=p115fuse 6 | __doc__ = """ 7 | 🕸️ Python 115 FUSE mount 🕷️ 8 | 9 | ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ 10 | ▐░░░░░░░░░░░▌ ▄█░░░░▌ ▄█░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ 11 | ▐░█▀▀▀▀▀▀▀█░▌▐░░▌▐░░▌ ▐░░▌▐░░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ 12 | ▐░▌ ▐░▌ ▀▀ ▐░░▌ ▀▀ ▐░░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ 13 | ▐░█▄▄▄▄▄▄▄█░▌ ▐░░▌ ▐░░▌ ▐░░░░░░░░░░░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ 14 | ▐░░░░░░░░░░░▌ ▐░░▌ ▐░░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ 15 | ▐░█▀▀▀▀▀▀▀▀▀ ▐░░▌ ▐░░▌ ▐░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ 16 | ▐░▌ ▐░░▌ ▐░░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ 17 | ▐░▌ ▄▄▄▄█░░█▄▄▄ ▄▄▄▄█░░█▄▄▄ ▄▄▄▄▄▄▄▄▄█░▌▐░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ 18 | ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ 19 | ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ 20 | 21 | """ 22 | 23 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 24 | from pathlib import Path 25 | 26 | parser = ArgumentParser(formatter_class=RawTextHelpFormatter, description=__doc__) 27 | parser.add_argument("mountpoint", nargs="?", help="挂载路径") 28 | parser.add_argument("-cp", "--cookies-path", help="""\ 29 | cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt 30 | 如果你需要直接传入一个 cookies 字符串,需要这样写 31 | 32 | .. code:: shell 33 | 34 | COOKIES='UID=...; CID=..., SEID=...' 35 | p115dav --cookies-path <(echo "$COOKIES") 36 | 37 | """) 38 | parser.add_argument("-cl", "--check-for-relogin", action="store_true", help="当风控时,自动重新扫码登录") 39 | parser.add_argument( 40 | "-fo", "--fuse-option", dest="fuse_options", metavar="option", nargs="+", 41 | help="""fuse 挂载选项,支持如下几种格式: 42 | - name 设置 name 选项 43 | - name= 取消 name 选项 44 | - name=value 设置 name 选项,值为 value 45 | 参考资料: 46 | - https://man7.org/linux/man-pages/man8/mount.fuse3.8.html 47 | - https://code.google.com/archive/p/macfuse/wikis/OPTIONS.wiki 48 | """) 49 | parser.add_argument("-ll", "--log-level", default="ERROR", help=f"指定日志级别,可以是数字或名称,不传此参数则不输出日志,默认值: 'ERROR'") 50 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 51 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 52 | 53 | 54 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 55 | args = parser.parse_args(argv) 56 | if args.version: 57 | from p115fuse import __version__ 58 | print(".".join(map(str, __version__))) 59 | raise SystemExit(0) 60 | elif args.license: 61 | from p115fuse import __license__ 62 | print(__license__) 63 | raise SystemExit(0) 64 | return args 65 | 66 | 67 | def main(argv: None | list[str] | Namespace = None, /): 68 | if isinstance(argv, Namespace): 69 | args = argv 70 | else: 71 | args = parse_args(argv) 72 | 73 | import logging 74 | 75 | from p115client import P115Client 76 | from p115fuse import P115FuseOperations, logger 77 | 78 | if log_level := args.log_level: 79 | if log_level.isascii() and log_level.isdecimal(): 80 | log_level = int(log_level) 81 | else: 82 | log_level = getattr(logging, log_level.upper(), 0) 83 | if log_level: 84 | logger.setLevel(log_level) 85 | 86 | options = { 87 | "mountpoint": args.mountpoint, 88 | "foreground": True, 89 | "max_readahead": 0, 90 | "noauto_cache": True, 91 | } 92 | if fuse_options := args.fuse_options: 93 | for option in fuse_options: 94 | if "=" in option: 95 | name, value = option.split("=", 1) 96 | if value: 97 | options[name] = value 98 | else: 99 | options.pop(name, None) 100 | else: 101 | options[option] = True 102 | P115FuseOperations(P115Client( 103 | Path(args.cookies_path or "115-cookies.txt"), 104 | check_for_relogin=args.check_for_relogin, 105 | )).run_forever(**options) 106 | 107 | 108 | if __name__ == "__main__": 109 | from sys import path 110 | 111 | path[0] = str(Path(__file__).parents[1]) 112 | main() 113 | 114 | -------------------------------------------------------------------------------- /modules/p115dav/p115dav/static/images/potplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/static/images/potplayer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/p115tinydav/p115tinydav/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __doc__ = """\ 6 | ╭───────────────────────── \x1b[31mWelcome to \x1b[1m115 tiny dav\x1b[0m ────────────────────────────╮ 7 | │ │ 8 | │ \x1b[1;35mmaintained by\x1b[0m \x1b[3;5;31m❤\x1b[0m \x1b[32mChenyangGao \x1b[4;34mhttps://chenyanggao.github.io\x1b[0m │ 9 | │ │ 10 | │ \x1b[32mGithub \x1b[4;34mhttps://github.com/ChenyangGao/p115client/\x1b[0m │ 11 | │ │ 12 | │ \x1b[32mlicense \x1b[4;34mhttps://www.gnu.org/licenses/gpl-3.0.txt\x1b[0m │ 13 | │ │ 14 | │ \x1b[32mversion \x1b[1;36m0.0.1\x1b[0m │ 15 | │ │ 16 | ╰──────────────────────────────────────────────────────────────────────────────╯ 17 | """ 18 | 19 | from argparse import ArgumentParser, Namespace, RawTextHelpFormatter 20 | 21 | parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter) 22 | parser.add_argument("-c", "--cookies", default="", help="cookies 字符串,优先级高于 -cp/--cookies-path") 23 | parser.add_argument("-cp", "--cookies-path", default="", help="cookies 文件保存路径,默认为当前工作目录下的 115-cookies.txt") 24 | parser.add_argument("-H", "--host", default="0.0.0.0", help="ip 或 hostname,默认值:'0.0.0.0'") 25 | parser.add_argument("-P", "--port", default=8000, type=int, help="端口号,默认值:8000,如果为 0 则自动确定") 26 | parser.add_argument("-cu", "--cache-url", action="store_true", help="缓存下载链接") 27 | parser.add_argument("-d", "--debug", action="store_true", help="启用调试,会输出更详细信息") 28 | parser.add_argument("-uc", "--uvicorn-run-config-path", help="uvicorn 启动时的配置文件路径,会作为关键字参数传给 `uvicorn.run`,支持 JSON、YAML 或 TOML 格式,会根据扩展名确定,不能确定时视为 JSON") 29 | parser.add_argument("-v", "--version", action="store_true", help="输出版本号") 30 | parser.add_argument("-l", "--license", action="store_true", help="输出授权信息") 31 | 32 | 33 | def parse_args(argv: None | list[str] = None, /) -> Namespace: 34 | args = parser.parse_args(argv) 35 | if args.version: 36 | from p115tinydav import __version__ 37 | print(".".join(map(str, __version__))) 38 | raise SystemExit(0) 39 | elif args.license: 40 | from p115tinydav import __license__ 41 | print(__license__) 42 | raise SystemExit(0) 43 | return args 44 | 45 | 46 | def main(argv: None | list[str] | Namespace = None, /): 47 | if isinstance(argv, Namespace): 48 | args = argv 49 | else: 50 | args = parse_args(argv) 51 | 52 | from p115client import P115Client 53 | 54 | if cookies := args.cookies.strip(): 55 | client = P115Client(cookies, check_for_relogin=True) 56 | else: 57 | from pathlib import Path 58 | client = P115Client(Path(args.cookies_path or "115-cookies.txt"), check_for_relogin=True) 59 | 60 | uvicorn_run_config_path = args.uvicorn_run_config_path 61 | if uvicorn_run_config_path: 62 | file = open(uvicorn_run_config_path, "rb") 63 | match suffix := Path(uvicorn_run_config_path).suffix.lower(): 64 | case ".yml" | ".yaml": 65 | from yaml import load as yaml_load, Loader 66 | run_config = yaml_load(file, Loader=Loader) 67 | case ".toml": 68 | from tomllib import load as toml_load 69 | run_config = toml_load(file) 70 | case _: 71 | from orjson import loads as json_loads 72 | run_config = json_loads(file.read()) 73 | else: 74 | run_config = {} 75 | 76 | if args.host: 77 | run_config["host"] = args.host 78 | else: 79 | run_config.setdefault("host", "0.0.0.0") 80 | if args.port: 81 | run_config["port"] = args.port 82 | elif not run_config.get("port"): 83 | from socket import create_connection 84 | 85 | def get_available_ip(start: int = 1024, stop: int = 65536) -> int: 86 | for port in range(start, stop): 87 | try: 88 | with create_connection(("127.0.0.1", port), timeout=1): 89 | pass 90 | except OSError: 91 | return port 92 | raise RuntimeError("no available ports") 93 | 94 | run_config["port"] = get_available_ip() 95 | 96 | run_config.setdefault("proxy_headers", True) 97 | run_config.setdefault("server_header", False) 98 | run_config.setdefault("forwarded_allow_ips", "*") 99 | run_config.setdefault("timeout_graceful_shutdown", 1) 100 | run_config.setdefault("access_log", False) 101 | 102 | from p115tinydav.app import make_application 103 | from uvicorn import run 104 | 105 | print(__doc__) 106 | app = make_application( 107 | client, 108 | debug=args.debug, 109 | cache_url=args.cache_url, 110 | ) 111 | run(app, **run_config) 112 | 113 | 114 | if __name__ == "__main__": 115 | from pathlib import Path 116 | from sys import path 117 | 118 | path[0] = str(Path(__file__).parents[1]) 119 | main() 120 | 121 | -------------------------------------------------------------------------------- /modules/p115fuse/p115fuse/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __author__ = "ChenyangGao " 5 | __version__ = (0, 0, 3) 6 | __license__ = "GPLv3 " 7 | __all__ = ["P115FuseOperations"] 8 | 9 | import errno 10 | import logging 11 | 12 | from collections.abc import Callable, Mapping 13 | from itertools import count 14 | from os import PathLike 15 | from os.path import exists 16 | from pathlib import Path 17 | from posixpath import split as splitpath 18 | from shutil import rmtree 19 | from stat import S_IFDIR, S_IFREG 20 | from typing import Any 21 | from uuid import uuid4 22 | 23 | from cachedict import TTLDict 24 | from mfusepy import FUSE, Operations # type: ignore 25 | from orjson import dumps 26 | from p115client import P115Client 27 | from richlog_fs import access_log, get_logger 28 | 29 | 30 | logger = get_logger("p115fuse") 31 | log = access_log(logger=logger, level=None) 32 | 33 | 34 | def attr_to_stat(attr: Mapping, /) -> dict: 35 | return { 36 | "st_mode": (S_IFDIR if attr["is_dir"] else S_IFREG) | 0o777, 37 | "st_ino": attr["id"], 38 | "st_dev": 0, 39 | "st_nlink": 1, 40 | "st_uid": 0, 41 | "st_gid": 0, 42 | "st_size": attr.get("size", 0), 43 | "st_atime": attr.get("atime") or attr.get("mtime", 0), 44 | "st_mtime": attr.get("mtime", 0), 45 | "st_ctime": attr.get("ctime", 0), 46 | "xattr": attr, 47 | } 48 | 49 | 50 | class P115FuseOperations(Operations): 51 | 52 | def __init__( 53 | self, 54 | /, 55 | client: str | PathLike | P115Client = Path("~/115-cookies.txt").expanduser(), 56 | readdir_ttl: float = 60, 57 | ): 58 | if not isinstance(client, P115Client): 59 | client = P115Client(client, check_for_relogin=True) 60 | self.client = client 61 | self.fs = client.get_fs(id_to_readdir=TTLDict(readdir_ttl)) 62 | self._opened: dict[int, Any] = {} 63 | self._get_id: Callable[[], int] = count(1).__next__ 64 | 65 | @log 66 | def getattr(self, /, path: str, fh: int = 0) -> dict[str, Any]: 67 | return attr_to_stat(self.fs.get_attr(path)) 68 | 69 | @log 70 | def getxattr(self, /, path: str, name: str, position: int = 0) -> bytes: 71 | attr = self.getattr(path)["xattr"] 72 | if name in attr: 73 | return dumps(attr[name]) 74 | return b"" 75 | 76 | @log 77 | def listxattr(self, /, path: str) -> list[str]: 78 | attr = self.getattr(path)["xattr"] 79 | return list(attr) 80 | 81 | @log 82 | def mkdir(self, /, path: str, mode: int = 0) -> int: 83 | dir_, name = splitpath(path) 84 | self.fs.mkdir(dir_, name) 85 | return 0 86 | 87 | @log 88 | def open(self, /, path: str, flags: int) -> int: 89 | file = self.fs.open(path) 90 | fh = self._get_id() 91 | self._opened[fh] = file 92 | return fh 93 | 94 | @log 95 | def opendir(self, /, path: str) -> int: 96 | return 0 97 | 98 | @log 99 | def read(self, /, path: str, size: int, offset: int, fh: int) -> bytes: 100 | file = self._opened[fh] 101 | file.seek(offset) 102 | return file.read(size) 103 | 104 | @log 105 | def readdir(self, /, path: str, fh: int = 0) -> list[str]: 106 | children = self.fs.readdir(path) 107 | return [".", "..", *(a["name"] for a in children)] 108 | 109 | @log 110 | def release(self, /, path: str, fh: int) -> int: 111 | if file := self._opened.pop(fh, None): 112 | file.close() 113 | return 0 114 | 115 | @log 116 | def releasedir(self, /, path: str, fh: int) -> int: 117 | return 0 118 | 119 | @log 120 | def rename(self, /, src: str, dst: str) -> int: 121 | if src != dst: 122 | src_dir, src_name = splitpath(src) 123 | dst_dir, dst_name = splitpath(dst) 124 | attr = self.fs.get_attr(src) 125 | if src_dir != dst_dir: 126 | if dst_dir == "/": 127 | cid = 0 128 | else: 129 | dstdir_attr = self.fs.get_attr(dst_dir) 130 | if not dstdir_attr["is_dir"]: 131 | raise NotADirectoryError(errno.ENOTDIR, dst_dir) 132 | cid = dstdir_attr["id"] 133 | self.fs.move(attr, cid) 134 | if src_name != dst_name: 135 | self.fs.rename(attr, dst_name) 136 | return 0 137 | 138 | @log 139 | def unlink(self, /, path: str) -> int: 140 | self.fs.remove(path) 141 | return 0 142 | 143 | @log 144 | def rmdir(self, /, path: str) -> int: 145 | self.fs.remove(path) 146 | return 0 147 | 148 | def run_forever(self, /, mountpoint: None | str = None, **options): 149 | if not mountpoint: 150 | mountpoint = str(uuid4()) 151 | will_remove_mountpoint = not exists(mountpoint) 152 | try: 153 | print(f"🏠 mountpoint: \x1b[4;34m{mountpoint!r}\x1b[0m") 154 | print(f"🔨 options: {options}") 155 | return FUSE(self, mountpoint, **options) 156 | finally: 157 | if will_remove_mountpoint: 158 | rmtree(mountpoint) 159 | 160 | 161 | if __name__ == "__main__": 162 | logger.setLevel(logging.DEBUG) 163 | P115FuseOperations().run_forever( 164 | foreground=True, 165 | max_readahead=0, 166 | noauto_cache=True, 167 | ) 168 | 169 | -------------------------------------------------------------------------------- /p115client/exception.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # encoding: utf-8 3 | 4 | __all__ = [ 5 | "P115Error", "P115Warning", "P115OSError", "P115AccessError", 6 | "P115AuthenticationError", "P115BusyOSError", "P115DataError", 7 | "P115OperationalError", "P115FileTooBig", "P115ExceededError", 8 | "P115InvalidArgumentError", "P115NoSpaceError", "P115NotSupportedError", 9 | "P115LoginError", "P115AccessTokenError", "P115OpenAppAuthLimitExceeded", 10 | "error", "throw", "errno2error" 11 | ] 12 | 13 | import warnings 14 | 15 | from itertools import count 16 | from collections.abc import Mapping 17 | from functools import cached_property 18 | from typing import Never 19 | 20 | from errno2 import errno, errno2error as _errno2error 21 | 22 | 23 | warnings.filterwarnings("always", category=UserWarning) 24 | setattr(warnings, "formatwarning", lambda message, category, filename, lineno, line=None, _getid=count(1).__next__: 25 | f"\r\x1b[K\x1b[1;31;43m{category.__qualname__}\x1b[0m(\x1b[32m{_getid()}\x1b[0m) @ \x1b[3;4;34m{filename}\x1b[0m:\x1b[36m{lineno}\x1b[0m \x1b[5;31m➜\x1b[0m \x1b[1m{message}\x1b[0m\n" 26 | ) 27 | 28 | 29 | class P115Error(Exception): 30 | """本模块的最基础异常类 31 | """ 32 | 33 | 34 | class P115Warning(P115Error, UserWarning): 35 | """本模块的最基础警示类 36 | """ 37 | 38 | 39 | class P115OSError(P115Error, OSError): 40 | """本模块的最基础输出输出异常类 41 | """ 42 | def __getattr__(self, attr, /): 43 | try: 44 | return self[attr] 45 | except KeyError as e: 46 | raise AttributeError(attr) from e 47 | 48 | def __getitem__(self, key, /): 49 | message = self.message 50 | if isinstance(message, Mapping): 51 | return message[key] 52 | raise KeyError(key) 53 | 54 | @cached_property 55 | def message(self, /): 56 | if args := self.args: 57 | if len(args) >= 2 and isinstance(args[0], int): 58 | return args[1] 59 | return args[0] 60 | 61 | 62 | class P115AuthenticationError(P115OSError): 63 | """当登录状态无效时抛出 64 | """ 65 | 66 | 67 | class P115BusyOSError(P115OSError): 68 | """当操作繁忙时抛出(115 网盘的复制、移动、删除、还原只允许最多一个操作进行中) 69 | """ 70 | 71 | 72 | class P115DataError(P115OSError): 73 | """当响应数据解析失败时抛出 74 | """ 75 | 76 | 77 | class P115OperationalError(P115OSError): 78 | """当接口使用方法错误时抛出,例如参数错误、空间不足、超出允许数量范围等 79 | """ 80 | 81 | 82 | class P115AccessError(P115OperationalError, PermissionError): 83 | """当不可访问时抛出,可能是文件被和谐了 84 | """ 85 | 86 | 87 | class P115FileTooBig(P115OperationalError, PermissionError): 88 | """文件过大 89 | """ 90 | 91 | 92 | class P115ExceededError(P115OperationalError, PermissionError): 93 | """超出允许数量范围 94 | """ 95 | 96 | 97 | class P115InvalidArgumentError(P115OperationalError, ValueError): 98 | """参数错误 99 | """ 100 | 101 | 102 | class P115NoSpaceError(P115OperationalError, PermissionError): 103 | """空间不足 104 | """ 105 | 106 | 107 | class P115NotSupportedError(P115OperationalError): 108 | """当调用不存在的接口或者接口不支持此操作时抛出 109 | """ 110 | 111 | 112 | class P115LoginError(P115AuthenticationError): 113 | """当登录失败时抛出 114 | """ 115 | 116 | 117 | class P115AccessTokenError(P115OSError, ValueError): 118 | """access_token 错误或者无效 119 | """ 120 | 121 | 122 | class P115OpenAppAuthLimitExceeded(P115AuthenticationError): 123 | """当授权应用数达到上限时抛出 124 | """ 125 | 126 | 127 | def error(*args, **kwds) -> BaseException: 128 | """构建异常 129 | 130 | .. tip:: 131 | 会根据传入的位置参数,做一些类型推断 132 | 133 | - 第 1 个位置参数,记作 `errcode`,大概是一个 `errno2.errno` 的枚举类型,不能成功推断则用 `errno2.errno.EIO` 134 | - 第 2 个位置参数(若第 1 个位置参数不满足上一条,则用此参数),记作 `exctype`,大概是一个 `P115Error` 类型或其子类型,不能成功推断则用 `P115OSError` 135 | 136 | 假设剩余的所有没被提取的位置参数记作 `rargs`,最终构建的异常为 `exctype(errcode, *rargs, **kwds)` 137 | """ 138 | if args and isinstance(args[0], errno): 139 | errcode = args[0] 140 | args = args[1:] 141 | else: 142 | errcode = errno.EIO 143 | if not (args and isinstance(args[0], type) and issubclass(args[0], BaseException)): 144 | args = errno2error.get(errcode, P115OSError), *args 145 | return errcode.error(*args, **kwds) 146 | 147 | 148 | def throw(*args, **kwds) -> Never: 149 | """抛出异常 150 | 151 | .. tip:: 152 | 会根据传入的位置参数,做一些类型推断 153 | 154 | - 第 1 个位置参数,记作 `errcode`,大概是一个 `errno2.errno` 的枚举类型,不能成功推断则用 `errno2.errno.EIO` 155 | - 第 2 个位置参数(若第 1 个位置参数不满足上一条,则用此参数),记作 `exctype`,大概是一个 `P115Error` 类型或其子类型,不能成功推断则用 `P115OSError` 156 | 157 | 假设剩余的所有没被提取的位置参数记作 `rargs`,最终抛出的异常为 `exctype(errcode, *rargs, **kwds)` 158 | """ 159 | raise error(*args, **kwds) 160 | 161 | 162 | #: errno 到的异常类的映射 163 | errno2error: dict[errno, type[P115Error]] = { 164 | errno.EAUTH: P115AuthenticationError, 165 | errno.EBUSY: P115BusyOSError, 166 | errno.ENODATA: P115DataError, 167 | errno.EACCES: P115AccessError, 168 | errno.EFBIG: P115FileTooBig, 169 | errno.ERANGE: P115ExceededError, 170 | errno.EINVAL: P115InvalidArgumentError, 171 | errno.ENOSPC: P115NoSpaceError, 172 | errno.ENOTSUP: P115NotSupportedError, 173 | errno.ENOSYS: P115NotSupportedError, 174 | } 175 | 176 | _modns = globals() 177 | for errcode, exctype in _errno2error.items(): 178 | typename = "P115" + exctype.__name__ 179 | if typename not in _modns: 180 | cls = _modns[typename] = type(typename, (P115OSError, exctype), {"__doc__": errcode.description}) 181 | __all__.append(typename) 182 | errno2error[errcode] = cls 183 | 184 | 185 | def __getattr__(attr: str, /) -> type[P115OSError]: 186 | raise AttributeError(attr) 187 | 188 | -------------------------------------------------------------------------------- /modules/p115servedb/p115servedb/views/list.jinja: -------------------------------------------------------------------------------- 1 | {%- extends "base.jinja" %} 2 | {%- block content %} 3 | {%- for ancestor in ancestors[:-1] %}{{ ancestor["name"] | escape_name }}{{ ancestor["name"] and "/" }}{% endfor %}{{ ancestors[-1]["name"] | escape_name }} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {%- if ancestors.__len__() > 1 %} 18 | 19 | {%- endif %} 20 | 21 | {%- set platform = (user_agent.get("platform", {}).get("name") or "").lower() %} 22 | {%- for attr in children %} 23 | 24 | {%- set name = attr["name"] %} 25 | {%- set url = origin + attr["url"] %} 26 | 27 | 28 | 56 | {%- if attr["is_dir"] %} 57 | 58 | {%- else %} 59 | 60 | {%- endif %} 61 | 62 | 63 | 64 | {%- endfor %} 65 | 66 |
NameOpenSizeAttrLast Modified
..
{{ name }} 29 | {%- if attr["type"] == 4 %} 30 | Artplayer 31 | {%- endif %} 32 | {%- if attr["type"] in (3, 4) %} 33 | plyr 34 | IINA 35 | PotPlayer 36 | {%- if platform == "ios" %} 37 | VLC 38 | {%- else %} 39 | VLC 40 | {%- endif %} 41 | Fileball 42 | MX Player 43 | MX Player Pro 44 | infuse 45 | {%- if platform == "mac os" %} 46 | nPlayer 47 | {%- else %} 48 | nPlayer 49 | {%- endif %} 50 | OmniPlayer 51 | Fig Player 52 | MPV 53 | 恒星播放器 54 | {%- endif %} 55 | --{{ attr["size"] | format_size }}{{ attr["size"] }}attr{{ attr["mtime"] | format_timestamp }}
67 | {%- endblock %} --------------------------------------------------------------------------------