├── app.ico ├── core.rar ├── old ├── grpc_csharp_ext.x64.dll ├── config.yaml ├── core.py.bak └── app.py ├── bulid.bat ├── grpc_core ├── build.bat ├── taskstatus_pb2_grpc.py ├── type │ ├── api_pb2_grpc.py │ ├── taskdo_pb2_grpc.py │ ├── video_pb2_grpc.py │ ├── blinkresult_pb2_grpc.py │ ├── servericon_pb2_grpc.py │ ├── taskstatus_pb2_grpc.py │ ├── updatestatus_pb2_grpc.py │ ├── api.proto │ ├── updatestatus.proto │ ├── video.proto │ ├── blinkresult.proto │ ├── taskdo.proto │ ├── api_pb2.pyi │ ├── taskstatus.proto │ ├── blinkresult_pb2.pyi │ ├── video_pb2.pyi │ ├── updatestatus_pb2.pyi │ ├── taskdo_pb2.pyi │ ├── taskstatus_pb2.pyi │ ├── api_pb2.py │ ├── video_pb2.py │ ├── blinkresult_pb2.py │ ├── taskdo_pb2.py │ ├── updatestatus_pb2.py │ ├── servericon.proto │ ├── taskstatus_pb2.py │ ├── servericon_pb2.py │ └── servericon_pb2.pyi ├── taskstatus.proto ├── status.proto ├── taskstatus_pb2.pyi ├── status_pb2.pyi ├── taskstatus_pb2.py ├── user.proto ├── status_pb2.py ├── bvideo.proto ├── user_pb2.pyi ├── task.proto ├── user_pb2.py ├── status_pb2_grpc.py ├── bvideo_pb2.py ├── task_pb2.py ├── bvideo_pb2_grpc.py ├── bvideo_pb2.pyi ├── task_pb2.pyi ├── user_pb2_grpc.py └── task_pb2_grpc.py ├── .gitignore ├── Pipfile ├── grpc.code-workspace ├── README.md ├── test.py ├── Pipfile.lock └── app.py /app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiJiDown/jithon/HEAD/app.ico -------------------------------------------------------------------------------- /core.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiJiDown/jithon/HEAD/core.rar -------------------------------------------------------------------------------- /old/grpc_csharp_ext.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiJiDown/jithon/HEAD/old/grpc_csharp_ext.x64.dll -------------------------------------------------------------------------------- /bulid.bat: -------------------------------------------------------------------------------- 1 | pyi-makespec -F -i f:\code\jithon\grpc_ver\app.exe --hidden-import plyer.platforms.win.notification app.py -------------------------------------------------------------------------------- /grpc_core/build.bat: -------------------------------------------------------------------------------- 1 | python -m grpc_tools.protoc --python_out=. --grpc_python_out=. --pyi_out=. -I. taskstatus.proto -------------------------------------------------------------------------------- /grpc_core/taskstatus_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/api_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/taskdo_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/video_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/blinkresult_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/servericon_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/taskstatus_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /grpc_core/type/updatestatus_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | temp 2 | .vscode 3 | resources/ 4 | __pycache__/ 5 | app.spec 6 | build/ 7 | dist/ 8 | hugo_0.109.0_windows-amd64/ 9 | wiki.md 10 | set.json 11 | core/ 12 | grpc_ver/grpc.code-workspace 13 | .exe 14 | .dll 15 | .png 16 | .yaml 17 | *.obj 18 | *.log 19 | *.png 20 | grpc_ver/test.py 21 | grpc_ver/Pipfile.lock 22 | grpc_ver/Pipfile 23 | *.ico 24 | 25 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "*" 8 | pywebio = "*" 9 | grpcio = "*" 10 | grpcio-tools = "*" 11 | tqdm = "*" 12 | pyuac = "*" 13 | loguru = "*" 14 | pyinstaller = "*" 15 | pywin32 = "*" 16 | plyer = "*" 17 | 18 | [dev-packages] 19 | 20 | [requires] 21 | python_version = "3.11" 22 | -------------------------------------------------------------------------------- /grpc_core/type/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/apipb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // API 接口 9 | enum ApiType { 10 | // WEB 接口 11 | ApiType_WEB = 0; 12 | // TV 接口 13 | ApiType_TV = 1; 14 | // APP 接口 15 | ApiType_APP = 2; 16 | } -------------------------------------------------------------------------------- /grpc.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "../../论坛" 5 | }, 6 | { 7 | "path": ".." 8 | }, 9 | { 10 | "path": "../../Jitwin2.0" 11 | }, 12 | { 13 | "name": "Lofter-Saver", 14 | "path": "../../Lofter-Saver" 15 | }, 16 | { 17 | "path": "../../bw" 18 | }, 19 | { 20 | "path": "../../auto" 21 | }, 22 | { 23 | "name": "grpc", 24 | "path": "../../grpc" 25 | } 26 | ], 27 | "settings": {} 28 | } -------------------------------------------------------------------------------- /grpc_core/type/updatestatus.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/updatestatuspb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // 检查更新状态 9 | enum UpdateStatusType { 10 | CHECKING = 0; // 检查中 / 下载更新 11 | NOTSUPPORTUPDATE = 1; // 不支持自动更新 12 | UPTODATE = 2; // 已经是最新版本 13 | NEEDUPDATE = 3; // 需要更新 14 | } -------------------------------------------------------------------------------- /grpc_core/type/video.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/videopb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // 视频/音频编码 9 | enum VideoType { 10 | // 未知 11 | VideoType_UNKNOWN = 0; 12 | // AVC (H.264) 编码 13 | VideoType_AVC = 1; 14 | // HEVC (H.265) 编码 15 | VideoType_HEVC = 2; 16 | // AV1 编码 17 | VideoType_AV1 = 3; 18 | } -------------------------------------------------------------------------------- /grpc_core/type/blinkresult.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/blinkresultpb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // B站 URL 检测结果 9 | message BLinkResult { 10 | // 解析出来的 ID 数据 (AV号, ep号, up号) 11 | int64 id = 1; 12 | // 短标记 (av10086, ep317925, up116683) 13 | string mark = 2; 14 | // 前往 B站 原始链接 15 | string web_link = 3; 16 | } -------------------------------------------------------------------------------- /grpc_core/type/taskdo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskdopb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // 任务操作 9 | enum TaskDo { 10 | TaskDo_NOTHING = 0; // 什么也不做 11 | TaskDo_PAUSE = 1; // 暂停 12 | TaskDo_RESUME = 2; // 继续 13 | TaskDo_DELETE = 3; // 仅删除任务 14 | TaskDo_DELETE_AND_FILE = 4; // 删除任务和本地文件 15 | } -------------------------------------------------------------------------------- /grpc_core/type/api_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class ApiType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | ApiType_WEB: _ClassVar[ApiType] 10 | ApiType_TV: _ClassVar[ApiType] 11 | ApiType_APP: _ClassVar[ApiType] 12 | ApiType_WEB: ApiType 13 | ApiType_TV: ApiType 14 | ApiType_APP: ApiType 15 | -------------------------------------------------------------------------------- /grpc_core/taskstatus.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskstatuspb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // 任务状态 9 | enum TaskStatusType { 10 | TaskStatusType_TASK_ALL = 0; // 所有任务 11 | TaskStatusType_TASK_ERROR = 1; // 任务错误 12 | TaskStatusType_TASK_STOP = 2; // 任务停止 13 | TaskStatusType_TASK_WAIT = 3; // 任务等待 14 | TaskStatusType_TASK_RUNNING = 4; // 任务运行 15 | TaskStatusType_TASK_COMPLETE = 5; // 任务完成 16 | } 17 | -------------------------------------------------------------------------------- /grpc_core/type/taskstatus.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskstatuspb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // 任务状态 9 | enum TaskStatusType { 10 | TaskStatusType_TASK_ERROR = 0; // 任务错误 11 | TaskStatusType_TASK_PAUSE = 1; // 任务暂停 12 | TaskStatusType_TASK_RUNNING = 2; // 任务运行 13 | TaskStatusType_TASK_WAIT = 3; // 任务等待 14 | TaskStatusType_TASK_MERGEING = 4; // 任务正在进行合并 15 | TaskStatusType_TASK_GENMUSIC = 5; // 任务正在提取MP3 16 | TaskStatusType_TASK_COMPLETE = 6; // 任务完成 17 | } 18 | -------------------------------------------------------------------------------- /grpc_core/type/blinkresult_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf import descriptor as _descriptor 2 | from google.protobuf import message as _message 3 | from typing import ClassVar as _ClassVar, Optional as _Optional 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class BLinkResult(_message.Message): 8 | __slots__ = ["id", "mark", "web_link"] 9 | ID_FIELD_NUMBER: _ClassVar[int] 10 | MARK_FIELD_NUMBER: _ClassVar[int] 11 | WEB_LINK_FIELD_NUMBER: _ClassVar[int] 12 | id: int 13 | mark: str 14 | web_link: str 15 | def __init__(self, id: _Optional[int] = ..., mark: _Optional[str] = ..., web_link: _Optional[str] = ...) -> None: ... 16 | -------------------------------------------------------------------------------- /grpc_core/type/video_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class VideoType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | VideoType_UNKNOWN: _ClassVar[VideoType] 10 | VideoType_AVC: _ClassVar[VideoType] 11 | VideoType_HEVC: _ClassVar[VideoType] 12 | VideoType_AV1: _ClassVar[VideoType] 13 | VideoType_UNKNOWN: VideoType 14 | VideoType_AVC: VideoType 15 | VideoType_HEVC: VideoType 16 | VideoType_AV1: VideoType 17 | -------------------------------------------------------------------------------- /grpc_core/type/updatestatus_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class UpdateStatusType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | CHECKING: _ClassVar[UpdateStatusType] 10 | NOTSUPPORTUPDATE: _ClassVar[UpdateStatusType] 11 | UPTODATE: _ClassVar[UpdateStatusType] 12 | NEEDUPDATE: _ClassVar[UpdateStatusType] 13 | CHECKING: UpdateStatusType 14 | NOTSUPPORTUPDATE: UpdateStatusType 15 | UPTODATE: UpdateStatusType 16 | NEEDUPDATE: UpdateStatusType 17 | -------------------------------------------------------------------------------- /grpc_core/type/taskdo_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class TaskDo(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | TaskDo_NOTHING: _ClassVar[TaskDo] 10 | TaskDo_PAUSE: _ClassVar[TaskDo] 11 | TaskDo_RESUME: _ClassVar[TaskDo] 12 | TaskDo_DELETE: _ClassVar[TaskDo] 13 | TaskDo_DELETE_AND_FILE: _ClassVar[TaskDo] 14 | TaskDo_NOTHING: TaskDo 15 | TaskDo_PAUSE: TaskDo 16 | TaskDo_RESUME: TaskDo 17 | TaskDo_DELETE: TaskDo 18 | TaskDo_DELETE_AND_FILE: TaskDo 19 | -------------------------------------------------------------------------------- /old/config.yaml: -------------------------------------------------------------------------------- 1 | portable: false 2 | log-level: debug 3 | external-controller: 127.0.0.1:64000 4 | external-ui: "" 5 | secret: "" 6 | user-info: 7 | raw-access-token: "" 8 | raw-cookies: "" 9 | hide-nickname: false 10 | download-task: 11 | temp-dir: "" 12 | download-dir: G:\JiJiDown 13 | ffmpeg-path: "" 14 | max-task: 2 15 | download-speed-limit: 1 16 | disable-mcdn: false 17 | jdm: 18 | max-retry: 0 19 | retry-wait: 0 20 | jdm-task-workers: 0 21 | session-workers: 0 22 | min-split-size: 0 23 | proxy-addr: "" 24 | check-best-mirror: false 25 | cache-in-ram: false 26 | cache-in-ram-limit: 0 27 | insecure-skip-verify: false 28 | certs-file-path: "" 29 | -------------------------------------------------------------------------------- /grpc_core/status.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown"; 6 | option csharp_namespace = "JiJiDown.Core.SDK"; 7 | 8 | import "google/protobuf/empty.proto"; 9 | import "type/servericon.proto"; 10 | import "type/updatestatus.proto"; 11 | 12 | // Status 13 | service Status { 14 | // 检查服务器是否在线 15 | rpc Ping(google.protobuf.Empty) returns (StatusPingPong); 16 | // 检查更新 17 | rpc CheckUpdate(google.protobuf.Empty) returns (stream StatusCheckUpdateReply); 18 | } 19 | 20 | // 检查服务器是否在线-回复 21 | message StatusPingPong { 22 | // 服务器名称 23 | string server_name = 1; 24 | // 操作系统 logo 25 | ServerIconType os_icon = 2; 26 | // 操作系统名称 27 | string os_system_name = 3; 28 | } 29 | 30 | // 检查更新-回复 31 | message StatusCheckUpdateReply { 32 | // 更新状态 33 | UpdateStatusType status = 1; 34 | // Changelog 35 | string change_log = 2; 36 | } -------------------------------------------------------------------------------- /grpc_core/taskstatus_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class TaskStatusType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | TaskStatusType_TASK_ALL: _ClassVar[TaskStatusType] 10 | TaskStatusType_TASK_ERROR: _ClassVar[TaskStatusType] 11 | TaskStatusType_TASK_STOP: _ClassVar[TaskStatusType] 12 | TaskStatusType_TASK_WAIT: _ClassVar[TaskStatusType] 13 | TaskStatusType_TASK_RUNNING: _ClassVar[TaskStatusType] 14 | TaskStatusType_TASK_COMPLETE: _ClassVar[TaskStatusType] 15 | TaskStatusType_TASK_ALL: TaskStatusType 16 | TaskStatusType_TASK_ERROR: TaskStatusType 17 | TaskStatusType_TASK_STOP: TaskStatusType 18 | TaskStatusType_TASK_WAIT: TaskStatusType 19 | TaskStatusType_TASK_RUNNING: TaskStatusType 20 | TaskStatusType_TASK_COMPLETE: TaskStatusType 21 | -------------------------------------------------------------------------------- /grpc_core/type/taskstatus_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class TaskStatusType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | TaskStatusType_TASK_ERROR: _ClassVar[TaskStatusType] 10 | TaskStatusType_TASK_PAUSE: _ClassVar[TaskStatusType] 11 | TaskStatusType_TASK_RUNNING: _ClassVar[TaskStatusType] 12 | TaskStatusType_TASK_WAIT: _ClassVar[TaskStatusType] 13 | TaskStatusType_TASK_MERGEING: _ClassVar[TaskStatusType] 14 | TaskStatusType_TASK_GENMUSIC: _ClassVar[TaskStatusType] 15 | TaskStatusType_TASK_COMPLETE: _ClassVar[TaskStatusType] 16 | TaskStatusType_TASK_ERROR: TaskStatusType 17 | TaskStatusType_TASK_PAUSE: TaskStatusType 18 | TaskStatusType_TASK_RUNNING: TaskStatusType 19 | TaskStatusType_TASK_WAIT: TaskStatusType 20 | TaskStatusType_TASK_MERGEING: TaskStatusType 21 | TaskStatusType_TASK_GENMUSIC: TaskStatusType 22 | TaskStatusType_TASK_COMPLETE: TaskStatusType 23 | -------------------------------------------------------------------------------- /grpc_core/status_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf import empty_pb2 as _empty_pb2 2 | from type import servericon_pb2 as _servericon_pb2 3 | from type import updatestatus_pb2 as _updatestatus_pb2 4 | from google.protobuf import descriptor as _descriptor 5 | from google.protobuf import message as _message 6 | from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union 7 | 8 | DESCRIPTOR: _descriptor.FileDescriptor 9 | 10 | class StatusPingPong(_message.Message): 11 | __slots__ = ["server_name", "os_icon", "os_system_name"] 12 | SERVER_NAME_FIELD_NUMBER: _ClassVar[int] 13 | OS_ICON_FIELD_NUMBER: _ClassVar[int] 14 | OS_SYSTEM_NAME_FIELD_NUMBER: _ClassVar[int] 15 | server_name: str 16 | os_icon: _servericon_pb2.ServerIconType 17 | os_system_name: str 18 | def __init__(self, server_name: _Optional[str] = ..., os_icon: _Optional[_Union[_servericon_pb2.ServerIconType, str]] = ..., os_system_name: _Optional[str] = ...) -> None: ... 19 | 20 | class StatusCheckUpdateReply(_message.Message): 21 | __slots__ = ["status", "change_log"] 22 | STATUS_FIELD_NUMBER: _ClassVar[int] 23 | CHANGE_LOG_FIELD_NUMBER: _ClassVar[int] 24 | status: _updatestatus_pb2.UpdateStatusType 25 | change_log: str 26 | def __init__(self, status: _Optional[_Union[_updatestatus_pb2.UpdateStatusType, str]] = ..., change_log: _Optional[str] = ...) -> None: ... 27 | -------------------------------------------------------------------------------- /grpc_core/type/api_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/api.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0etype/api.proto\x12\rjijidown.core*;\n\x07\x41piType\x12\x0f\n\x0b\x41piType_WEB\x10\x00\x12\x0e\n\nApiType_TV\x10\x01\x12\x0f\n\x0b\x41piType_APP\x10\x02\x42[Z?github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/apipb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.api_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'Z?github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/apipb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_APITYPE']._serialized_start=33 26 | _globals['_APITYPE']._serialized_end=92 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/type/video_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/video.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10type/video.proto\x12\rjijidown.core*\\\n\tVideoType\x12\x15\n\x11VideoType_UNKNOWN\x10\x00\x12\x11\n\rVideoType_AVC\x10\x01\x12\x12\n\x0eVideoType_HEVC\x10\x02\x12\x11\n\rVideoType_AV1\x10\x03\x42]ZAgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/videopb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.video_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZAgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/videopb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_VIDEOTYPE']._serialized_start=35 26 | _globals['_VIDEOTYPE']._serialized_end=127 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/type/blinkresult_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/blinkresult.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16type/blinkresult.proto\x12\rjijidown.core\"9\n\x0b\x42LinkResult\x12\n\n\x02id\x18\x01 \x01(\x03\x12\x0c\n\x04mark\x18\x02 \x01(\t\x12\x10\n\x08web_link\x18\x03 \x01(\tBcZGgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/blinkresultpb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.blinkresult_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZGgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/blinkresultpb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_BLINKRESULT']._serialized_start=41 26 | _globals['_BLINKRESULT']._serialized_end=98 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/type/taskdo_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/taskdo.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11type/taskdo.proto\x12\rjijidown.core*p\n\x06TaskDo\x12\x12\n\x0eTaskDo_NOTHING\x10\x00\x12\x10\n\x0cTaskDo_PAUSE\x10\x01\x12\x11\n\rTaskDo_RESUME\x10\x02\x12\x11\n\rTaskDo_DELETE\x10\x03\x12\x1a\n\x16TaskDo_DELETE_AND_FILE\x10\x04\x42^ZBgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskdopb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.taskdo_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZBgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskdopb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_TASKDO']._serialized_start=36 26 | _globals['_TASKDO']._serialized_end=148 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/type/updatestatus_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/updatestatus.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17type/updatestatus.proto\x12\rjijidown.core*T\n\x10UpdateStatusType\x12\x0c\n\x08\x43HECKING\x10\x00\x12\x14\n\x10NOTSUPPORTUPDATE\x10\x01\x12\x0c\n\x08UPTODATE\x10\x02\x12\x0e\n\nNEEDUPDATE\x10\x03\x42\x64ZHgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/updatestatuspb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.updatestatus_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZHgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/updatestatuspb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_UPDATESTATUSTYPE']._serialized_start=42 26 | _globals['_UPDATESTATUSTYPE']._serialized_end=126 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/taskstatus_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: taskstatus.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10taskstatus.proto\x12\rjijidown.core*\xcb\x01\n\x0eTaskStatusType\x12\x1b\n\x17TaskStatusType_TASK_ALL\x10\x00\x12\x1d\n\x19TaskStatusType_TASK_ERROR\x10\x01\x12\x1c\n\x18TaskStatusType_TASK_STOP\x10\x02\x12\x1c\n\x18TaskStatusType_TASK_WAIT\x10\x03\x12\x1f\n\x1bTaskStatusType_TASK_RUNNING\x10\x04\x12 \n\x1cTaskStatusType_TASK_COMPLETE\x10\x05\x42\x62ZFgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskstatuspb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'taskstatus_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZFgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskstatuspb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_TASKSTATUSTYPE']._serialized_start=36 26 | _globals['_TASKSTATUSTYPE']._serialized_end=239 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/type/servericon.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/servericonpb"; 6 | option csharp_namespace = "JiJiDown.Core.SDK.Types"; 7 | 8 | // 服务器系统 logo 9 | enum ServerIconType { 10 | ServerIconType_ICON_UNKNOWN = 0; // 未知系统 11 | ServerIconType_ICON_WINDOWS_7 = 1; // Windows 7 12 | ServerIconType_ICON_WINDOWS_10 = 2; // Windows 8/8.1/10/default 13 | ServerIconType_ICON_WINDOWS_11 = 3; // Windows 11 14 | 15 | // Other Linux 16 | ServerIconType_ICON_LINUX = 4; 17 | ServerIconType_ICON_LINUX_DEBIAN = 5; // Debian 18 | ServerIconType_ICON_LINUX_RASPIOS = 6; // Raspberry Pi OS 19 | ServerIconType_ICON_LINUX_UBUNTU = 7; // Ubuntu 20 | ServerIconType_ICON_LINUX_FEDORA = 8; // Fedora 21 | ServerIconType_ICON_LINUX_RED_HAT = 9; // Red Hat 22 | ServerIconType_ICON_LINUX_CENTOS = 10; // CentOS 23 | ServerIconType_ICON_LINUX_CENTOS_STREAM = 11; // CentOS Stream 24 | ServerIconType_ICON_LINUX_ROCKY_LINUX = 12; // Rocky Linux 25 | ServerIconType_ICON_LINUX_ALMALINUX = 13; // AlmaLinux 26 | ServerIconType_ICON_LINUX_ARCH_LINUX = 14; // Arch Linux 27 | 28 | // Other macOS 29 | ServerIconType_ICON_DARWIN = 15; 30 | ServerIconType_ICON_DARWIN_HIGH_SIERRA = 16; // High Sierra 31 | ServerIconType_ICON_DARWIN_MOJAVE = 17; // Mojave 32 | ServerIconType_ICON_DARWIN_CATALINA = 18; // Catalina 33 | ServerIconType_ICON_DARWIN_BIG_SUR = 19; // Big Sur 34 | ServerIconType_ICON_DARWIN_MONTEREY = 20; // Monterey 35 | ServerIconType_ICON_DARWIN_VENTURA = 21; // Ventura 36 | } -------------------------------------------------------------------------------- /grpc_core/type/taskstatus_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/taskstatus.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15type/taskstatus.proto\x12\rjijidown.core*\xf3\x01\n\x0eTaskStatusType\x12\x1d\n\x19TaskStatusType_TASK_ERROR\x10\x00\x12\x1d\n\x19TaskStatusType_TASK_PAUSE\x10\x01\x12\x1f\n\x1bTaskStatusType_TASK_RUNNING\x10\x02\x12\x1c\n\x18TaskStatusType_TASK_WAIT\x10\x03\x12 \n\x1cTaskStatusType_TASK_MERGEING\x10\x04\x12 \n\x1cTaskStatusType_TASK_GENMUSIC\x10\x05\x12 \n\x1cTaskStatusType_TASK_COMPLETE\x10\x06\x42\x62ZFgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskstatuspb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.taskstatus_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZFgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/taskstatuspb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_TASKSTATUSTYPE']._serialized_start=41 26 | _globals['_TASKSTATUSTYPE']._serialized_end=284 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jithon 2 | ## 鸡枞 3 | 4 | Jithon(**鸡枞**)2.0是Jithon 1.0**重构开发**的基于**webUI**界面的唧唧**2.0**前端 5 | 拥有**现代化的外观**、**人性化的交互**以及支持**跨平台运行**的特点 6 | 7 | 本次更新重点: 8 | 9 | 1. 实现了**彻底无需旧前端**的新前端发布 10 | 2. 基于多线程实现了下载列表的**实时刷新** 11 | 3. 支持**批量**视频下载 12 | 4. 支持**一键下载**收藏夹等视频集合 13 | 5. 支持**批量选定分辨率**下载 14 | 6. webUI的特点使得该前端可以部署在**任何设备上**实现远程下载 15 | 7. 已经**完全支持**唧唧2.0核心支持的各种链接解析 16 | 8. 可以和旧UI共存互不干扰(仅需下载Jithon_2.0_Beta.exe) 17 | 9. 支持**自动修复核心启动错误** 18 | 10. 支持**cookies登录**或者自动通过浏览器缓存**一键登录** 19 | 20 | 在经历2023/1/11的更新过后,主程序已经会**自动配置**核心以及配置文件了 21 | 仅需下载主程序并放到合适位置启动 22 | 主程序会自动根据当前系统下载核心、配置相关环境、自动设置主程序位置为下载目录 23 | 24 | ## 下载Jithon 25 | 预编译完成的二进制文件可以在 [Github releases](https://github.com/JiJiDown/jithon/releases) 找到 26 | 目前有两个平台的版本 27 | - Windows7-11 **AMD64** 28 | - Linux 2.6.23及之后版本 **ARM64**(Ubuntu系) 29 | - Linux 2.6.23及之后版本 **AMD64**(Ubuntu系) 30 | ## 软件截图 31 | 32 | [![image.png](https://i.postimg.cc/mDpyp3jL/image.png)](https://postimg.cc/jwNfL7Bm) 33 | 34 | [![17-12-2022-5455-127-0-0-1.jpg](https://i.postimg.cc/xd4Ptyt0/17-12-2022-5455-127-0-0-1.jpg)](https://postimg.cc/xkLHdmw7) 35 | 36 | ## 自行构建步骤 37 | 0. 从Github下载代码 38 | 1. 安装python**3.8**或以上版本 39 | 2. 使用pip指令安装依赖 40 | ```Plain 41 | requests 42 | pywebio 43 | pyinstaller 44 | grpcio 45 | grpcio-tools 46 | tqdm 47 | pyuac 48 | loguru 49 | plyer 50 | ``` 51 | 3. 创建pyinstaller spec (specification) 文件 52 | ```Plain 53 | pyi-makespec app.py 54 | ``` 55 | 或者如下,选择使用pyinstall参数生成单文件 56 | ```Plain 57 | pyi-makespec -F app.py 58 | ``` 59 | 4. 编辑生成的spec文件,将其中`Analysis`的`data`参数修改为: 60 | ```Python 61 | from pywebio.utils import pyinstaller_datas 62 | 63 | a = Analysis( 64 | ... 65 | datas=pyinstaller_datas(), 66 | ... 67 | ``` 68 | 5. 使用spec文件来构建可执行文件: 69 | ```Plain 70 | pyinstaller app.spec 71 | ``` 72 | 6. 构建成功会显示类似如下所示的内容 73 | >`39416 INFO: Building EXE from EXE-00.toc completed successfully.` 74 | 75 | 构建的二进制文件会出现在当前目录下的`dist`文件夹 -------------------------------------------------------------------------------- /grpc_core/user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown"; 6 | option csharp_namespace = "JiJiDown.Core.SDK"; 7 | 8 | import "google/protobuf/empty.proto"; 9 | 10 | // User 11 | service User { 12 | // 获取用户信息 13 | rpc Info(google.protobuf.Empty) returns (UserInfoReply); 14 | // 获取登录二维码 15 | rpc LoginQRCode(UserLoginQRCodeReq) returns (UserLoginQRCodeReply); 16 | // 获取登录状态 17 | rpc LoginStatus(UserLoginStatusReq) returns (stream UserLoginStatusReply); 18 | // 导入 Cookie 19 | rpc ImportCookie(UserImportCookieReq) returns (google.protobuf.Empty); 20 | } 21 | 22 | // 获取用户信息-回复 23 | message UserInfoReply { 24 | // 是否登录 25 | bool is_login = 1; 26 | // 用户 id 27 | int64 mid = 2; 28 | // 用户昵称 29 | string uname = 3; 30 | // 用户头像 31 | bytes face = 4; 32 | // 会员开通状态 33 | bool vip_status = 5; 34 | // 会员名称 35 | string vip_label_text = 6; 36 | // 头衔 37 | string badge = 7; 38 | } 39 | 40 | enum LoginQRCodeAPI { 41 | LoginQRCodeAPI_WEB = 0; 42 | LoginQRCodeAPI_TV = 1; 43 | } 44 | 45 | // 获取登录二维码-请求 46 | message UserLoginQRCodeReq { 47 | LoginQRCodeAPI api = 1; 48 | } 49 | 50 | // 获取登录二维码-回复 51 | message UserLoginQRCodeReply { 52 | // 二维码图片 []byte PNG 53 | bytes qr_code = 1; 54 | // uuid 55 | string id = 2; 56 | } 57 | 58 | enum LoginStatus { 59 | // 未知 60 | LoginStatus_UNKNOWN = 0; 61 | // 登录成功 62 | LoginStatus_SUCCEEDED = 1; 63 | // 二维码已失效 64 | LoginStatus_EXPIRED = 2; 65 | // 未扫码 66 | LoginStatus_UNSCANNED = 3; 67 | // 二维码已扫码未确认 68 | LoginStatus_UNCONFIRMED = 4; 69 | } 70 | 71 | // 获取登录状态-请求 72 | message UserLoginStatusReq { 73 | // uuid 74 | string id = 1; 75 | } 76 | 77 | // 获取登录状态-回复 78 | message UserLoginStatusReply { 79 | // 是否登录成功 80 | bool login_successful = 1; 81 | // 登录状态 82 | LoginStatus status = 2; 83 | } 84 | 85 | // 导入 Cookie-请求 86 | message UserImportCookieReq { 87 | // Cookies 88 | string cookies = 1; 89 | // AccessToken 90 | string access_token = 2; 91 | } 92 | -------------------------------------------------------------------------------- /grpc_core/status_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: status.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 15 | from grpc_core.type import servericon_pb2 as type_dot_servericon__pb2 16 | from grpc_core.type import updatestatus_pb2 as type_dot_updatestatus__pb2 17 | 18 | 19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cstatus.proto\x12\rjijidown.core\x1a\x1bgoogle/protobuf/empty.proto\x1a\x15type/servericon.proto\x1a\x17type/updatestatus.proto\"m\n\x0eStatusPingPong\x12\x13\n\x0bserver_name\x18\x01 \x01(\t\x12.\n\x07os_icon\x18\x02 \x01(\x0e\x32\x1d.jijidown.core.ServerIconType\x12\x16\n\x0eos_system_name\x18\x03 \x01(\t\"]\n\x16StatusCheckUpdateReply\x12/\n\x06status\x18\x01 \x01(\x0e\x32\x1f.jijidown.core.UpdateStatusType\x12\x12\n\nchange_log\x18\x02 \x01(\t2\x97\x01\n\x06Status\x12=\n\x04Ping\x12\x16.google.protobuf.Empty\x1a\x1d.jijidown.core.StatusPingPong\x12N\n\x0b\x43heckUpdate\x12\x16.google.protobuf.Empty\x1a%.jijidown.core.StatusCheckUpdateReply0\x01\x42IZ3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\xaa\x02\x11JiJiDown.Core.SDKb\x06proto3') 20 | 21 | _globals = globals() 22 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 23 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'status_pb2', _globals) 24 | if _descriptor._USE_C_DESCRIPTORS == False: 25 | 26 | DESCRIPTOR._options = None 27 | DESCRIPTOR._serialized_options = b'Z3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\252\002\021JiJiDown.Core.SDK' 28 | _globals['_STATUSPINGPONG']._serialized_start=108 29 | _globals['_STATUSPINGPONG']._serialized_end=217 30 | _globals['_STATUSCHECKUPDATEREPLY']._serialized_start=219 31 | _globals['_STATUSCHECKUPDATEREPLY']._serialized_end=312 32 | _globals['_STATUS']._serialized_start=315 33 | _globals['_STATUS']._serialized_end=466 34 | # @@protoc_insertion_point(module_scope) 35 | -------------------------------------------------------------------------------- /grpc_core/type/servericon_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: type/servericon.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15type/servericon.proto\x12\rjijidown.core*\xe1\x06\n\x0eServerIconType\x12\x1f\n\x1bServerIconType_ICON_UNKNOWN\x10\x00\x12!\n\x1dServerIconType_ICON_WINDOWS_7\x10\x01\x12\"\n\x1eServerIconType_ICON_WINDOWS_10\x10\x02\x12\"\n\x1eServerIconType_ICON_WINDOWS_11\x10\x03\x12\x1d\n\x19ServerIconType_ICON_LINUX\x10\x04\x12$\n ServerIconType_ICON_LINUX_DEBIAN\x10\x05\x12%\n!ServerIconType_ICON_LINUX_RASPIOS\x10\x06\x12$\n ServerIconType_ICON_LINUX_UBUNTU\x10\x07\x12$\n ServerIconType_ICON_LINUX_FEDORA\x10\x08\x12%\n!ServerIconType_ICON_LINUX_RED_HAT\x10\t\x12$\n ServerIconType_ICON_LINUX_CENTOS\x10\n\x12+\n\'ServerIconType_ICON_LINUX_CENTOS_STREAM\x10\x0b\x12)\n%ServerIconType_ICON_LINUX_ROCKY_LINUX\x10\x0c\x12\'\n#ServerIconType_ICON_LINUX_ALMALINUX\x10\r\x12(\n$ServerIconType_ICON_LINUX_ARCH_LINUX\x10\x0e\x12\x1e\n\x1aServerIconType_ICON_DARWIN\x10\x0f\x12*\n&ServerIconType_ICON_DARWIN_HIGH_SIERRA\x10\x10\x12%\n!ServerIconType_ICON_DARWIN_MOJAVE\x10\x11\x12\'\n#ServerIconType_ICON_DARWIN_CATALINA\x10\x12\x12&\n\"ServerIconType_ICON_DARWIN_BIG_SUR\x10\x13\x12\'\n#ServerIconType_ICON_DARWIN_MONTEREY\x10\x14\x12&\n\"ServerIconType_ICON_DARWIN_VENTURA\x10\x15\x42\x62ZFgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/servericonpb\xaa\x02\x17JiJiDown.Core.SDK.Typesb\x06proto3') 17 | 18 | _globals = globals() 19 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 20 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'type.servericon_pb2', _globals) 21 | if _descriptor._USE_C_DESCRIPTORS == False: 22 | 23 | DESCRIPTOR._options = None 24 | DESCRIPTOR._serialized_options = b'ZFgithub.com/JiJiDown/JiJiDownCore-go/common/jijidown/types/servericonpb\252\002\027JiJiDown.Core.SDK.Types' 25 | _globals['_SERVERICONTYPE']._serialized_start=41 26 | _globals['_SERVERICONTYPE']._serialized_end=906 27 | # @@protoc_insertion_point(module_scope) 28 | -------------------------------------------------------------------------------- /grpc_core/type/servericon_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 2 | from google.protobuf import descriptor as _descriptor 3 | from typing import ClassVar as _ClassVar 4 | 5 | DESCRIPTOR: _descriptor.FileDescriptor 6 | 7 | class ServerIconType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 8 | __slots__ = [] 9 | ServerIconType_ICON_UNKNOWN: _ClassVar[ServerIconType] 10 | ServerIconType_ICON_WINDOWS_7: _ClassVar[ServerIconType] 11 | ServerIconType_ICON_WINDOWS_10: _ClassVar[ServerIconType] 12 | ServerIconType_ICON_WINDOWS_11: _ClassVar[ServerIconType] 13 | ServerIconType_ICON_LINUX: _ClassVar[ServerIconType] 14 | ServerIconType_ICON_LINUX_DEBIAN: _ClassVar[ServerIconType] 15 | ServerIconType_ICON_LINUX_RASPIOS: _ClassVar[ServerIconType] 16 | ServerIconType_ICON_LINUX_UBUNTU: _ClassVar[ServerIconType] 17 | ServerIconType_ICON_LINUX_FEDORA: _ClassVar[ServerIconType] 18 | ServerIconType_ICON_LINUX_RED_HAT: _ClassVar[ServerIconType] 19 | ServerIconType_ICON_LINUX_CENTOS: _ClassVar[ServerIconType] 20 | ServerIconType_ICON_LINUX_CENTOS_STREAM: _ClassVar[ServerIconType] 21 | ServerIconType_ICON_LINUX_ROCKY_LINUX: _ClassVar[ServerIconType] 22 | ServerIconType_ICON_LINUX_ALMALINUX: _ClassVar[ServerIconType] 23 | ServerIconType_ICON_LINUX_ARCH_LINUX: _ClassVar[ServerIconType] 24 | ServerIconType_ICON_DARWIN: _ClassVar[ServerIconType] 25 | ServerIconType_ICON_DARWIN_HIGH_SIERRA: _ClassVar[ServerIconType] 26 | ServerIconType_ICON_DARWIN_MOJAVE: _ClassVar[ServerIconType] 27 | ServerIconType_ICON_DARWIN_CATALINA: _ClassVar[ServerIconType] 28 | ServerIconType_ICON_DARWIN_BIG_SUR: _ClassVar[ServerIconType] 29 | ServerIconType_ICON_DARWIN_MONTEREY: _ClassVar[ServerIconType] 30 | ServerIconType_ICON_DARWIN_VENTURA: _ClassVar[ServerIconType] 31 | ServerIconType_ICON_UNKNOWN: ServerIconType 32 | ServerIconType_ICON_WINDOWS_7: ServerIconType 33 | ServerIconType_ICON_WINDOWS_10: ServerIconType 34 | ServerIconType_ICON_WINDOWS_11: ServerIconType 35 | ServerIconType_ICON_LINUX: ServerIconType 36 | ServerIconType_ICON_LINUX_DEBIAN: ServerIconType 37 | ServerIconType_ICON_LINUX_RASPIOS: ServerIconType 38 | ServerIconType_ICON_LINUX_UBUNTU: ServerIconType 39 | ServerIconType_ICON_LINUX_FEDORA: ServerIconType 40 | ServerIconType_ICON_LINUX_RED_HAT: ServerIconType 41 | ServerIconType_ICON_LINUX_CENTOS: ServerIconType 42 | ServerIconType_ICON_LINUX_CENTOS_STREAM: ServerIconType 43 | ServerIconType_ICON_LINUX_ROCKY_LINUX: ServerIconType 44 | ServerIconType_ICON_LINUX_ALMALINUX: ServerIconType 45 | ServerIconType_ICON_LINUX_ARCH_LINUX: ServerIconType 46 | ServerIconType_ICON_DARWIN: ServerIconType 47 | ServerIconType_ICON_DARWIN_HIGH_SIERRA: ServerIconType 48 | ServerIconType_ICON_DARWIN_MOJAVE: ServerIconType 49 | ServerIconType_ICON_DARWIN_CATALINA: ServerIconType 50 | ServerIconType_ICON_DARWIN_BIG_SUR: ServerIconType 51 | ServerIconType_ICON_DARWIN_MONTEREY: ServerIconType 52 | ServerIconType_ICON_DARWIN_VENTURA: ServerIconType 53 | -------------------------------------------------------------------------------- /grpc_core/bvideo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown"; 6 | option csharp_namespace = "JiJiDown.Core.SDK"; 7 | 8 | import "type/video.proto"; 9 | import "type/api.proto"; 10 | import "type/blinkresult.proto"; 11 | 12 | // Bvideo 13 | service Bvideo { 14 | // 效验输入的内容, 有效返回 BLinkResult 15 | rpc CheckContent(BvideoContentReq) returns (BvideoCheckContentReply); 16 | // 获取视频信息 17 | rpc Info(BvideoContentReq) returns (BvideoInfoReply); 18 | // 查找此视频所有支持的清晰度 19 | rpc AllQuality(BvideoAllQualityReq) returns (BvideoAllQualityReply); 20 | } 21 | 22 | // Bvideo content 输入的内容-请求 23 | message BvideoContentReq { 24 | // 输入的内容 25 | string content = 1; 26 | } 27 | 28 | // 效验输入的内容, 有效返回 BLinkResult-回复 29 | message BvideoCheckContentReply { 30 | // 数据有效 31 | bool is_valid = 1; 32 | // B站 URL 检测结果 33 | BLinkResult blink_result = 2; 34 | } 35 | 36 | // 获取视频信息-回复 37 | message BvideoInfoReply { 38 | // B站 URL 检测结果 39 | BLinkResult blink_result = 1; 40 | // 视频封面 41 | bytes video_cover = 2; 42 | // 视频标题 43 | string video_title = 3; 44 | // 视频文件名 45 | string video_filename = 4; 46 | // 描述 47 | string video_desc = 5; 48 | // 视频子类型 49 | string sub_sort = 6; 50 | // 视频类型 51 | string sort = 7; 52 | // UP主昵称 53 | string up_name = 9; 54 | // UP主 ID 55 | int64 up_mid = 10; 56 | // UP主头像 57 | bytes up_face = 11; 58 | // B站发布时间 番剧的字符串时间 59 | string bili_pubdate_str = 12; 60 | // 是否为互动视频 61 | bool is_stein_gate = 13; 62 | // 视频列表块 63 | repeated BvideoBlock block = 14; 64 | } 65 | 66 | message BvideoBlock { 67 | // 块标题 68 | string block_title = 1; 69 | // 视频列表 70 | repeated BvideoPage list = 2; 71 | } 72 | 73 | message BvideoPage { 74 | // av 号 75 | int64 page_av = 1; 76 | // BV 号 77 | string page_bv = 2; 78 | // cid 79 | int64 page_cid = 3; 80 | // page 索引 81 | int32 page_index = 4; 82 | // page 封面 83 | bytes page_cover = 5; 84 | // page 标题 85 | string page_title = 6; 86 | // page 信息 (发布时间, 视频时长) 87 | repeated string page_info = 7; 88 | } 89 | 90 | // 查找此视频所有支持的清晰度-请求 91 | message BvideoAllQualityReq { 92 | // aid 93 | int64 aid = 1; 94 | // bvid 和 aid 二个参数必传一个, bvid 优先级比 aid 高 95 | string bvid = 2; 96 | // cid 97 | int64 cid = 3; 98 | } 99 | 100 | message BvideoBVideoItem { 101 | // 清晰度 id 102 | uint32 quality_id = 1; 103 | // 清晰度文本 [e.g. 高清 1080P] 104 | string quality_text = 2; 105 | // 编码 [e.g. AVC] 106 | VideoType codec = 3; 107 | // 帧率 [e.g. 29.412 fps] 108 | string frame_rate = 4; 109 | // 比特率 [e.g. 525 kbps] 110 | string bit_rate = 5; 111 | // Stream 大小 [e.g. 15.92 MiB] 112 | string stream_size = 6; 113 | // item 来源 API 114 | ApiType api_type = 7; 115 | } 116 | 117 | message BvideoBAudioItem { 118 | // 清晰度 id 119 | uint32 quality_id = 1; 120 | // 清晰度文本 [e.g. 极高音质] 121 | string quality_text = 2; 122 | // 编码 [e.g. AAC] 123 | string codec_text = 3; 124 | // 比特率 [e.g. 321 kbps] 125 | string bit_rate = 4; 126 | // Stream 大小 [e.g. 9.74 MiB] 127 | string stream_size = 5; 128 | // item 来源 API 129 | ApiType api_type = 6; 130 | } 131 | 132 | // 查找此视频所有支持的清晰度-回复 133 | message BvideoAllQualityReply { 134 | // Video Stream 135 | repeated BvideoBVideoItem video = 1; 136 | // Audio Stream 137 | repeated BvideoBAudioItem audio = 2; 138 | } 139 | -------------------------------------------------------------------------------- /grpc_core/user_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf import empty_pb2 as _empty_pb2 2 | from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper 3 | from google.protobuf import descriptor as _descriptor 4 | from google.protobuf import message as _message 5 | from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union 6 | 7 | DESCRIPTOR: _descriptor.FileDescriptor 8 | 9 | class LoginQRCodeAPI(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 10 | __slots__ = [] 11 | LoginQRCodeAPI_WEB: _ClassVar[LoginQRCodeAPI] 12 | LoginQRCodeAPI_TV: _ClassVar[LoginQRCodeAPI] 13 | 14 | class LoginStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): 15 | __slots__ = [] 16 | LoginStatus_UNKNOWN: _ClassVar[LoginStatus] 17 | LoginStatus_SUCCEEDED: _ClassVar[LoginStatus] 18 | LoginStatus_EXPIRED: _ClassVar[LoginStatus] 19 | LoginStatus_UNSCANNED: _ClassVar[LoginStatus] 20 | LoginStatus_UNCONFIRMED: _ClassVar[LoginStatus] 21 | LoginQRCodeAPI_WEB: LoginQRCodeAPI 22 | LoginQRCodeAPI_TV: LoginQRCodeAPI 23 | LoginStatus_UNKNOWN: LoginStatus 24 | LoginStatus_SUCCEEDED: LoginStatus 25 | LoginStatus_EXPIRED: LoginStatus 26 | LoginStatus_UNSCANNED: LoginStatus 27 | LoginStatus_UNCONFIRMED: LoginStatus 28 | 29 | class UserInfoReply(_message.Message): 30 | __slots__ = ["is_login", "mid", "uname", "face", "vip_status", "vip_label_text", "badge"] 31 | IS_LOGIN_FIELD_NUMBER: _ClassVar[int] 32 | MID_FIELD_NUMBER: _ClassVar[int] 33 | UNAME_FIELD_NUMBER: _ClassVar[int] 34 | FACE_FIELD_NUMBER: _ClassVar[int] 35 | VIP_STATUS_FIELD_NUMBER: _ClassVar[int] 36 | VIP_LABEL_TEXT_FIELD_NUMBER: _ClassVar[int] 37 | BADGE_FIELD_NUMBER: _ClassVar[int] 38 | is_login: bool 39 | mid: int 40 | uname: str 41 | face: bytes 42 | vip_status: bool 43 | vip_label_text: str 44 | badge: str 45 | def __init__(self, is_login: bool = ..., mid: _Optional[int] = ..., uname: _Optional[str] = ..., face: _Optional[bytes] = ..., vip_status: bool = ..., vip_label_text: _Optional[str] = ..., badge: _Optional[str] = ...) -> None: ... 46 | 47 | class UserLoginQRCodeReq(_message.Message): 48 | __slots__ = ["api"] 49 | API_FIELD_NUMBER: _ClassVar[int] 50 | api: LoginQRCodeAPI 51 | def __init__(self, api: _Optional[_Union[LoginQRCodeAPI, str]] = ...) -> None: ... 52 | 53 | class UserLoginQRCodeReply(_message.Message): 54 | __slots__ = ["qr_code", "id"] 55 | QR_CODE_FIELD_NUMBER: _ClassVar[int] 56 | ID_FIELD_NUMBER: _ClassVar[int] 57 | qr_code: bytes 58 | id: str 59 | def __init__(self, qr_code: _Optional[bytes] = ..., id: _Optional[str] = ...) -> None: ... 60 | 61 | class UserLoginStatusReq(_message.Message): 62 | __slots__ = ["id"] 63 | ID_FIELD_NUMBER: _ClassVar[int] 64 | id: str 65 | def __init__(self, id: _Optional[str] = ...) -> None: ... 66 | 67 | class UserLoginStatusReply(_message.Message): 68 | __slots__ = ["login_successful", "status"] 69 | LOGIN_SUCCESSFUL_FIELD_NUMBER: _ClassVar[int] 70 | STATUS_FIELD_NUMBER: _ClassVar[int] 71 | login_successful: bool 72 | status: LoginStatus 73 | def __init__(self, login_successful: bool = ..., status: _Optional[_Union[LoginStatus, str]] = ...) -> None: ... 74 | 75 | class UserImportCookieReq(_message.Message): 76 | __slots__ = ["cookies", "access_token"] 77 | COOKIES_FIELD_NUMBER: _ClassVar[int] 78 | ACCESS_TOKEN_FIELD_NUMBER: _ClassVar[int] 79 | cookies: str 80 | access_token: str 81 | def __init__(self, cookies: _Optional[str] = ..., access_token: _Optional[str] = ...) -> None: ... 82 | -------------------------------------------------------------------------------- /grpc_core/task.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package jijidown.core; 4 | 5 | option go_package = "github.com/JiJiDown/JiJiDownCore-go/common/jijidown"; 6 | option csharp_namespace = "JiJiDown.Core.SDK"; 7 | 8 | import "google/protobuf/empty.proto"; 9 | import "type/api.proto"; 10 | import "type/video.proto"; 11 | import "type/taskstatus.proto"; 12 | import "type/taskdo.proto"; 13 | 14 | // Task 15 | service Task { 16 | // 创建新任务 17 | rpc New(TaskNewReq) returns (google.protobuf.Empty); 18 | // 批量创建新任务 19 | rpc NewBatch(TaskNewBatchReq) returns (TaskNewBatchReply); 20 | // 任务状态 21 | rpc Status(TaskStatusReq) returns (TaskStatusReply); 22 | // 获取任务列表 23 | rpc List(TaskListReq) returns (TaskListReply); 24 | // 任务控制 (暂停, 继续, 删除) 25 | rpc Control(TaskControlReq) returns (google.protobuf.Empty); 26 | // 任务完成通知 27 | rpc Notification(google.protobuf.Empty) returns (stream TaskNotificationReply); 28 | } 29 | 30 | // 创建新任务-请求 31 | message TaskNewReq { 32 | // aid 33 | int64 aid = 1; 34 | // bvid 和 aid 二个参数必传一个, bvid 优先级比 aid 高 35 | string bvid = 2; 36 | // cid 37 | int64 cid = 3; 38 | // 视频质量 39 | uint32 video_quality = 4; 40 | // 音频质量 41 | uint32 audio_quality = 5; 42 | // 视频编码格式 43 | VideoType video_codec = 6; 44 | // 下载使用的 API (WEB / TV / APP) 45 | ApiType api_type = 7; 46 | // 保存文件名 47 | string save_filename = 8; 48 | // 仅下载音频 49 | bool audio_only = 9; 50 | // 批量下载回调 51 | uint64 callback = 10; 52 | } 53 | 54 | // 任务创建状态 55 | message TaskCreationStatus { 56 | // 批量下载回调 57 | uint64 callback = 1; 58 | // 错误详细信息 59 | string err = 2; 60 | } 61 | 62 | // 批量创建新任务-回复 63 | message TaskNewBatchReply { 64 | // 批量下载 任务创建状态 65 | repeated TaskCreationStatus task_creation_status = 1; 66 | } 67 | 68 | // 批量创建新任务-请求 69 | message TaskNewBatchReq { 70 | // 批量下载任务组 71 | repeated TaskNewReq new_tasks = 1; 72 | } 73 | 74 | // 任务状态-请求 75 | message TaskStatusReq { 76 | // 任务 id 77 | string task_id = 1; 78 | } 79 | 80 | // 任务进度 81 | message TaskProgress { 82 | // 总长度 83 | string total_length = 1; 84 | // 完成长度 85 | string completed_length = 2; 86 | // 剩余长度 87 | string left_length = 3; 88 | // 下载速度 每秒 89 | string download_speed = 4; 90 | // 下载完成预计时间 91 | string eta = 5; 92 | // 下载进度 1 - 100 93 | uint32 progress = 6; 94 | } 95 | 96 | // 任务状态-回复 97 | message TaskStatusReply { 98 | // 任务 id 99 | string task_id = 1; 100 | // 任务标题名称 101 | string task_title = 2; 102 | // 视频清晰度角标 103 | string video_badge = 3; 104 | // 音频质量角标 105 | string audio_badge = 4; 106 | // 视频编码格式 107 | string video_codec = 5; 108 | // 下载使用的 API (WEB / TV / APP) 109 | ApiType api_type = 6; 110 | // 仅下载音频 111 | bool audio_only = 7; 112 | // 任务状态 113 | TaskStatusType task_status = 8; 114 | // 任务添加时间 115 | int64 add_time = 9; 116 | // 任务完成时间 117 | int64 complete_time = 10; 118 | // 任务进度 119 | TaskProgress progress = 11; 120 | // 下载文件保存路径 121 | string save_path = 12; 122 | } 123 | 124 | // 获取任务列表-请求 125 | message TaskListReq { 126 | // 任务列表状态 127 | TaskStatusType task_status = 1; 128 | } 129 | 130 | // 获取任务列表-回复 131 | message TaskListReply { 132 | // 任务列表状态 133 | repeated TaskStatusReply tasks = 1; 134 | } 135 | 136 | // 任务控制 (暂停, 继续, 删除)-请求 137 | message TaskControlReq { 138 | // 任务 id 139 | string task_id = 1; 140 | // 操作 141 | TaskDo do = 2; 142 | } 143 | 144 | // 任务完成通知-回复 145 | message TaskNotificationReply { 146 | // 任务 id 147 | string task_id = 1; 148 | // 任务标题名称 149 | string task_title = 2; 150 | // 平均下载速度 151 | string average_speed = 3; 152 | // 下载用时 153 | string elapsed_time = 4; 154 | } -------------------------------------------------------------------------------- /grpc_core/user_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: user.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nuser.proto\x12\rjijidown.core\x1a\x1bgoogle/protobuf/empty.proto\"\x86\x01\n\rUserInfoReply\x12\x10\n\x08is_login\x18\x01 \x01(\x08\x12\x0b\n\x03mid\x18\x02 \x01(\x03\x12\r\n\x05uname\x18\x03 \x01(\t\x12\x0c\n\x04\x66\x61\x63\x65\x18\x04 \x01(\x0c\x12\x12\n\nvip_status\x18\x05 \x01(\x08\x12\x16\n\x0evip_label_text\x18\x06 \x01(\t\x12\r\n\x05\x62\x61\x64ge\x18\x07 \x01(\t\"@\n\x12UserLoginQRCodeReq\x12*\n\x03\x61pi\x18\x01 \x01(\x0e\x32\x1d.jijidown.core.LoginQRCodeAPI\"3\n\x14UserLoginQRCodeReply\x12\x0f\n\x07qr_code\x18\x01 \x01(\x0c\x12\n\n\x02id\x18\x02 \x01(\t\" \n\x12UserLoginStatusReq\x12\n\n\x02id\x18\x01 \x01(\t\"\\\n\x14UserLoginStatusReply\x12\x18\n\x10login_successful\x18\x01 \x01(\x08\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.jijidown.core.LoginStatus\"<\n\x13UserImportCookieReq\x12\x0f\n\x07\x63ookies\x18\x01 \x01(\t\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x02 \x01(\t*?\n\x0eLoginQRCodeAPI\x12\x16\n\x12LoginQRCodeAPI_WEB\x10\x00\x12\x15\n\x11LoginQRCodeAPI_TV\x10\x01*\x92\x01\n\x0bLoginStatus\x12\x17\n\x13LoginStatus_UNKNOWN\x10\x00\x12\x19\n\x15LoginStatus_SUCCEEDED\x10\x01\x12\x17\n\x13LoginStatus_EXPIRED\x10\x02\x12\x19\n\x15LoginStatus_UNSCANNED\x10\x03\x12\x1b\n\x17LoginStatus_UNCONFIRMED\x10\x04\x32\xc0\x02\n\x04User\x12<\n\x04Info\x12\x16.google.protobuf.Empty\x1a\x1c.jijidown.core.UserInfoReply\x12U\n\x0bLoginQRCode\x12!.jijidown.core.UserLoginQRCodeReq\x1a#.jijidown.core.UserLoginQRCodeReply\x12W\n\x0bLoginStatus\x12!.jijidown.core.UserLoginStatusReq\x1a#.jijidown.core.UserLoginStatusReply0\x01\x12J\n\x0cImportCookie\x12\".jijidown.core.UserImportCookieReq\x1a\x16.google.protobuf.EmptyBIZ3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\xaa\x02\x11JiJiDown.Core.SDKb\x06proto3') 18 | 19 | _globals = globals() 20 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 21 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'user_pb2', _globals) 22 | if _descriptor._USE_C_DESCRIPTORS == False: 23 | 24 | DESCRIPTOR._options = None 25 | DESCRIPTOR._serialized_options = b'Z3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\252\002\021JiJiDown.Core.SDK' 26 | _globals['_LOGINQRCODEAPI']._serialized_start=504 27 | _globals['_LOGINQRCODEAPI']._serialized_end=567 28 | _globals['_LOGINSTATUS']._serialized_start=570 29 | _globals['_LOGINSTATUS']._serialized_end=716 30 | _globals['_USERINFOREPLY']._serialized_start=59 31 | _globals['_USERINFOREPLY']._serialized_end=193 32 | _globals['_USERLOGINQRCODEREQ']._serialized_start=195 33 | _globals['_USERLOGINQRCODEREQ']._serialized_end=259 34 | _globals['_USERLOGINQRCODEREPLY']._serialized_start=261 35 | _globals['_USERLOGINQRCODEREPLY']._serialized_end=312 36 | _globals['_USERLOGINSTATUSREQ']._serialized_start=314 37 | _globals['_USERLOGINSTATUSREQ']._serialized_end=346 38 | _globals['_USERLOGINSTATUSREPLY']._serialized_start=348 39 | _globals['_USERLOGINSTATUSREPLY']._serialized_end=440 40 | _globals['_USERIMPORTCOOKIEREQ']._serialized_start=442 41 | _globals['_USERIMPORTCOOKIEREQ']._serialized_end=502 42 | _globals['_USER']._serialized_start=719 43 | _globals['_USER']._serialized_end=1039 44 | # @@protoc_insertion_point(module_scope) 45 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | import re 3 | from grpc_core import user_pb2 4 | from grpc_core import user_pb2_grpc 5 | from grpc_core import task_pb2 6 | from grpc_core import task_pb2_grpc 7 | from grpc_core import status_pb2 8 | from grpc_core import status_pb2_grpc 9 | from grpc_core import bvideo_pb2 10 | from grpc_core import bvideo_pb2_grpc 11 | import base64 12 | #import core 13 | import subprocess 14 | import ctypes,sys,os 15 | from pathlib import Path #路径库 16 | import requests 17 | import browser_cookie3 18 | 19 | import google.protobuf.empty_pb2 as empty_pb2 20 | import os 21 | #需要创建pyi文件不然pb2不知道使用哪个类 22 | 23 | #错误处理 24 | def run(): 25 | channel = grpc.insecure_channel('localhost:64000') 26 | stub = user_pb2_grpc.UserStub(channel) 27 | #a = stub.LoginQRCode(user_pb2.UserLoginQRCodeReq(api=0),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 28 | #input(a.id) 29 | try: 30 | b = stub.LoginStatus(user_pb2.UserLoginStatusReq(id='da546958-fd3d-4369-ae8c-bfd382028f8e'),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 31 | for c in b: 32 | print(c.status) 33 | except Exception as e: 34 | code_name = e.code().name # 错误类型 35 | code_value = e.code().value 36 | print(e.details())#错误信息 37 | print(code_name)#错误类型 38 | print(code_value)#错误状态码 39 | 40 | def runb(): 41 | channel = grpc.insecure_channel('localhost:64000') 42 | stub = user_pb2_grpc.UserStub(channel) 43 | try: 44 | response = stub.Info(user_pb2.UserInfoReply(),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 45 | except Exception as e:#捕获错误 46 | print(e) 47 | input() 48 | b = '' 49 | for a in response.face: 50 | b += str(a) 51 | with open('face.png','wb') as f: 52 | f.write(response.face) 53 | 54 | def runc(): 55 | channel = grpc.insecure_channel('localhost:64000') 56 | stub = bvideo_pb2_grpc.BvideoStub(channel) 57 | response = stub.Info(bvideo_pb2.BvideoContentReq(content='https://www.bilibili.com/video/BV1us4y167zF/?spm_id_from=333.1007.tianma.1-3-3.click&vd_source=32f2f4d0d74594e0b5a183b394bf9a49'),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 58 | print(response.video_title) 59 | a = response.block[0].list[0] 60 | print(a.page_bv) 61 | print 62 | 63 | def rund(): 64 | channel = grpc.insecure_channel('localhost:64000') 65 | stub = bvideo_pb2_grpc.BvideoStub(channel) 66 | response = stub.Info(bvideo_pb2.BvideoContentReq(content='https://www.bilibili.com/video/BV1us4y167zF/?spm_id_from=333.1007.tianma.1-3-3.click&vd_source=32f2f4d0d74594e0b5a183b394bf9a49'),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 67 | response = stub.AllQuality(bvideo_pb2.BvideoAllQualityReq(aid=response.block[0].list[0].page_av,cid=response.block[0].list[0].page_cid),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 68 | print 69 | 70 | def rune(): 71 | out = core.info('https://www.bilibili.com/video/BV1us4y167zF') 72 | response = core.quality(bvid=out['block'][0].list[0].page_bv,cid=out['block'][0].list[0].page_cid) 73 | print(response) 74 | 75 | def login(): 76 | channel = grpc.insecure_channel('localhost:64000') 77 | stub = user_pb2_grpc.UserStub(channel) 78 | response = stub.LoginQRCode(user_pb2.UserLoginQRCodeReq(api=1),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 79 | login_image:bytes = response.qr_code 80 | with open('qr.png','wb') as f: 81 | f.write(login_image) 82 | 83 | def update(): 84 | channel = grpc.insecure_channel('localhost:64000') 85 | stub = status_pb2_grpc.StatusStub(channel) 86 | response =stub.CheckUpdate(empty_pb2.Empty(),metadata=[('client_sdk','JiJiDownPython/1.0.0')]) 87 | for one in response: 88 | print(one.status) 89 | print(one.change_log) 90 | print(one.change_log) 91 | from plyer import notification 92 | notification.notify(title="测试", 93 | message="测试弹窗功能", 94 | app_icon='F:\\code\\jithon\\grpc_ver\\app.ico', 95 | timeout=10 96 | ) 97 | """诡秘16 98 | 诡秘15 99 | 伊藤萝莉1-3 100 | 百日百合1 101 | 甘雨本 102 | 恋人不行1-2 103 | 辉夜22 104 | 仙狐小姐9-10""" -------------------------------------------------------------------------------- /grpc_core/status_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 6 | from grpc_core import status_pb2 as status__pb2 7 | 8 | 9 | class StatusStub(object): 10 | """Status 11 | """ 12 | 13 | def __init__(self, channel): 14 | """Constructor. 15 | 16 | Args: 17 | channel: A grpc.Channel. 18 | """ 19 | self.Ping = channel.unary_unary( 20 | '/jijidown.core.Status/Ping', 21 | request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 22 | response_deserializer=status__pb2.StatusPingPong.FromString, 23 | ) 24 | self.CheckUpdate = channel.unary_stream( 25 | '/jijidown.core.Status/CheckUpdate', 26 | request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 27 | response_deserializer=status__pb2.StatusCheckUpdateReply.FromString, 28 | ) 29 | 30 | 31 | class StatusServicer(object): 32 | """Status 33 | """ 34 | 35 | def Ping(self, request, context): 36 | """检查服务器是否在线 37 | """ 38 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 39 | context.set_details('Method not implemented!') 40 | raise NotImplementedError('Method not implemented!') 41 | 42 | def CheckUpdate(self, request, context): 43 | """检查更新 44 | """ 45 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 46 | context.set_details('Method not implemented!') 47 | raise NotImplementedError('Method not implemented!') 48 | 49 | 50 | def add_StatusServicer_to_server(servicer, server): 51 | rpc_method_handlers = { 52 | 'Ping': grpc.unary_unary_rpc_method_handler( 53 | servicer.Ping, 54 | request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 55 | response_serializer=status__pb2.StatusPingPong.SerializeToString, 56 | ), 57 | 'CheckUpdate': grpc.unary_stream_rpc_method_handler( 58 | servicer.CheckUpdate, 59 | request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 60 | response_serializer=status__pb2.StatusCheckUpdateReply.SerializeToString, 61 | ), 62 | } 63 | generic_handler = grpc.method_handlers_generic_handler( 64 | 'jijidown.core.Status', rpc_method_handlers) 65 | server.add_generic_rpc_handlers((generic_handler,)) 66 | 67 | 68 | # This class is part of an EXPERIMENTAL API. 69 | class Status(object): 70 | """Status 71 | """ 72 | 73 | @staticmethod 74 | def Ping(request, 75 | target, 76 | options=(), 77 | channel_credentials=None, 78 | call_credentials=None, 79 | insecure=False, 80 | compression=None, 81 | wait_for_ready=None, 82 | timeout=None, 83 | metadata=None): 84 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Status/Ping', 85 | google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 86 | status__pb2.StatusPingPong.FromString, 87 | options, channel_credentials, 88 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 89 | 90 | @staticmethod 91 | def CheckUpdate(request, 92 | target, 93 | options=(), 94 | channel_credentials=None, 95 | call_credentials=None, 96 | insecure=False, 97 | compression=None, 98 | wait_for_ready=None, 99 | timeout=None, 100 | metadata=None): 101 | return grpc.experimental.unary_stream(request, target, '/jijidown.core.Status/CheckUpdate', 102 | google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 103 | status__pb2.StatusCheckUpdateReply.FromString, 104 | options, channel_credentials, 105 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 106 | -------------------------------------------------------------------------------- /grpc_core/bvideo_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: bvideo.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from grpc_core.type import video_pb2 as type_dot_video__pb2 15 | from grpc_core.type import api_pb2 as type_dot_api__pb2 16 | from grpc_core.type import blinkresult_pb2 as type_dot_blinkresult__pb2 17 | 18 | 19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x62video.proto\x12\rjijidown.core\x1a\x10type/video.proto\x1a\x0etype/api.proto\x1a\x16type/blinkresult.proto\"#\n\x10\x42videoContentReq\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\"]\n\x17\x42videoCheckContentReply\x12\x10\n\x08is_valid\x18\x01 \x01(\x08\x12\x30\n\x0c\x62link_result\x18\x02 \x01(\x0b\x32\x1a.jijidown.core.BLinkResult\"\xc7\x02\n\x0f\x42videoInfoReply\x12\x30\n\x0c\x62link_result\x18\x01 \x01(\x0b\x32\x1a.jijidown.core.BLinkResult\x12\x13\n\x0bvideo_cover\x18\x02 \x01(\x0c\x12\x13\n\x0bvideo_title\x18\x03 \x01(\t\x12\x16\n\x0evideo_filename\x18\x04 \x01(\t\x12\x12\n\nvideo_desc\x18\x05 \x01(\t\x12\x10\n\x08sub_sort\x18\x06 \x01(\t\x12\x0c\n\x04sort\x18\x07 \x01(\t\x12\x0f\n\x07up_name\x18\t \x01(\t\x12\x0e\n\x06up_mid\x18\n \x01(\x03\x12\x0f\n\x07up_face\x18\x0b \x01(\x0c\x12\x18\n\x10\x62ili_pubdate_str\x18\x0c \x01(\t\x12\x15\n\ris_stein_gate\x18\r \x01(\x08\x12)\n\x05\x62lock\x18\x0e \x03(\x0b\x32\x1a.jijidown.core.BvideoBlock\"K\n\x0b\x42videoBlock\x12\x13\n\x0b\x62lock_title\x18\x01 \x01(\t\x12\'\n\x04list\x18\x02 \x03(\x0b\x32\x19.jijidown.core.BvideoPage\"\x8f\x01\n\nBvideoPage\x12\x0f\n\x07page_av\x18\x01 \x01(\x03\x12\x0f\n\x07page_bv\x18\x02 \x01(\t\x12\x10\n\x08page_cid\x18\x03 \x01(\x03\x12\x12\n\npage_index\x18\x04 \x01(\x05\x12\x12\n\npage_cover\x18\x05 \x01(\x0c\x12\x12\n\npage_title\x18\x06 \x01(\t\x12\x11\n\tpage_info\x18\x07 \x03(\t\"=\n\x13\x42videoAllQualityReq\x12\x0b\n\x03\x61id\x18\x01 \x01(\x03\x12\x0c\n\x04\x62vid\x18\x02 \x01(\t\x12\x0b\n\x03\x63id\x18\x03 \x01(\x03\"\xca\x01\n\x10\x42videoBVideoItem\x12\x12\n\nquality_id\x18\x01 \x01(\r\x12\x14\n\x0cquality_text\x18\x02 \x01(\t\x12\'\n\x05\x63odec\x18\x03 \x01(\x0e\x32\x18.jijidown.core.VideoType\x12\x12\n\nframe_rate\x18\x04 \x01(\t\x12\x10\n\x08\x62it_rate\x18\x05 \x01(\t\x12\x13\n\x0bstream_size\x18\x06 \x01(\t\x12(\n\x08\x61pi_type\x18\x07 \x01(\x0e\x32\x16.jijidown.core.ApiType\"\xa1\x01\n\x10\x42videoBAudioItem\x12\x12\n\nquality_id\x18\x01 \x01(\r\x12\x14\n\x0cquality_text\x18\x02 \x01(\t\x12\x12\n\ncodec_text\x18\x03 \x01(\t\x12\x10\n\x08\x62it_rate\x18\x04 \x01(\t\x12\x13\n\x0bstream_size\x18\x05 \x01(\t\x12(\n\x08\x61pi_type\x18\x06 \x01(\x0e\x32\x16.jijidown.core.ApiType\"w\n\x15\x42videoAllQualityReply\x12.\n\x05video\x18\x01 \x03(\x0b\x32\x1f.jijidown.core.BvideoBVideoItem\x12.\n\x05\x61udio\x18\x02 \x03(\x0b\x32\x1f.jijidown.core.BvideoBAudioItem2\x82\x02\n\x06\x42video\x12W\n\x0c\x43heckContent\x12\x1f.jijidown.core.BvideoContentReq\x1a&.jijidown.core.BvideoCheckContentReply\x12G\n\x04Info\x12\x1f.jijidown.core.BvideoContentReq\x1a\x1e.jijidown.core.BvideoInfoReply\x12V\n\nAllQuality\x12\".jijidown.core.BvideoAllQualityReq\x1a$.jijidown.core.BvideoAllQualityReplyBIZ3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\xaa\x02\x11JiJiDown.Core.SDKb\x06proto3') 20 | 21 | _globals = globals() 22 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 23 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'bvideo_pb2', _globals) 24 | if _descriptor._USE_C_DESCRIPTORS == False: 25 | 26 | DESCRIPTOR._options = None 27 | DESCRIPTOR._serialized_options = b'Z3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\252\002\021JiJiDown.Core.SDK' 28 | _globals['_BVIDEOCONTENTREQ']._serialized_start=89 29 | _globals['_BVIDEOCONTENTREQ']._serialized_end=124 30 | _globals['_BVIDEOCHECKCONTENTREPLY']._serialized_start=126 31 | _globals['_BVIDEOCHECKCONTENTREPLY']._serialized_end=219 32 | _globals['_BVIDEOINFOREPLY']._serialized_start=222 33 | _globals['_BVIDEOINFOREPLY']._serialized_end=549 34 | _globals['_BVIDEOBLOCK']._serialized_start=551 35 | _globals['_BVIDEOBLOCK']._serialized_end=626 36 | _globals['_BVIDEOPAGE']._serialized_start=629 37 | _globals['_BVIDEOPAGE']._serialized_end=772 38 | _globals['_BVIDEOALLQUALITYREQ']._serialized_start=774 39 | _globals['_BVIDEOALLQUALITYREQ']._serialized_end=835 40 | _globals['_BVIDEOBVIDEOITEM']._serialized_start=838 41 | _globals['_BVIDEOBVIDEOITEM']._serialized_end=1040 42 | _globals['_BVIDEOBAUDIOITEM']._serialized_start=1043 43 | _globals['_BVIDEOBAUDIOITEM']._serialized_end=1204 44 | _globals['_BVIDEOALLQUALITYREPLY']._serialized_start=1206 45 | _globals['_BVIDEOALLQUALITYREPLY']._serialized_end=1325 46 | _globals['_BVIDEO']._serialized_start=1328 47 | _globals['_BVIDEO']._serialized_end=1586 48 | # @@protoc_insertion_point(module_scope) 49 | -------------------------------------------------------------------------------- /grpc_core/task_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: task.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import symbol_database as _symbol_database 8 | from google.protobuf.internal import builder as _builder 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 15 | from type import api_pb2 as type_dot_api__pb2 16 | from type import video_pb2 as type_dot_video__pb2 17 | from type import taskstatus_pb2 as type_dot_taskstatus__pb2 18 | from type import taskdo_pb2 as type_dot_taskdo__pb2 19 | 20 | 21 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ntask.proto\x12\rjijidown.core\x1a\x1bgoogle/protobuf/empty.proto\x1a\x0etype/api.proto\x1a\x10type/video.proto\x1a\x15type/taskstatus.proto\x1a\x11type/taskdo.proto\"\xf8\x01\n\nTaskNewReq\x12\x0b\n\x03\x61id\x18\x01 \x01(\x03\x12\x0c\n\x04\x62vid\x18\x02 \x01(\t\x12\x0b\n\x03\x63id\x18\x03 \x01(\x03\x12\x15\n\rvideo_quality\x18\x04 \x01(\r\x12\x15\n\raudio_quality\x18\x05 \x01(\r\x12-\n\x0bvideo_codec\x18\x06 \x01(\x0e\x32\x18.jijidown.core.VideoType\x12(\n\x08\x61pi_type\x18\x07 \x01(\x0e\x32\x16.jijidown.core.ApiType\x12\x15\n\rsave_filename\x18\x08 \x01(\t\x12\x12\n\naudio_only\x18\t \x01(\x08\x12\x10\n\x08\x63\x61llback\x18\n \x01(\x04\"3\n\x12TaskCreationStatus\x12\x10\n\x08\x63\x61llback\x18\x01 \x01(\x04\x12\x0b\n\x03\x65rr\x18\x02 \x01(\t\"T\n\x11TaskNewBatchReply\x12?\n\x14task_creation_status\x18\x01 \x03(\x0b\x32!.jijidown.core.TaskCreationStatus\"?\n\x0fTaskNewBatchReq\x12,\n\tnew_tasks\x18\x01 \x03(\x0b\x32\x19.jijidown.core.TaskNewReq\" \n\rTaskStatusReq\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\x8a\x01\n\x0cTaskProgress\x12\x14\n\x0ctotal_length\x18\x01 \x01(\t\x12\x18\n\x10\x63ompleted_length\x18\x02 \x01(\t\x12\x13\n\x0bleft_length\x18\x03 \x01(\t\x12\x16\n\x0e\x64ownload_speed\x18\x04 \x01(\t\x12\x0b\n\x03\x65ta\x18\x05 \x01(\t\x12\x10\n\x08progress\x18\x06 \x01(\r\"\xd2\x02\n\x0fTaskStatusReply\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\ntask_title\x18\x02 \x01(\t\x12\x13\n\x0bvideo_badge\x18\x03 \x01(\t\x12\x13\n\x0b\x61udio_badge\x18\x04 \x01(\t\x12\x13\n\x0bvideo_codec\x18\x05 \x01(\t\x12(\n\x08\x61pi_type\x18\x06 \x01(\x0e\x32\x16.jijidown.core.ApiType\x12\x12\n\naudio_only\x18\x07 \x01(\x08\x12\x32\n\x0btask_status\x18\x08 \x01(\x0e\x32\x1d.jijidown.core.TaskStatusType\x12\x10\n\x08\x61\x64\x64_time\x18\t \x01(\x03\x12\x15\n\rcomplete_time\x18\n \x01(\x03\x12-\n\x08progress\x18\x0b \x01(\x0b\x32\x1b.jijidown.core.TaskProgress\x12\x11\n\tsave_path\x18\x0c \x01(\t\"A\n\x0bTaskListReq\x12\x32\n\x0btask_status\x18\x01 \x01(\x0e\x32\x1d.jijidown.core.TaskStatusType\">\n\rTaskListReply\x12-\n\x05tasks\x18\x01 \x03(\x0b\x32\x1e.jijidown.core.TaskStatusReply\"D\n\x0eTaskControlReq\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12!\n\x02\x64o\x18\x02 \x01(\x0e\x32\x15.jijidown.core.TaskDo\"i\n\x15TaskNotificationReply\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x12\n\ntask_title\x18\x02 \x01(\t\x12\x15\n\raverage_speed\x18\x03 \x01(\t\x12\x14\n\x0c\x65lapsed_time\x18\x04 \x01(\t2\xaa\x03\n\x04Task\x12\x38\n\x03New\x12\x19.jijidown.core.TaskNewReq\x1a\x16.google.protobuf.Empty\x12L\n\x08NewBatch\x12\x1e.jijidown.core.TaskNewBatchReq\x1a .jijidown.core.TaskNewBatchReply\x12\x46\n\x06Status\x12\x1c.jijidown.core.TaskStatusReq\x1a\x1e.jijidown.core.TaskStatusReply\x12@\n\x04List\x12\x1a.jijidown.core.TaskListReq\x1a\x1c.jijidown.core.TaskListReply\x12@\n\x07\x43ontrol\x12\x1d.jijidown.core.TaskControlReq\x1a\x16.google.protobuf.Empty\x12N\n\x0cNotification\x12\x16.google.protobuf.Empty\x1a$.jijidown.core.TaskNotificationReply0\x01\x42IZ3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\xaa\x02\x11JiJiDown.Core.SDKb\x06proto3') 22 | 23 | _globals = globals() 24 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 25 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'task_pb2', _globals) 26 | if _descriptor._USE_C_DESCRIPTORS == False: 27 | 28 | DESCRIPTOR._options = None 29 | DESCRIPTOR._serialized_options = b'Z3github.com/JiJiDown/JiJiDownCore-go/common/jijidown\252\002\021JiJiDown.Core.SDK' 30 | _globals['_TASKNEWREQ']._serialized_start=135 31 | _globals['_TASKNEWREQ']._serialized_end=383 32 | _globals['_TASKCREATIONSTATUS']._serialized_start=385 33 | _globals['_TASKCREATIONSTATUS']._serialized_end=436 34 | _globals['_TASKNEWBATCHREPLY']._serialized_start=438 35 | _globals['_TASKNEWBATCHREPLY']._serialized_end=522 36 | _globals['_TASKNEWBATCHREQ']._serialized_start=524 37 | _globals['_TASKNEWBATCHREQ']._serialized_end=587 38 | _globals['_TASKSTATUSREQ']._serialized_start=589 39 | _globals['_TASKSTATUSREQ']._serialized_end=621 40 | _globals['_TASKPROGRESS']._serialized_start=624 41 | _globals['_TASKPROGRESS']._serialized_end=762 42 | _globals['_TASKSTATUSREPLY']._serialized_start=765 43 | _globals['_TASKSTATUSREPLY']._serialized_end=1103 44 | _globals['_TASKLISTREQ']._serialized_start=1105 45 | _globals['_TASKLISTREQ']._serialized_end=1170 46 | _globals['_TASKLISTREPLY']._serialized_start=1172 47 | _globals['_TASKLISTREPLY']._serialized_end=1234 48 | _globals['_TASKCONTROLREQ']._serialized_start=1236 49 | _globals['_TASKCONTROLREQ']._serialized_end=1304 50 | _globals['_TASKNOTIFICATIONREPLY']._serialized_start=1306 51 | _globals['_TASKNOTIFICATIONREPLY']._serialized_end=1411 52 | _globals['_TASK']._serialized_start=1414 53 | _globals['_TASK']._serialized_end=1840 54 | # @@protoc_insertion_point(module_scope) 55 | -------------------------------------------------------------------------------- /grpc_core/bvideo_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | from grpc_core import bvideo_pb2 as bvideo__pb2 6 | 7 | 8 | class BvideoStub(object): 9 | """Bvideo 10 | """ 11 | 12 | def __init__(self, channel): 13 | """Constructor. 14 | 15 | Args: 16 | channel: A grpc.Channel. 17 | """ 18 | self.CheckContent = channel.unary_unary( 19 | '/jijidown.core.Bvideo/CheckContent', 20 | request_serializer=bvideo__pb2.BvideoContentReq.SerializeToString, 21 | response_deserializer=bvideo__pb2.BvideoCheckContentReply.FromString, 22 | ) 23 | self.Info = channel.unary_unary( 24 | '/jijidown.core.Bvideo/Info', 25 | request_serializer=bvideo__pb2.BvideoContentReq.SerializeToString, 26 | response_deserializer=bvideo__pb2.BvideoInfoReply.FromString, 27 | ) 28 | self.AllQuality = channel.unary_unary( 29 | '/jijidown.core.Bvideo/AllQuality', 30 | request_serializer=bvideo__pb2.BvideoAllQualityReq.SerializeToString, 31 | response_deserializer=bvideo__pb2.BvideoAllQualityReply.FromString, 32 | ) 33 | 34 | 35 | class BvideoServicer(object): 36 | """Bvideo 37 | """ 38 | 39 | def CheckContent(self, request, context): 40 | """效验输入的内容, 有效返回 BLinkResult 41 | """ 42 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 43 | context.set_details('Method not implemented!') 44 | raise NotImplementedError('Method not implemented!') 45 | 46 | def Info(self, request, context): 47 | """获取视频信息 48 | """ 49 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 50 | context.set_details('Method not implemented!') 51 | raise NotImplementedError('Method not implemented!') 52 | 53 | def AllQuality(self, request, context): 54 | """查找此视频所有支持的清晰度 55 | """ 56 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 57 | context.set_details('Method not implemented!') 58 | raise NotImplementedError('Method not implemented!') 59 | 60 | 61 | def add_BvideoServicer_to_server(servicer, server): 62 | rpc_method_handlers = { 63 | 'CheckContent': grpc.unary_unary_rpc_method_handler( 64 | servicer.CheckContent, 65 | request_deserializer=bvideo__pb2.BvideoContentReq.FromString, 66 | response_serializer=bvideo__pb2.BvideoCheckContentReply.SerializeToString, 67 | ), 68 | 'Info': grpc.unary_unary_rpc_method_handler( 69 | servicer.Info, 70 | request_deserializer=bvideo__pb2.BvideoContentReq.FromString, 71 | response_serializer=bvideo__pb2.BvideoInfoReply.SerializeToString, 72 | ), 73 | 'AllQuality': grpc.unary_unary_rpc_method_handler( 74 | servicer.AllQuality, 75 | request_deserializer=bvideo__pb2.BvideoAllQualityReq.FromString, 76 | response_serializer=bvideo__pb2.BvideoAllQualityReply.SerializeToString, 77 | ), 78 | } 79 | generic_handler = grpc.method_handlers_generic_handler( 80 | 'jijidown.core.Bvideo', rpc_method_handlers) 81 | server.add_generic_rpc_handlers((generic_handler,)) 82 | 83 | 84 | # This class is part of an EXPERIMENTAL API. 85 | class Bvideo(object): 86 | """Bvideo 87 | """ 88 | 89 | @staticmethod 90 | def CheckContent(request, 91 | target, 92 | options=(), 93 | channel_credentials=None, 94 | call_credentials=None, 95 | insecure=False, 96 | compression=None, 97 | wait_for_ready=None, 98 | timeout=None, 99 | metadata=None): 100 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Bvideo/CheckContent', 101 | bvideo__pb2.BvideoContentReq.SerializeToString, 102 | bvideo__pb2.BvideoCheckContentReply.FromString, 103 | options, channel_credentials, 104 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 105 | 106 | @staticmethod 107 | def Info(request, 108 | target, 109 | options=(), 110 | channel_credentials=None, 111 | call_credentials=None, 112 | insecure=False, 113 | compression=None, 114 | wait_for_ready=None, 115 | timeout=None, 116 | metadata=None): 117 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Bvideo/Info', 118 | bvideo__pb2.BvideoContentReq.SerializeToString, 119 | bvideo__pb2.BvideoInfoReply.FromString, 120 | options, channel_credentials, 121 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 122 | 123 | @staticmethod 124 | def AllQuality(request, 125 | target, 126 | options=(), 127 | channel_credentials=None, 128 | call_credentials=None, 129 | insecure=False, 130 | compression=None, 131 | wait_for_ready=None, 132 | timeout=None, 133 | metadata=None): 134 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Bvideo/AllQuality', 135 | bvideo__pb2.BvideoAllQualityReq.SerializeToString, 136 | bvideo__pb2.BvideoAllQualityReply.FromString, 137 | options, channel_credentials, 138 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 139 | -------------------------------------------------------------------------------- /grpc_core/bvideo_pb2.pyi: -------------------------------------------------------------------------------- 1 | from type import video_pb2 as _video_pb2 2 | from type import api_pb2 as _api_pb2 3 | from type import blinkresult_pb2 as _blinkresult_pb2 4 | from google.protobuf.internal import containers as _containers 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import message as _message 7 | from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union 8 | 9 | DESCRIPTOR: _descriptor.FileDescriptor 10 | 11 | class BvideoContentReq(_message.Message): 12 | __slots__ = ["content"] 13 | CONTENT_FIELD_NUMBER: _ClassVar[int] 14 | content: str 15 | def __init__(self, content: _Optional[str] = ...) -> None: ... 16 | 17 | class BvideoCheckContentReply(_message.Message): 18 | __slots__ = ["is_valid", "blink_result"] 19 | IS_VALID_FIELD_NUMBER: _ClassVar[int] 20 | BLINK_RESULT_FIELD_NUMBER: _ClassVar[int] 21 | is_valid: bool 22 | blink_result: _blinkresult_pb2.BLinkResult 23 | def __init__(self, is_valid: bool = ..., blink_result: _Optional[_Union[_blinkresult_pb2.BLinkResult, _Mapping]] = ...) -> None: ... 24 | 25 | class BvideoInfoReply(_message.Message): 26 | __slots__ = ["blink_result", "video_cover", "video_title", "video_filename", "video_desc", "sub_sort", "sort", "up_name", "up_mid", "up_face", "bili_pubdate_str", "is_stein_gate", "block"] 27 | BLINK_RESULT_FIELD_NUMBER: _ClassVar[int] 28 | VIDEO_COVER_FIELD_NUMBER: _ClassVar[int] 29 | VIDEO_TITLE_FIELD_NUMBER: _ClassVar[int] 30 | VIDEO_FILENAME_FIELD_NUMBER: _ClassVar[int] 31 | VIDEO_DESC_FIELD_NUMBER: _ClassVar[int] 32 | SUB_SORT_FIELD_NUMBER: _ClassVar[int] 33 | SORT_FIELD_NUMBER: _ClassVar[int] 34 | UP_NAME_FIELD_NUMBER: _ClassVar[int] 35 | UP_MID_FIELD_NUMBER: _ClassVar[int] 36 | UP_FACE_FIELD_NUMBER: _ClassVar[int] 37 | BILI_PUBDATE_STR_FIELD_NUMBER: _ClassVar[int] 38 | IS_STEIN_GATE_FIELD_NUMBER: _ClassVar[int] 39 | BLOCK_FIELD_NUMBER: _ClassVar[int] 40 | blink_result: _blinkresult_pb2.BLinkResult 41 | video_cover: bytes 42 | video_title: str 43 | video_filename: str 44 | video_desc: str 45 | sub_sort: str 46 | sort: str 47 | up_name: str 48 | up_mid: int 49 | up_face: bytes 50 | bili_pubdate_str: str 51 | is_stein_gate: bool 52 | block: _containers.RepeatedCompositeFieldContainer[BvideoBlock] 53 | def __init__(self, blink_result: _Optional[_Union[_blinkresult_pb2.BLinkResult, _Mapping]] = ..., video_cover: _Optional[bytes] = ..., video_title: _Optional[str] = ..., video_filename: _Optional[str] = ..., video_desc: _Optional[str] = ..., sub_sort: _Optional[str] = ..., sort: _Optional[str] = ..., up_name: _Optional[str] = ..., up_mid: _Optional[int] = ..., up_face: _Optional[bytes] = ..., bili_pubdate_str: _Optional[str] = ..., is_stein_gate: bool = ..., block: _Optional[_Iterable[_Union[BvideoBlock, _Mapping]]] = ...) -> None: ... 54 | 55 | class BvideoBlock(_message.Message): 56 | __slots__ = ["block_title", "list"] 57 | BLOCK_TITLE_FIELD_NUMBER: _ClassVar[int] 58 | LIST_FIELD_NUMBER: _ClassVar[int] 59 | block_title: str 60 | list: _containers.RepeatedCompositeFieldContainer[BvideoPage] 61 | def __init__(self, block_title: _Optional[str] = ..., list: _Optional[_Iterable[_Union[BvideoPage, _Mapping]]] = ...) -> None: ... 62 | 63 | class BvideoPage(_message.Message): 64 | __slots__ = ["page_av", "page_bv", "page_cid", "page_index", "page_cover", "page_title", "page_info"] 65 | PAGE_AV_FIELD_NUMBER: _ClassVar[int] 66 | PAGE_BV_FIELD_NUMBER: _ClassVar[int] 67 | PAGE_CID_FIELD_NUMBER: _ClassVar[int] 68 | PAGE_INDEX_FIELD_NUMBER: _ClassVar[int] 69 | PAGE_COVER_FIELD_NUMBER: _ClassVar[int] 70 | PAGE_TITLE_FIELD_NUMBER: _ClassVar[int] 71 | PAGE_INFO_FIELD_NUMBER: _ClassVar[int] 72 | page_av: int 73 | page_bv: str 74 | page_cid: int 75 | page_index: int 76 | page_cover: bytes 77 | page_title: str 78 | page_info: _containers.RepeatedScalarFieldContainer[str] 79 | def __init__(self, page_av: _Optional[int] = ..., page_bv: _Optional[str] = ..., page_cid: _Optional[int] = ..., page_index: _Optional[int] = ..., page_cover: _Optional[bytes] = ..., page_title: _Optional[str] = ..., page_info: _Optional[_Iterable[str]] = ...) -> None: ... 80 | 81 | class BvideoAllQualityReq(_message.Message): 82 | __slots__ = ["aid", "bvid", "cid"] 83 | AID_FIELD_NUMBER: _ClassVar[int] 84 | BVID_FIELD_NUMBER: _ClassVar[int] 85 | CID_FIELD_NUMBER: _ClassVar[int] 86 | aid: int 87 | bvid: str 88 | cid: int 89 | def __init__(self, aid: _Optional[int] = ..., bvid: _Optional[str] = ..., cid: _Optional[int] = ...) -> None: ... 90 | 91 | class BvideoBVideoItem(_message.Message): 92 | __slots__ = ["quality_id", "quality_text", "codec", "frame_rate", "bit_rate", "stream_size", "api_type"] 93 | QUALITY_ID_FIELD_NUMBER: _ClassVar[int] 94 | QUALITY_TEXT_FIELD_NUMBER: _ClassVar[int] 95 | CODEC_FIELD_NUMBER: _ClassVar[int] 96 | FRAME_RATE_FIELD_NUMBER: _ClassVar[int] 97 | BIT_RATE_FIELD_NUMBER: _ClassVar[int] 98 | STREAM_SIZE_FIELD_NUMBER: _ClassVar[int] 99 | API_TYPE_FIELD_NUMBER: _ClassVar[int] 100 | quality_id: int 101 | quality_text: str 102 | codec: _video_pb2.VideoType 103 | frame_rate: str 104 | bit_rate: str 105 | stream_size: str 106 | api_type: _api_pb2.ApiType 107 | def __init__(self, quality_id: _Optional[int] = ..., quality_text: _Optional[str] = ..., codec: _Optional[_Union[_video_pb2.VideoType, str]] = ..., frame_rate: _Optional[str] = ..., bit_rate: _Optional[str] = ..., stream_size: _Optional[str] = ..., api_type: _Optional[_Union[_api_pb2.ApiType, str]] = ...) -> None: ... 108 | 109 | class BvideoBAudioItem(_message.Message): 110 | __slots__ = ["quality_id", "quality_text", "codec_text", "bit_rate", "stream_size", "api_type"] 111 | QUALITY_ID_FIELD_NUMBER: _ClassVar[int] 112 | QUALITY_TEXT_FIELD_NUMBER: _ClassVar[int] 113 | CODEC_TEXT_FIELD_NUMBER: _ClassVar[int] 114 | BIT_RATE_FIELD_NUMBER: _ClassVar[int] 115 | STREAM_SIZE_FIELD_NUMBER: _ClassVar[int] 116 | API_TYPE_FIELD_NUMBER: _ClassVar[int] 117 | quality_id: int 118 | quality_text: str 119 | codec_text: str 120 | bit_rate: str 121 | stream_size: str 122 | api_type: _api_pb2.ApiType 123 | def __init__(self, quality_id: _Optional[int] = ..., quality_text: _Optional[str] = ..., codec_text: _Optional[str] = ..., bit_rate: _Optional[str] = ..., stream_size: _Optional[str] = ..., api_type: _Optional[_Union[_api_pb2.ApiType, str]] = ...) -> None: ... 124 | 125 | class BvideoAllQualityReply(_message.Message): 126 | __slots__ = ["video", "audio"] 127 | VIDEO_FIELD_NUMBER: _ClassVar[int] 128 | AUDIO_FIELD_NUMBER: _ClassVar[int] 129 | video: _containers.RepeatedCompositeFieldContainer[BvideoBVideoItem] 130 | audio: _containers.RepeatedCompositeFieldContainer[BvideoBAudioItem] 131 | def __init__(self, video: _Optional[_Iterable[_Union[BvideoBVideoItem, _Mapping]]] = ..., audio: _Optional[_Iterable[_Union[BvideoBAudioItem, _Mapping]]] = ...) -> None: ... 132 | -------------------------------------------------------------------------------- /grpc_core/task_pb2.pyi: -------------------------------------------------------------------------------- 1 | from google.protobuf import empty_pb2 as _empty_pb2 2 | from type import api_pb2 as _api_pb2 3 | from type import video_pb2 as _video_pb2 4 | from type import taskstatus_pb2 as _taskstatus_pb2 5 | from type import taskdo_pb2 as _taskdo_pb2 6 | from google.protobuf.internal import containers as _containers 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import message as _message 9 | from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union 10 | 11 | DESCRIPTOR: _descriptor.FileDescriptor 12 | 13 | class TaskNewReq(_message.Message): 14 | __slots__ = ["aid", "bvid", "cid", "video_quality", "audio_quality", "video_codec", "api_type", "save_filename", "audio_only", "callback"] 15 | AID_FIELD_NUMBER: _ClassVar[int] 16 | BVID_FIELD_NUMBER: _ClassVar[int] 17 | CID_FIELD_NUMBER: _ClassVar[int] 18 | VIDEO_QUALITY_FIELD_NUMBER: _ClassVar[int] 19 | AUDIO_QUALITY_FIELD_NUMBER: _ClassVar[int] 20 | VIDEO_CODEC_FIELD_NUMBER: _ClassVar[int] 21 | API_TYPE_FIELD_NUMBER: _ClassVar[int] 22 | SAVE_FILENAME_FIELD_NUMBER: _ClassVar[int] 23 | AUDIO_ONLY_FIELD_NUMBER: _ClassVar[int] 24 | CALLBACK_FIELD_NUMBER: _ClassVar[int] 25 | aid: int 26 | bvid: str 27 | cid: int 28 | video_quality: int 29 | audio_quality: int 30 | video_codec: _video_pb2.VideoType 31 | api_type: _api_pb2.ApiType 32 | save_filename: str 33 | audio_only: bool 34 | callback: int 35 | def __init__(self, aid: _Optional[int] = ..., bvid: _Optional[str] = ..., cid: _Optional[int] = ..., video_quality: _Optional[int] = ..., audio_quality: _Optional[int] = ..., video_codec: _Optional[_Union[_video_pb2.VideoType, str]] = ..., api_type: _Optional[_Union[_api_pb2.ApiType, str]] = ..., save_filename: _Optional[str] = ..., audio_only: bool = ..., callback: _Optional[int] = ...) -> None: ... 36 | 37 | class TaskCreationStatus(_message.Message): 38 | __slots__ = ["callback", "err"] 39 | CALLBACK_FIELD_NUMBER: _ClassVar[int] 40 | ERR_FIELD_NUMBER: _ClassVar[int] 41 | callback: int 42 | err: str 43 | def __init__(self, callback: _Optional[int] = ..., err: _Optional[str] = ...) -> None: ... 44 | 45 | class TaskNewBatchReply(_message.Message): 46 | __slots__ = ["task_creation_status"] 47 | TASK_CREATION_STATUS_FIELD_NUMBER: _ClassVar[int] 48 | task_creation_status: _containers.RepeatedCompositeFieldContainer[TaskCreationStatus] 49 | def __init__(self, task_creation_status: _Optional[_Iterable[_Union[TaskCreationStatus, _Mapping]]] = ...) -> None: ... 50 | 51 | class TaskNewBatchReq(_message.Message): 52 | __slots__ = ["new_tasks"] 53 | NEW_TASKS_FIELD_NUMBER: _ClassVar[int] 54 | new_tasks: _containers.RepeatedCompositeFieldContainer[TaskNewReq] 55 | def __init__(self, new_tasks: _Optional[_Iterable[_Union[TaskNewReq, _Mapping]]] = ...) -> None: ... 56 | 57 | class TaskStatusReq(_message.Message): 58 | __slots__ = ["task_id"] 59 | TASK_ID_FIELD_NUMBER: _ClassVar[int] 60 | task_id: str 61 | def __init__(self, task_id: _Optional[str] = ...) -> None: ... 62 | 63 | class TaskProgress(_message.Message): 64 | __slots__ = ["total_length", "completed_length", "left_length", "download_speed", "eta", "progress"] 65 | TOTAL_LENGTH_FIELD_NUMBER: _ClassVar[int] 66 | COMPLETED_LENGTH_FIELD_NUMBER: _ClassVar[int] 67 | LEFT_LENGTH_FIELD_NUMBER: _ClassVar[int] 68 | DOWNLOAD_SPEED_FIELD_NUMBER: _ClassVar[int] 69 | ETA_FIELD_NUMBER: _ClassVar[int] 70 | PROGRESS_FIELD_NUMBER: _ClassVar[int] 71 | total_length: str 72 | completed_length: str 73 | left_length: str 74 | download_speed: str 75 | eta: str 76 | progress: int 77 | def __init__(self, total_length: _Optional[str] = ..., completed_length: _Optional[str] = ..., left_length: _Optional[str] = ..., download_speed: _Optional[str] = ..., eta: _Optional[str] = ..., progress: _Optional[int] = ...) -> None: ... 78 | 79 | class TaskStatusReply(_message.Message): 80 | __slots__ = ["task_id", "task_title", "video_badge", "audio_badge", "video_codec", "api_type", "audio_only", "task_status", "add_time", "complete_time", "progress", "save_path"] 81 | TASK_ID_FIELD_NUMBER: _ClassVar[int] 82 | TASK_TITLE_FIELD_NUMBER: _ClassVar[int] 83 | VIDEO_BADGE_FIELD_NUMBER: _ClassVar[int] 84 | AUDIO_BADGE_FIELD_NUMBER: _ClassVar[int] 85 | VIDEO_CODEC_FIELD_NUMBER: _ClassVar[int] 86 | API_TYPE_FIELD_NUMBER: _ClassVar[int] 87 | AUDIO_ONLY_FIELD_NUMBER: _ClassVar[int] 88 | TASK_STATUS_FIELD_NUMBER: _ClassVar[int] 89 | ADD_TIME_FIELD_NUMBER: _ClassVar[int] 90 | COMPLETE_TIME_FIELD_NUMBER: _ClassVar[int] 91 | PROGRESS_FIELD_NUMBER: _ClassVar[int] 92 | SAVE_PATH_FIELD_NUMBER: _ClassVar[int] 93 | task_id: str 94 | task_title: str 95 | video_badge: str 96 | audio_badge: str 97 | video_codec: str 98 | api_type: _api_pb2.ApiType 99 | audio_only: bool 100 | task_status: _taskstatus_pb2.TaskStatusType 101 | add_time: int 102 | complete_time: int 103 | progress: TaskProgress 104 | save_path: str 105 | def __init__(self, task_id: _Optional[str] = ..., task_title: _Optional[str] = ..., video_badge: _Optional[str] = ..., audio_badge: _Optional[str] = ..., video_codec: _Optional[str] = ..., api_type: _Optional[_Union[_api_pb2.ApiType, str]] = ..., audio_only: bool = ..., task_status: _Optional[_Union[_taskstatus_pb2.TaskStatusType, str]] = ..., add_time: _Optional[int] = ..., complete_time: _Optional[int] = ..., progress: _Optional[_Union[TaskProgress, _Mapping]] = ..., save_path: _Optional[str] = ...) -> None: ... 106 | 107 | class TaskListReq(_message.Message): 108 | __slots__ = ["task_status"] 109 | TASK_STATUS_FIELD_NUMBER: _ClassVar[int] 110 | task_status: _taskstatus_pb2.TaskStatusType 111 | def __init__(self, task_status: _Optional[_Union[_taskstatus_pb2.TaskStatusType, str]] = ...) -> None: ... 112 | 113 | class TaskListReply(_message.Message): 114 | __slots__ = ["tasks"] 115 | TASKS_FIELD_NUMBER: _ClassVar[int] 116 | tasks: _containers.RepeatedCompositeFieldContainer[TaskStatusReply] 117 | def __init__(self, tasks: _Optional[_Iterable[_Union[TaskStatusReply, _Mapping]]] = ...) -> None: ... 118 | 119 | class TaskControlReq(_message.Message): 120 | __slots__ = ["task_id", "do"] 121 | TASK_ID_FIELD_NUMBER: _ClassVar[int] 122 | DO_FIELD_NUMBER: _ClassVar[int] 123 | task_id: str 124 | do: _taskdo_pb2.TaskDo 125 | def __init__(self, task_id: _Optional[str] = ..., do: _Optional[_Union[_taskdo_pb2.TaskDo, str]] = ...) -> None: ... 126 | 127 | class TaskNotificationReply(_message.Message): 128 | __slots__ = ["task_id", "task_title", "average_speed", "elapsed_time"] 129 | TASK_ID_FIELD_NUMBER: _ClassVar[int] 130 | TASK_TITLE_FIELD_NUMBER: _ClassVar[int] 131 | AVERAGE_SPEED_FIELD_NUMBER: _ClassVar[int] 132 | ELAPSED_TIME_FIELD_NUMBER: _ClassVar[int] 133 | task_id: str 134 | task_title: str 135 | average_speed: str 136 | elapsed_time: str 137 | def __init__(self, task_id: _Optional[str] = ..., task_title: _Optional[str] = ..., average_speed: _Optional[str] = ..., elapsed_time: _Optional[str] = ...) -> None: ... 138 | -------------------------------------------------------------------------------- /grpc_core/user_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 6 | from grpc_core import user_pb2 as user__pb2 7 | 8 | 9 | class UserStub(object): 10 | """User 11 | """ 12 | 13 | def __init__(self, channel): 14 | """Constructor. 15 | 16 | Args: 17 | channel: A grpc.Channel. 18 | """ 19 | self.Info = channel.unary_unary( 20 | '/jijidown.core.User/Info', 21 | request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 22 | response_deserializer=user__pb2.UserInfoReply.FromString, 23 | ) 24 | self.LoginQRCode = channel.unary_unary( 25 | '/jijidown.core.User/LoginQRCode', 26 | request_serializer=user__pb2.UserLoginQRCodeReq.SerializeToString, 27 | response_deserializer=user__pb2.UserLoginQRCodeReply.FromString, 28 | ) 29 | self.LoginStatus = channel.unary_stream( 30 | '/jijidown.core.User/LoginStatus', 31 | request_serializer=user__pb2.UserLoginStatusReq.SerializeToString, 32 | response_deserializer=user__pb2.UserLoginStatusReply.FromString, 33 | ) 34 | self.ImportCookie = channel.unary_unary( 35 | '/jijidown.core.User/ImportCookie', 36 | request_serializer=user__pb2.UserImportCookieReq.SerializeToString, 37 | response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 38 | ) 39 | 40 | 41 | class UserServicer(object): 42 | """User 43 | """ 44 | 45 | def Info(self, request, context): 46 | """获取用户信息 47 | """ 48 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 49 | context.set_details('Method not implemented!') 50 | raise NotImplementedError('Method not implemented!') 51 | 52 | def LoginQRCode(self, request, context): 53 | """获取登录二维码 54 | """ 55 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 56 | context.set_details('Method not implemented!') 57 | raise NotImplementedError('Method not implemented!') 58 | 59 | def LoginStatus(self, request, context): 60 | """获取登录状态 61 | """ 62 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 63 | context.set_details('Method not implemented!') 64 | raise NotImplementedError('Method not implemented!') 65 | 66 | def ImportCookie(self, request, context): 67 | """导入 Cookie 68 | """ 69 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 70 | context.set_details('Method not implemented!') 71 | raise NotImplementedError('Method not implemented!') 72 | 73 | 74 | def add_UserServicer_to_server(servicer, server): 75 | rpc_method_handlers = { 76 | 'Info': grpc.unary_unary_rpc_method_handler( 77 | servicer.Info, 78 | request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 79 | response_serializer=user__pb2.UserInfoReply.SerializeToString, 80 | ), 81 | 'LoginQRCode': grpc.unary_unary_rpc_method_handler( 82 | servicer.LoginQRCode, 83 | request_deserializer=user__pb2.UserLoginQRCodeReq.FromString, 84 | response_serializer=user__pb2.UserLoginQRCodeReply.SerializeToString, 85 | ), 86 | 'LoginStatus': grpc.unary_stream_rpc_method_handler( 87 | servicer.LoginStatus, 88 | request_deserializer=user__pb2.UserLoginStatusReq.FromString, 89 | response_serializer=user__pb2.UserLoginStatusReply.SerializeToString, 90 | ), 91 | 'ImportCookie': grpc.unary_unary_rpc_method_handler( 92 | servicer.ImportCookie, 93 | request_deserializer=user__pb2.UserImportCookieReq.FromString, 94 | response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 95 | ), 96 | } 97 | generic_handler = grpc.method_handlers_generic_handler( 98 | 'jijidown.core.User', rpc_method_handlers) 99 | server.add_generic_rpc_handlers((generic_handler,)) 100 | 101 | 102 | # This class is part of an EXPERIMENTAL API. 103 | class User(object): 104 | """User 105 | """ 106 | 107 | @staticmethod 108 | def Info(request, 109 | target, 110 | options=(), 111 | channel_credentials=None, 112 | call_credentials=None, 113 | insecure=False, 114 | compression=None, 115 | wait_for_ready=None, 116 | timeout=None, 117 | metadata=None): 118 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.User/Info', 119 | google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 120 | user__pb2.UserInfoReply.FromString, 121 | options, channel_credentials, 122 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 123 | 124 | @staticmethod 125 | def LoginQRCode(request, 126 | target, 127 | options=(), 128 | channel_credentials=None, 129 | call_credentials=None, 130 | insecure=False, 131 | compression=None, 132 | wait_for_ready=None, 133 | timeout=None, 134 | metadata=None): 135 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.User/LoginQRCode', 136 | user__pb2.UserLoginQRCodeReq.SerializeToString, 137 | user__pb2.UserLoginQRCodeReply.FromString, 138 | options, channel_credentials, 139 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 140 | 141 | @staticmethod 142 | def LoginStatus(request, 143 | target, 144 | options=(), 145 | channel_credentials=None, 146 | call_credentials=None, 147 | insecure=False, 148 | compression=None, 149 | wait_for_ready=None, 150 | timeout=None, 151 | metadata=None): 152 | return grpc.experimental.unary_stream(request, target, '/jijidown.core.User/LoginStatus', 153 | user__pb2.UserLoginStatusReq.SerializeToString, 154 | user__pb2.UserLoginStatusReply.FromString, 155 | options, channel_credentials, 156 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 157 | 158 | @staticmethod 159 | def ImportCookie(request, 160 | target, 161 | options=(), 162 | channel_credentials=None, 163 | call_credentials=None, 164 | insecure=False, 165 | compression=None, 166 | wait_for_ready=None, 167 | timeout=None, 168 | metadata=None): 169 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.User/ImportCookie', 170 | user__pb2.UserImportCookieReq.SerializeToString, 171 | google_dot_protobuf_dot_empty__pb2.Empty.FromString, 172 | options, channel_credentials, 173 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 174 | -------------------------------------------------------------------------------- /grpc_core/task_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 6 | import task_pb2 as task__pb2 7 | 8 | 9 | class TaskStub(object): 10 | """Task 11 | """ 12 | 13 | def __init__(self, channel): 14 | """Constructor. 15 | 16 | Args: 17 | channel: A grpc.Channel. 18 | """ 19 | self.New = channel.unary_unary( 20 | '/jijidown.core.Task/New', 21 | request_serializer=task__pb2.TaskNewReq.SerializeToString, 22 | response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 23 | ) 24 | self.NewBatch = channel.unary_unary( 25 | '/jijidown.core.Task/NewBatch', 26 | request_serializer=task__pb2.TaskNewBatchReq.SerializeToString, 27 | response_deserializer=task__pb2.TaskNewBatchReply.FromString, 28 | ) 29 | self.Status = channel.unary_unary( 30 | '/jijidown.core.Task/Status', 31 | request_serializer=task__pb2.TaskStatusReq.SerializeToString, 32 | response_deserializer=task__pb2.TaskStatusReply.FromString, 33 | ) 34 | self.List = channel.unary_unary( 35 | '/jijidown.core.Task/List', 36 | request_serializer=task__pb2.TaskListReq.SerializeToString, 37 | response_deserializer=task__pb2.TaskListReply.FromString, 38 | ) 39 | self.Control = channel.unary_unary( 40 | '/jijidown.core.Task/Control', 41 | request_serializer=task__pb2.TaskControlReq.SerializeToString, 42 | response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 43 | ) 44 | self.Notification = channel.unary_stream( 45 | '/jijidown.core.Task/Notification', 46 | request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 47 | response_deserializer=task__pb2.TaskNotificationReply.FromString, 48 | ) 49 | 50 | 51 | class TaskServicer(object): 52 | """Task 53 | """ 54 | 55 | def New(self, request, context): 56 | """创建新任务 57 | """ 58 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 59 | context.set_details('Method not implemented!') 60 | raise NotImplementedError('Method not implemented!') 61 | 62 | def NewBatch(self, request, context): 63 | """批量创建新任务 64 | """ 65 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 66 | context.set_details('Method not implemented!') 67 | raise NotImplementedError('Method not implemented!') 68 | 69 | def Status(self, request, context): 70 | """任务状态 71 | """ 72 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 73 | context.set_details('Method not implemented!') 74 | raise NotImplementedError('Method not implemented!') 75 | 76 | def List(self, request, context): 77 | """获取任务列表 78 | """ 79 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 80 | context.set_details('Method not implemented!') 81 | raise NotImplementedError('Method not implemented!') 82 | 83 | def Control(self, request, context): 84 | """任务控制 (暂停, 继续, 删除) 85 | """ 86 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 87 | context.set_details('Method not implemented!') 88 | raise NotImplementedError('Method not implemented!') 89 | 90 | def Notification(self, request, context): 91 | """任务完成通知 92 | """ 93 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 94 | context.set_details('Method not implemented!') 95 | raise NotImplementedError('Method not implemented!') 96 | 97 | 98 | def add_TaskServicer_to_server(servicer, server): 99 | rpc_method_handlers = { 100 | 'New': grpc.unary_unary_rpc_method_handler( 101 | servicer.New, 102 | request_deserializer=task__pb2.TaskNewReq.FromString, 103 | response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 104 | ), 105 | 'NewBatch': grpc.unary_unary_rpc_method_handler( 106 | servicer.NewBatch, 107 | request_deserializer=task__pb2.TaskNewBatchReq.FromString, 108 | response_serializer=task__pb2.TaskNewBatchReply.SerializeToString, 109 | ), 110 | 'Status': grpc.unary_unary_rpc_method_handler( 111 | servicer.Status, 112 | request_deserializer=task__pb2.TaskStatusReq.FromString, 113 | response_serializer=task__pb2.TaskStatusReply.SerializeToString, 114 | ), 115 | 'List': grpc.unary_unary_rpc_method_handler( 116 | servicer.List, 117 | request_deserializer=task__pb2.TaskListReq.FromString, 118 | response_serializer=task__pb2.TaskListReply.SerializeToString, 119 | ), 120 | 'Control': grpc.unary_unary_rpc_method_handler( 121 | servicer.Control, 122 | request_deserializer=task__pb2.TaskControlReq.FromString, 123 | response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 124 | ), 125 | 'Notification': grpc.unary_stream_rpc_method_handler( 126 | servicer.Notification, 127 | request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, 128 | response_serializer=task__pb2.TaskNotificationReply.SerializeToString, 129 | ), 130 | } 131 | generic_handler = grpc.method_handlers_generic_handler( 132 | 'jijidown.core.Task', rpc_method_handlers) 133 | server.add_generic_rpc_handlers((generic_handler,)) 134 | 135 | 136 | # This class is part of an EXPERIMENTAL API. 137 | class Task(object): 138 | """Task 139 | """ 140 | 141 | @staticmethod 142 | def New(request, 143 | target, 144 | options=(), 145 | channel_credentials=None, 146 | call_credentials=None, 147 | insecure=False, 148 | compression=None, 149 | wait_for_ready=None, 150 | timeout=None, 151 | metadata=None): 152 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Task/New', 153 | task__pb2.TaskNewReq.SerializeToString, 154 | google_dot_protobuf_dot_empty__pb2.Empty.FromString, 155 | options, channel_credentials, 156 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 157 | 158 | @staticmethod 159 | def NewBatch(request, 160 | target, 161 | options=(), 162 | channel_credentials=None, 163 | call_credentials=None, 164 | insecure=False, 165 | compression=None, 166 | wait_for_ready=None, 167 | timeout=None, 168 | metadata=None): 169 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Task/NewBatch', 170 | task__pb2.TaskNewBatchReq.SerializeToString, 171 | task__pb2.TaskNewBatchReply.FromString, 172 | options, channel_credentials, 173 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 174 | 175 | @staticmethod 176 | def Status(request, 177 | target, 178 | options=(), 179 | channel_credentials=None, 180 | call_credentials=None, 181 | insecure=False, 182 | compression=None, 183 | wait_for_ready=None, 184 | timeout=None, 185 | metadata=None): 186 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Task/Status', 187 | task__pb2.TaskStatusReq.SerializeToString, 188 | task__pb2.TaskStatusReply.FromString, 189 | options, channel_credentials, 190 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 191 | 192 | @staticmethod 193 | def List(request, 194 | target, 195 | options=(), 196 | channel_credentials=None, 197 | call_credentials=None, 198 | insecure=False, 199 | compression=None, 200 | wait_for_ready=None, 201 | timeout=None, 202 | metadata=None): 203 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Task/List', 204 | task__pb2.TaskListReq.SerializeToString, 205 | task__pb2.TaskListReply.FromString, 206 | options, channel_credentials, 207 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 208 | 209 | @staticmethod 210 | def Control(request, 211 | target, 212 | options=(), 213 | channel_credentials=None, 214 | call_credentials=None, 215 | insecure=False, 216 | compression=None, 217 | wait_for_ready=None, 218 | timeout=None, 219 | metadata=None): 220 | return grpc.experimental.unary_unary(request, target, '/jijidown.core.Task/Control', 221 | task__pb2.TaskControlReq.SerializeToString, 222 | google_dot_protobuf_dot_empty__pb2.Empty.FromString, 223 | options, channel_credentials, 224 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 225 | 226 | @staticmethod 227 | def Notification(request, 228 | target, 229 | options=(), 230 | channel_credentials=None, 231 | call_credentials=None, 232 | insecure=False, 233 | compression=None, 234 | wait_for_ready=None, 235 | timeout=None, 236 | metadata=None): 237 | return grpc.experimental.unary_stream(request, target, '/jijidown.core.Task/Notification', 238 | google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, 239 | task__pb2.TaskNotificationReply.FromString, 240 | options, channel_credentials, 241 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 242 | -------------------------------------------------------------------------------- /old/core.py.bak: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import time 4 | from base64 import b64decode # 二维码编码 5 | from pathlib import Path #路径库 6 | import hashlib#sha256加密库 7 | 8 | import wget 9 | import requests 10 | 11 | # 初始化 12 | local_core = str(Path('resources/JiJiDownCore-win64.exe').resolve()) 13 | local_time = time.strftime("%y/%m/%d", time.localtime()) 14 | brower = requests.Session() 15 | headers = { 16 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36' 17 | } 18 | 19 | base_url = "http://127.0.0.1:64000" # 默认端口 20 | local_dir = str(Path.cwd()) # 默认下载地址 21 | appdata = os.getenv('APPDATA') # 获取系统变量 22 | Path(local_dir+'/resources').mkdir(parents=True,exist_ok=True) # 创建核心文件夹 23 | Path(local_dir+'/temp').mkdir(parents=True,exist_ok=True)# 创建临时文件夹 24 | Path(appdata+'/JiJiDown').mkdir(parents=True,exist_ok=True)# 创建jijidown配置文件夹 25 | 26 | 27 | #检查核心是否启动 28 | def check() -> str: 29 | """ 30 | 检查核心是否启动 31 | """ 32 | try: 33 | # cj = {i.split("=")[0]:i.split("=")[1] for i in cookies.split(";")} 34 | response: dict = requests.get(url=base_url+"/jijidown/settings/get_download_dir", headers={ 35 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", 36 | "accept": "application/json, text/plain, */*", 37 | "accept-encoding": "gzip, deflate, br", 38 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 39 | "origin": "https://space.bilibili.com" 40 | },timeout=5) 41 | if response.status_code == 200: 42 | return 'ok' 43 | return 'error' 44 | except: 45 | return 'error' 46 | 47 | #添加编码信息 48 | def code(keys): 49 | if keys['codec'] == 0: 50 | keys['codec'] = 'NONE' 51 | if keys['codec'] == 1: 52 | keys['codec'] = 'H264' 53 | if keys['codec'] == 2: 54 | keys['codec'] = 'H265' 55 | if keys['codec'] == 3: 56 | keys['codec'] = 'AV1' 57 | if keys['codec'] == 4: 58 | keys['codec'] = 'M4A' 59 | if keys['codec'] == 5: 60 | keys['codec'] = 'DOLBY' 61 | return keys 62 | 63 | #列表排序 64 | def dic(info): 65 | new_list = []#创建临时列表 66 | for a in info: 67 | new_list.append(a["quality"]) 68 | new_list.sort(reverse = True)#列表排序从大到小 69 | new_info = [] 70 | for b in new_list: 71 | for c in info: 72 | if c["quality"] == b: 73 | new_info.append(c) 74 | return new_info 75 | 76 | # 浏览器请求函数 77 | def get(url: str) -> dict: 78 | """ 79 | 请求数据 80 | """ 81 | try: 82 | # cj = {i.split("=")[0]:i.split("=")[1] for i in cookies.split(";")} 83 | response: dict = brower.get(url=url, headers={ 84 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", 85 | "accept": "application/json, text/plain, */*", 86 | "accept-encoding": "gzip, deflate, br", 87 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 88 | "origin": "https://space.bilibili.com" 89 | }).json() 90 | return response 91 | except: 92 | print('核心未启动,尝试重新访问核心') 93 | time.sleep(1) 94 | get(url) 95 | 96 | # 浏览器发送函数 97 | def post(url: str, json: str) -> dict: 98 | """ 99 | 发送json格式信息 100 | """ 101 | response = brower.post(url=url, json=json, headers={ 102 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", 103 | "accept": "application/json, text/plain, */*", 104 | "accept-encoding": "gzip, deflate, br", 105 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 106 | "origin": "https://space.bilibili.com" 107 | }).json() 108 | return response 109 | 110 | # 浏览器发送函数 111 | def patch(url: str) -> dict: 112 | """ 113 | 发送patch信息 114 | """ 115 | response = brower.patch(url=url, headers={ 116 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", 117 | "accept": "application/json, text/plain, */*", 118 | "accept-encoding": "gzip, deflate, br", 119 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 120 | "origin": "https://space.bilibili.com" 121 | }).json() 122 | return response 123 | 124 | def delete(url: str) -> dict: 125 | """ 126 | 发送patch信息 127 | """ 128 | response = brower.delete(url=url, headers={ 129 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", 130 | "accept": "application/json, text/plain, */*", 131 | "accept-encoding": "gzip, deflate, br", 132 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 133 | "origin": "https://space.bilibili.com" 134 | }).json() 135 | return response 136 | ############################################################ User层 137 | 138 | # 获取登录状态 139 | def get_user_info() -> dict: 140 | """ 141 | 获取用户登陆状态 142 | 如果未登录 code = bool False 143 | mid = int 用户mid 144 | is_login = bool 登陆状态 145 | uname = str 用户名 146 | face = str 用户头像url 147 | vip_status = bool 大会员登陆状态 148 | vip_label_text = str 大会员状态 149 | """ 150 | data: dict = get(base_url+'/bili/user/get_user_info') 151 | if data['message'] == "no login": 152 | return {'code': False} # 返回状态 153 | user_data = data['data'] 154 | mid: int = user_data['mid'] # 用户mid 155 | is_login: bool = user_data['is_login'] # 登录状态 156 | uname: str = user_data['uname'] # 用户名 157 | face: str = user_data['face'] # 用户头像 158 | vip_status: bool = user_data['vip_status'] # 大会员登录状态 159 | vip_label_text: str = user_data['vip_label_text'] # 大会员状态(年度大会员等) 160 | return {'code': True, 'mid': mid, 'is_login': is_login, 'uname': uname, 'face': face, 'vip_status': vip_status, 'vip_label_text': vip_label_text} 161 | 162 | # 获取登录二维码 163 | def get_login_status() -> dict: 164 | """ 165 | 获取登录二维码 166 | code = 0为已登录 167 | 返回{'code','image'} 168 | """ 169 | data = get(base_url+'/bili/user/tv/get_login_status') 170 | login_data = data['data'] 171 | login_successful: bool = login_data['login_successful'] # 是否已登录 172 | login_image = login_data['image'] # 登录用二维码 173 | if login_successful != True: 174 | login_image = b64decode(login_image) # 转换为图片 175 | return {'code': 1,'image':login_image} 176 | return {'code': 0} 177 | 178 | ############################################################ Jiji层 179 | 180 | # 获取服务器sha265 181 | def get_sha265() -> dict: 182 | """ 183 | 获取最新的服务器上存储的sha265 184 | """ 185 | cloud_hash = brower.get('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-hash.txt').text#获取云端最新核心信息 186 | cloud_hash = cloud_hash.split() 187 | fin = [] 188 | for a in cloud_hash: 189 | a = a.split('|') 190 | fin.append(a) 191 | return fin 192 | 193 | # 检查是否存在核心 194 | def find_core(): 195 | dir_path = Path('resources/').iterdir() 196 | fin = [] 197 | for i in dir_path: 198 | fin.append(i.name) 199 | return fin 200 | 201 | # 下载最新版本 202 | def down_core(system_type:str,system_bit:str) -> str: 203 | """ 204 | 自动匹配平台下载核心 205 | 返回下载的核心文件名 206 | """ 207 | if system_type == 'Windows':#如果平台为windows 208 | if system_bit == 'AMD64':#如果系统位数为64 209 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-win64.exe',out=str(Path('resources/'))) 210 | return core_name 211 | elif system_bit == 'x86': 212 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-win32.exe',out=str(Path('resources/'))) 213 | return core_name 214 | elif system_bit == 'i386': 215 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-win32.exe',out=str(Path('resources/'))) 216 | return core_name 217 | elif system_type == 'Linux': 218 | if system_bit == 'AMD64':#如果系统为x86平台 219 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-linux-amd64',out=str(Path('resources/'))) 220 | return core_name 221 | elif system_bit == 'aarch64': 222 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-linux-arm64',out=str(Path('resources/'))) 223 | #如果均不匹配下载amd64版本 224 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-linux-amd64',out=str(Path('resources/'))) 225 | return core_name 226 | elif system_type == 'darwin': 227 | if system_bit == 'amd64':#如果系统位数为64 228 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-darwin-amd64',out=str(Path('resources/'))) 229 | return core_name 230 | elif system_bit == 'arm64': 231 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-darwin-arm64',out=str(Path('resources/'))) 232 | #如果均不匹配下载amd64版本 233 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/JiJiDownCore-darwin-amd64',out=str(Path('resources/'))) 234 | return core_name 235 | return 'error' 236 | 237 | # 更新核心 238 | def update_core(system_type:str,system_bit:str) -> str: 239 | """ 240 | 核心更新函数 241 | """ 242 | global local_core#使用全局变量 243 | 244 | local_core_list = find_core()#获取本地文件夹列表 245 | b = [] 246 | for a in local_core_list:#查询本地核心是否存在 247 | if 'JiJiDownCore-' in a : 248 | b.append(a) 249 | if len(b) > 1:#如果存在多于一个的核心 250 | for a in b:#全部删除 251 | Path.unlink(Path('resources/'+a)) 252 | elif len(b) == 1:#如果只有一个,设置本地核心路径为那一个 253 | local_core = Path('resources/'+b[0]) 254 | if Path(local_core).exists():#如果只有一个 255 | with open(local_core,'rb') as f:#获取本地核心sha265 256 | sha265 = hashlib.sha256(f.read()).hexdigest() 257 | cloud_sha265 = get_sha265()#获取云端sha265 258 | for a in cloud_sha265:#对比sha265 259 | if a[0] == sha265: 260 | return '目前是最新版本' 261 | for a in cloud_sha265:#sha265不匹配就查找相同名称的最新核心进行替换 262 | if a[-1] == str(Path(local_core).name): 263 | Path.unlink(local_core)#删除旧核心 264 | core_name = wget.download('https://101.34.172.63/PC/ReWPF/core/'+str(Path(local_core).name),out=str(Path('resources/'))) 265 | return core_name+'已更新完成' 266 | else:#如果没有核心 267 | return_data = down_core(system_type,system_bit)#按平台下载核心 268 | return return_data+'自动下载完成' 269 | 270 | # 生成配置文件 271 | def make_yaml(): 272 | if not Path(appdata+'/JiJiDown/config.yaml').exists(): 273 | with open(str(Path(appdata+'/JiJiDown/config.yaml')),'w') as f:#写入设置文件 274 | f.write("""portable: false 275 | log-level: debug 276 | external-controller: 127.0.0.1:64000 277 | external-ui: "" 278 | secret: "" 279 | user-info: 280 | raw-access-token: "" 281 | raw-cookies: "" 282 | hide-nickname: false 283 | download-task: 284 | temp-dir: "" 285 | download-dir: """+local_dir+""" 286 | ffmpeg-path: "" 287 | max-task: 2 288 | download-speed-limit: 1 289 | disable-mcdn: false 290 | jdm: 291 | max-retry: 0 292 | retry-wait: 0 293 | jdm-task-workers: 0 294 | session-workers: 0 295 | min-split-size: 0 296 | proxy-addr: "" 297 | check-best-mirror: false 298 | cache-in-ram: false 299 | cache-in-ram-limit: 0 300 | insecure-skip-verify: false 301 | certs-file-path: "" 302 | """) 303 | 304 | # 获取下载目录 305 | def get_down_dir() -> str: 306 | """ 307 | 返回下载目录 308 | """ 309 | data = get(base_url+"/jijidown/settings/get_download_dir") 310 | data = data['data'] 311 | return data['dir'] 312 | 313 | # 更改下载目录 314 | def change_down_dir(down_dir: str): 315 | """ 316 | 修改下载目录 317 | """ 318 | json = {'dir': down_dir} 319 | post(base_url+'/jijidown/settings/set_download_dir', json) 320 | 321 | def ad() -> dict: 322 | """ 323 | 获取ad 324 | title = str 标题 325 | url = str 链接 326 | img = str 图片内容,转成bit进行显示 327 | """ 328 | data = get(base_url+"/jijidown/ad") 329 | return data['data'] 330 | 331 | ################################################################### video info层 332 | 333 | #获取视频信息 334 | def info(type: int, id: str) -> dict: 335 | """ 336 | 获取不同类型的视频信息 337 | 1为AV类型 2 为BV类型 3为EP类型 4为SS类型 5为MEDIA类型 6为UP类型 7为FAV类型 8为UP主合集 9为UP主列表 338 | 返回信息示例 339 | { 340 | "code": 0, 341 | "message": "", 342 | "data": { 343 | "id": 247906246, 344 | "link_type": 1, 345 | "video_cover": "http://i1.hdslb.com/bfs/archive/34f668b39d9ce216a644c79df8ddc70ad1b61c8c.jpg", 346 | "video_title": "完全疯了!清凉水手妹「So Crazy」甜蜜夏日!", 347 | "video_filename": "完全疯了!清凉水手妹「So Crazy」甜蜜夏日!", 348 | "video_desc": "歌曲&舞蹈:T-ara -so crazy\n摄影:亚特兰\n后期:面面桑,M子\n后勤:凌柒", 349 | "sub_sort": "明星舞蹈", 350 | "sort": "舞蹈", 351 | "up_name": "早上好七七", 352 | "up_mid": 1851105, 353 | "up_face": "https://i0.hdslb.com/bfs/face/111ea36c816eb52385d566e5d38221c960c255a0.jpg", 354 | "bili_pubdate_str": "2021-05-03 19:20:12", 355 | "is_stein_gate": false, 356 | "list": [ 357 | { 358 | "page_av": 247906246, 359 | "page_bv": "BV1sv411j7F8", 360 | "page_cid": 332978921, 361 | "duration": 106, 362 | "page": 1, 363 | "page_name": "1.完全疯了!清凉水手妹「So Crazy」甜蜜夏日!", 364 | "page_title": "so crazy", 365 | "video_title": "完全疯了!清凉水手妹「So Crazy」甜蜜夏日!", 366 | "video_filename": "完全疯了!清凉水手妹「So Crazy」甜蜜夏日! - 1.so crazy", 367 | "is_bangumi": false 368 | } 369 | ] 370 | } 371 | } 372 | """ 373 | data = get(base_url+'/bili/'+str(type)+'/'+str(id)+'/get_video_info') 374 | return data 375 | 376 | ################################################################## video quality层 377 | 378 | # 获取分辨率 379 | def quality(av:int, cid:int) -> dict: 380 | """ 381 | 获取视频清晰度 382 | av 为视频AV号 cid 为分P的id 383 | """ 384 | # 获取指定分P清晰度 385 | one_video_info = get(base_url+'/bili/1/'+str(av) +'/'+str(cid)+'/get_video_quality') 386 | one_video_info = one_video_info['data']['list'] 387 | 388 | new_video_info = {} # 新建分辨率排序 389 | new_video_info['audio'] = {} 390 | new_video_info['video'] = {} 391 | new_video_info['audio']['WEB'] = [] 392 | new_video_info['audio']['TV'] = [] 393 | new_video_info['audio']['APP'] = [] 394 | new_video_info['video']['WEB'] = [] 395 | new_video_info['video']['TV'] = [] 396 | new_video_info['video']['APP'] = [] 397 | 398 | for quality in one_video_info: # 分类 399 | quality = code(quality) 400 | if quality["api_type"] == 0: 401 | if quality["is_audio"] == True: 402 | new_video_info['audio']['WEB'].append(quality) 403 | continue 404 | if quality["is_video"] == True: 405 | new_video_info['video']['WEB'].append(quality) 406 | continue 407 | 408 | if quality["api_type"] == 1: 409 | if quality["is_audio"] == True: 410 | new_video_info['audio']['TV'].append(quality) 411 | continue 412 | if quality["is_video"] == True: 413 | new_video_info['video']['TV'].append(quality) 414 | continue 415 | 416 | if quality["api_type"] == 2: 417 | if quality["is_audio"] == True: 418 | new_video_info['audio']['APP'].append(quality) 419 | continue 420 | if quality["is_video"] == True: 421 | new_video_info['video']['APP'].append(quality) 422 | continue 423 | 424 | # 排序 425 | new_video_info['audio']['WEB'] = dic(new_video_info['audio']['WEB']) 426 | new_video_info['video']['WEB'] = dic(new_video_info['video']['WEB']) 427 | new_video_info['audio']['TV'] = dic(new_video_info['audio']['TV']) 428 | new_video_info['video']['TV'] = dic(new_video_info['video']['TV']) 429 | new_video_info['audio']['APP'] = dic(new_video_info['audio']['APP']) 430 | new_video_info['video']['APP'] = dic(new_video_info['video']['APP']) 431 | 432 | return new_video_info 433 | 434 | ################################################################## 任务管理层 435 | 436 | #创建下载任务 437 | def post_new_task(avid:int,cid:int,video_quality_data:dict,audio_quality_data:dict,video_filename:str) -> dict: 438 | """ 439 | video_quality为1000时使用默认最高分辨率下载 440 | 以下参数仅在video_quality不等于1000时生效 441 | api_type 指示使用的下载接口,在1000时默认走WEB接口 442 | 443 | UseAV = true 使用B站AV号处理 444 | UseAV = false 不使用B站AV号处理 445 | 446 | """ 447 | video_quality = video_quality_data['quality'] 448 | audio_quality = audio_quality_data['quality'] 449 | if video_quality != 1000: 450 | api_type = video_quality_data['api_type'] 451 | video_codecs = video_quality_data['codec'] 452 | json={ 453 | "avid": avid, 454 | "cid": cid, 455 | "video_quality": video_quality, 456 | "audio_quality": audio_quality, 457 | "api_type": api_type, 458 | "useav": True, 459 | "useflv": False, 460 | "video_codecs": video_codecs, 461 | "video_filename": video_filename 462 | } 463 | else: 464 | json={ 465 | "avid": avid, 466 | "cid": cid, 467 | "video_quality": 1000, 468 | "useav": True, 469 | "useflv": False, 470 | "video_codecs": 1,#TODO 这里以后增加选择项,默认使用什么编码 471 | "video_filename": video_filename 472 | } 473 | data = post(base_url+'/task/post_new_task',json) 474 | download_danmaku(avid,cid,str(video_filename))#下载弹幕 475 | return data 476 | 477 | #下载弹幕 478 | def download_danmaku(avid:int,cid:int,video_filename:str): 479 | return_data = get(base_url+'/danmaku/'+avid+'/'+cid+'/download_danmaku?file='+video_filename) 480 | 481 | #获取下载任务进度 482 | def get_task_status(control_name:str): 483 | return_data = get(base_url+'/task/'+control_name+'/get_task_status') 484 | return return_data['data'] 485 | 486 | #暂停任务 487 | def patch_pause_task(control_name:str): 488 | return_data = patch(base_url+'/task/'+control_name+'/patch_pause_task') 489 | 490 | #继续任务 491 | def patch_resume_task(control_name:str): 492 | return_data = patch(base_url+'/task/'+control_name+'/patch_resume_task') 493 | 494 | #删除任务和文件 495 | def delete_task(control_name:str): 496 | return_data = patch(base_url+'/task/'+control_name+'/delete_task_and_file') 497 | 498 | #读取json中的下载列表 499 | def load_json() -> dict: 500 | """ 501 | 读取配置参数 502 | """ 503 | if os.path.exists('set.json'): 504 | with open('set.json','r') as f: 505 | return_data = json.loads(f.read()) 506 | else: 507 | data = {} 508 | data['need_down_list'] = [] 509 | data['fin_down_list'] = [] 510 | with open('set.json','w') as f: 511 | f.write(json.dumps(data)) 512 | return_data = data 513 | return return_data 514 | 515 | #写入下载列表 516 | def save_json(data:dict) -> None: 517 | """ 518 | 读取配置参数 519 | """ 520 | with open('set.json','w') as f: 521 | f.write(json.dumps(data)) 522 | return -------------------------------------------------------------------------------- /old/app.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import time 4 | import threading #多进程库 5 | from pathlib import Path #路径库 6 | import platform#获取系统信息 7 | 8 | import requests 9 | import pywebio as io 10 | out = io.output 11 | ioin = io.input 12 | pin = io.pin 13 | 14 | import core #导入python核心接口 15 | 16 | ######TODO 防止核心和前端任务不同步,暂时删除下载列表保存功能 17 | data = {} 18 | data['need_down_list'] = [] 19 | data['fin_down_list'] = [] 20 | core.save_json(data) 21 | #声明下载列表 22 | set_info = core.load_json() 23 | #获取当前平台 24 | system_type = platform.system()#系统名称 25 | system_bit = platform.machine()#操作系统位数 26 | #启动时间 27 | local_time = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime()) 28 | 29 | #挂载核心 30 | def start_core(): 31 | """ 32 | 挂载核心 33 | """ 34 | print("windows核心已启动") 35 | os.system('taskkill /f /t /im "JiJiDownCore-win64.exe"')#关闭核心 36 | core_out = os.popen(str(Path('resources/JiJiDownCore-win64.exe'))+'>>'+str(Path('temp/log_'+local_time+'.log')))#启动核心 37 | 38 | #检查下载路径 39 | def check_dir(): 40 | """ 41 | 检查下载路径 42 | """ 43 | new_dir = pin.pin['change_dir'] 44 | if os.path.exists(new_dir): 45 | core.change_down_dir(new_dir)#修改路径 46 | out.toast('修改成功',color='success',duration='0')#弹出错误弹窗 47 | return 48 | out.toast('路径无效',color='error',duration='0')#弹出错误弹窗 49 | return 50 | 51 | #校验输入是否正确 52 | def check_input_url(url): 53 | #判断输入url类型 54 | if "BV" in url: 55 | video_info = re.findall('BV(..........)',url)[0] 56 | return 57 | elif "av" in url: 58 | video_info = re.findall('av(\d*)',url)[0] 59 | return 60 | elif "b23.tv" in url: 61 | url = re.findall('(https://b23.tv/.......)',url)[0] 62 | video_info = requests.get(url,allow_redirects=False).text#转换为网页版链接 63 | check_input_url(video_info)#重新检查 64 | elif "ep" in url: 65 | video_info = re.findall('ep(\d*)',url)[0] 66 | return 67 | elif "ss" in url: 68 | video_info = re.findall('ss(\d*)',url)[0] 69 | return 70 | elif "media/md" in url:#https://www.bilibili.com/bangumi/media/md28234644 71 | video_info = re.findall('md(\d*)',url)[0] 72 | return 73 | elif "space.bilibili.com" in url:#https://space.bilibili.com/1118188465 74 | video_info = re.findall('space.bilibili.com/(\d*)',url)[0] 75 | return 76 | #收藏夹处理 77 | elif "favlist?fid=" in url:#https://space.bilibili.com/1118188465/favlist?fid=1228786865 78 | video_info = re.findall('fid=(\d*)',url)[0] 79 | return 80 | #up主合集处理 81 | elif "?sid=" in url:#https://space.bilibili.com/1118188465/channel/collectiondetail?sid=28413 82 | video_info = re.findall('?sid=(\d*)',url)[0] 83 | return 84 | return '链接无效' 85 | 86 | #检查输入链接类型 87 | def get_video_id(url) -> list: 88 | """ 89 | 检查输入链接类型 90 | 输出为list [序号:int,id:str] 91 | """ 92 | #判断输入url类型 93 | #手机端链接处理 94 | if "b23.tv" in url: 95 | url = re.findall('(https://b23.tv/.......)',url)[0] 96 | video_info = requests.get(url,allow_redirects=False).text#转换为网页版链接 97 | get_video_id(video_info)#重新检查 98 | #ep类型处理 99 | if "ep" in url: 100 | video_info = re.findall('ep(\d*)',url)[0] 101 | if video_info != '': 102 | return [3,video_info] 103 | #ss类型处理 104 | if "ss" in url: 105 | video_info = re.findall('ss(\d*)',url)[0] 106 | if video_info != '': 107 | return [4,video_info] 108 | #media类型处理 109 | if "media/md" in url:#https://www.bilibili.com/bangumi/media/md28234644 110 | video_info = re.findall('md(\d*)',url)[0] 111 | if video_info != '': 112 | return [5,video_info] 113 | #收藏夹处理 114 | if "favlist?fid=" in url:#https://space.bilibili.com/1118188465/favlist?fid=1228786865 115 | video_info = re.findall('fid.([0-9]*)',url)[0] 116 | if video_info != '': 117 | return [7,video_info] 118 | #up主合集处理 119 | if "?sid=" in url:#https://space.bilibili.com/1118188465/channel/collectiondetail?sid=28413 120 | video_info = re.findall('?sid=(\d*)',url)[0] 121 | if video_info != '': 122 | return [8,video_info] 123 | #up主投稿处理 124 | if "space.bilibili.com" in url:#https://space.bilibili.com/1118188465 125 | video_info = re.findall('space.bilibili.com/(\d*)',url)[0] 126 | if video_info != '': 127 | return [6,video_info] 128 | #BV解析 129 | if "BV" in url: 130 | video_info = re.findall('BV(..........)',url)[0] 131 | if video_info != '': 132 | return [2,video_info] 133 | #AV解析 134 | if "av" in url: 135 | video_info = re.findall('av(\d*)',url)[0] 136 | if video_info != '': 137 | return [1,video_info] 138 | return '' 139 | 140 | #检查任务状态类型 141 | def get_task_status(data:int) -> str: 142 | """ 143 | 从代码查找对应的任务状态 144 | """ 145 | if data == 0: 146 | return '错误' 147 | if data == 1: 148 | return '暂停' 149 | if data == 2: 150 | return '运行中' 151 | if data == 3: 152 | return '等待' 153 | if data == 4: 154 | return '正在进行合并' 155 | if data == 5: 156 | return '正在提取MP3' 157 | if data == 6: 158 | return '完成' 159 | 160 | #将下载完成的移动到完成任务列表中 161 | def remove_fin_task(control_name:str): 162 | data = core.load_json()#读取配置 163 | for one in data['need_down_list']:#遍历正在进行的任务列表 164 | if control_name == one['control_name']: 165 | data['fin_down_list'].append(one) 166 | data['need_down_list'].remove(one) 167 | break 168 | core.save_json(data) 169 | return 170 | 171 | #从任务列表中移除任务(暂时)TODO 未来实现自动恢复下载进度 172 | def remove_task(control_name:str): 173 | data = core.load_json()#读取配置 174 | for one in data['need_down_list']:#遍历正在进行的任务列表 175 | if control_name == one['control_name']: 176 | core.delete_task(one['control_name'])#删除任务 177 | data['need_down_list'].remove(one) 178 | break 179 | core.save_json(data) 180 | return 181 | 182 | #获取被勾选视频数据 183 | def get_video_list_info(num:int) -> list: 184 | """ 185 | 获取被勾选的视频数据 186 | """ 187 | info_list = [] 188 | for checkbox_one in range(1,num+1): 189 | if len(pin.pin['check_'+str(checkbox_one)]) != 0:#判断是否勾选了至少一个 190 | info_list.append(pin.pin['check_'+str(checkbox_one)][0]) 191 | return info_list 192 | 193 | #获取选择的清晰度 194 | def get_choice_quality() -> dict: 195 | """ 196 | 获取选择的视频清晰度 197 | 返回值为{'video','audio'} 198 | """ 199 | return_dict = {} 200 | return_dict['video'] = pin.pin['select_video'] 201 | return_dict['audio'] = pin.pin['select_audio'] 202 | return return_dict 203 | 204 | #创建清晰度列表 205 | def get_video_quality_list(data:dict) -> list: 206 | """ 207 | 返回[视频清晰度,音频清晰度] 208 | """ 209 | out_audio_list = [] 210 | out_video_list = [] 211 | out_audio_list.append({'label':'默认最高音质','value':{'quality':30280}})#默认选中 212 | out_video_list.append({'label':'默认最高画质','value':{'quality':1000}})#默认选中 213 | for type in ['WEB','TV','APP']:#遍历不同接口 214 | for one_info in data['audio'][type]:#处理返回数据 215 | label = type+' '+one_info['quality_str']+' - ['+one_info['codec']+'] '+one_info['stream_size']#选项标签 216 | value = one_info#选项值 217 | return_dict = {'label':label,'value':value} 218 | out_audio_list.append(return_dict) 219 | for one_info in data['video'][type]:#处理返回数据 220 | label = type+' '+one_info['quality_str']+' - ['+one_info['codec']+'] '+one_info['stream_size']#选项标签 221 | value = one_info#选项值 222 | return_dict = {'label':label,'value':value} 223 | out_video_list.append(return_dict) 224 | return [out_video_list,out_audio_list] 225 | 226 | #处理下载文件名 227 | def make_down_name(info_date:dict,video_data:dict) -> str: 228 | """ 229 | 下载文件名处理 230 | 预留接口 231 | """ 232 | return video_data['video_filename'] 233 | 234 | #显示视频下载选择界面 235 | def print_video_info(info) -> list: 236 | """ 237 | 创建视频下载界面 238 | 返回[视频信息,需下载视频列表,清晰度] 239 | """ 240 | with out.use_scope('video_info'):#进入视频信息域 241 | data = info['data'] 242 | with out.use_scope('up_info'):#切换到up信息域 243 | face = requests.get(data['up_face']).content#缓存图片 244 | out.put_row([ 245 | None, 246 | out.put_image(face,height='50px').style('margin-top: 1rem;margin-bottom: 1rem;border:1px solid;border-radius:50%;box-shadow: 5px 5px 5px #A9A9A9'), 247 | None, 248 | out.put_column([ 249 | None, 250 | out.put_text(data['up_name']).style('font-size:1.25em;line-height: 0'), 251 | ],size='1fr 1fr'), 252 | None 253 | ],size='1fr 50px 25px auto 1fr').style('border: 1px solid #e9ecef;border-radius: .25rem')#限制up头像大小 254 | info_title = out.put_text(data['video_title']).style('border: 1px solid #e9ecef;border-radius: .25rem;font-size:2em;font-weight:bold;text-align:center')#视频标题 255 | if data['video_cover'] != '': 256 | cover = requests.get(data['video_cover']).content#缓存图片 257 | info_cover = out.put_image(cover).style('border-radius: 30px;box-shadow: 15px 15px 30px #bebebe,-15px -15px 30px #ffffff;')#视频封面 258 | info_desc = out.put_text(data['video_desc']).style('border: 1px solid #e9ecef;border-radius: .25rem')#视频简介 259 | info_two = out.put_column([ 260 | None, 261 | out.put_text('视频类型 '+str(data['sort'])+'-'+str(data['sub_sort'])).style('text-align:center'), 262 | None, 263 | out.put_text('视频发布时间 '+data['bili_pubdate_str']).style('text-align:center;line-height:0.5'), 264 | None 265 | ],size='0.5em auto 0.5em auto 0.5em').style('border: 1px solid #e9ecef;border-radius: .25rem') 266 | if data['video_cover'] != '': 267 | out.put_column([info_title,None,info_cover,None,info_desc,None,info_two],size='auto 1em auto 2em auto 1em auto').style('margin-top: 0.5rem;margin-bottom: 0.5rem')#设置为垂直排布 268 | else: 269 | out.put_column([info_title,None,info_desc,None,info_two],size='auto 10px auto 10px auto').style('margin-top: 0.5rem;margin-bottom: 0.5rem')#设置为垂直排布 270 | 271 | #创建清晰度选择 272 | get_video_quality_info = get_video_quality_list(core.quality(data['list'][0]['page_av'],data['list'][0]['page_cid']))#读取视频列表第一个的清晰度 273 | with out.use_scope('quality'):#创建清晰度选择域 274 | out.put_column([ 275 | pin.put_select(name='select_video',options=get_video_quality_info[0],value={'quality':1000}),#创建视频选择 276 | pin.put_select(name='select_audio',options=get_video_quality_info[1],value={'quality':30280})#创建音频选择 277 | ]) 278 | #创建列表 279 | table_list = [] 280 | num = 0 281 | table_list.append(['选择','标题']) 282 | for one in data['list']:#遍历列表 283 | num += 1 284 | list_one = [] 285 | list_one.append(pin.put_checkbox(name='check_'+str(num),options=[{'label':str(num),'value':one}]).style('line-height:0.5'))#创建单选框 286 | list_one.append(out.put_text(one['page_name']).style('width:100%'))#标题 287 | table_list.append(list_one) 288 | out.put_table(table_list,position=-1).style('width:100%') 289 | 290 | #移除加载提示 291 | out.clear('load') 292 | out.remove('load') 293 | #使页面滚动到分辨率选择置顶 294 | out.scroll_to('quality','top') 295 | #创建下载控制组件 296 | control_buttom = ioin.actions(buttons=[{'label':'开始下载','value':'0'},{'label':'下载全部','value':'1'},{'label':'退出界面','value':'2','color':'danger'}])#创建按键 297 | need_quality:dict = get_choice_quality()#获取选择的清晰度 298 | if control_buttom == '0':#如果选择下载被勾选部分 299 | need_video_list:list = get_video_list_info(len(data['list']))#获取被勾选的视频列表 300 | elif control_buttom == '1':#如果选择下载全部 301 | need_video_list:list = data['list'] 302 | elif control_buttom == '2':#如果选择退出 303 | return 'out' 304 | if len(need_video_list) == 0:#如果未选择任何视频 305 | out.toast('未选择任何视频',color='info',duration='0')#弹出错误弹窗 306 | return 'out' 307 | return [data,need_video_list,need_quality] 308 | 309 | #显示完成列表 310 | def show_fin_list(): 311 | """ 312 | 处理下载列表并显示 313 | {'need_video','control_name'} 314 | """ 315 | #加载任务列表 316 | data_list = core.load_json()['fin_down_list']#获取需要下载的列表 317 | if len(data_list) == 0:#如果列表为空则等待 318 | time.sleep(1) 319 | show_fin_list() 320 | show_list = [] 321 | #初始化 322 | for list_one in data_list:#遍历创建下载列表 323 | show_list.append([ 324 | out.put_row([ 325 | out.put_text(list_one['video_info']['page_name']),#TODO 创建标题 326 | out.put_column([ 327 | out.put_scope('sta_text_'+list_one['control_name']),#创建用于进度提示的域,控制值为'sta_text_'+list_one['control_name'] 328 | out.put_processbar(list_one['control_name'],init=0)#下载列表,控制值为list_one['control_name'] 329 | ]), 330 | out.put_scope('sta_worktype_'+list_one['control_name']),#创建用于显示任务状态的域,控制值为'sta_worktype_'+list_one['control_name'] 331 | ]) 332 | ])# 创建显示 333 | 334 | #显示列表 335 | with out.use_scope('fin_work',clear=True):#清除域 336 | out.put_table(show_list) 337 | 338 | #刷新状态 339 | while 1==1: 340 | for list_one in data_list:#遍历下载列表 341 | get_task_info = core.get_task_status(list_one['control_name'])#获取下载任务状态 342 | if get_task_info == None:#如果类型为None说明该任务不在下载器中 TODO 未来实现自动添加任务继续下载 343 | remove_task(list_one['control_name'])#移除任务 344 | show_down_list()#重新启动下载列表维护进程 345 | out.set_processbar(list_one['control_name'],value= get_task_info['progress'] / 100)#刷新进度条状态 346 | with out.use_scope('sta_text_'+list_one['control_name'],clear=True):#进入用于进度提示的域并清空 347 | out.put_text(get_task_info['status_text'])#显示进度提示文本 348 | with out.use_scope('sta_worktype_'+list_one['control_name'],clear=True):#进入用于显示任务状态提示文本的域并清空 349 | out.put_text(get_task_status(int(get_task_info['task_status'])))#显示任务状态提示文本 350 | if int(get_task_info['task_status']) == 6:#如果任务下载完成 351 | remove_fin_task(list_one['control_name'])#将任务移入完成列表 352 | if get_task_info['msg'] != '':#如果出现错误信息 353 | out.toast(list_one['video_info']['video_filename']+' '+get_task_info['msg'],color='error') 354 | time.sleep(1)#等待1秒 355 | #如果下载列表更新 356 | if data_list != core.load_json()['need_fin_list']:#如果现在维护的和存储的不一样 357 | show_fin_list() 358 | 359 | #显示下载列表 360 | def show_down_list(): 361 | """ 362 | 处理下载列表并显示 363 | {'need_video','control_name'} 364 | """ 365 | #加载任务列表 366 | data_list = core.load_json()['need_down_list']#获取需要下载的列表 367 | if len(data_list) == 0:#如果列表为空则等待 368 | time.sleep(1) 369 | show_down_list() 370 | show_list = [] 371 | #初始化 372 | for list_one in data_list:#遍历创建下载列表 373 | show_list.append([ 374 | out.put_row([ 375 | out.put_text(list_one['video_info']['page_name']),#TODO 创建标题 376 | out.put_column([ 377 | out.put_scope('sta_text_'+list_one['control_name']),#创建用于进度提示的域,控制值为'sta_text_'+list_one['control_name'] 378 | out.put_processbar(list_one['control_name'],init=0)#下载列表,控制值为list_one['control_name'] 379 | ]), 380 | out.put_scope('sta_worktype_'+list_one['control_name']),#创建用于显示任务状态的域,控制值为'sta_worktype_'+list_one['control_name'] 381 | #out.put_button('暂停',onclick=lambda:core.patch_pause_task(list_one['control_name']),color='warning'),#暂停按钮 382 | out.put_button('删除',onclick=lambda:remove_task(list_one['control_name']),color='danger'),#删除按钮 383 | ]) 384 | ])# 创建显示 385 | 386 | #显示列表 387 | with out.use_scope('down_work',clear=True):#清除域 388 | out.put_table(show_list) 389 | 390 | #刷新状态 391 | while 1==1: 392 | for list_one in data_list:#遍历下载列表 393 | get_task_info = core.get_task_status(list_one['control_name'])#获取下载任务状态 394 | if get_task_info == None:#如果类型为None说明该任务不在下载器中 TODO 未来实现自动添加任务继续下载 395 | remove_task(list_one['control_name'])#移除任务 396 | show_down_list()#重新启动下载列表维护进程 397 | out.set_processbar(list_one['control_name'],value= get_task_info['progress'] / 100)#刷新进度条状态 398 | with out.use_scope('sta_text_'+list_one['control_name'],clear=True):#进入用于进度提示的域并清空 399 | out.put_text(get_task_info['status_text'])#显示进度提示文本 400 | with out.use_scope('sta_worktype_'+list_one['control_name'],clear=True):#进入用于显示任务状态提示文本的域并清空 401 | out.put_text(get_task_status(int(get_task_info['task_status'])))#显示任务状态提示文本 402 | if int(get_task_info['task_status']) == 6:#如果任务下载完成 403 | remove_fin_task(list_one['control_name'])#将任务移入完成列表 404 | if get_task_info['msg'] != '':#如果出现错误信息 405 | out.toast(list_one['video_info']['video_filename']+' '+get_task_info['msg'],color='error') 406 | time.sleep(1)#等待1秒 407 | #如果下载列表更新 408 | if data_list != core.load_json()['need_down_list']:#如果现在维护的和存储的不一样 409 | show_down_list() 410 | 411 | #解析输入的url 412 | def start_url(): 413 | url = pin.pin['url_input']#获取输入 414 | check_return = check_input_url(url) 415 | if check_return != None: 416 | out.toast(check_return,duration=3,color='error')#显示错误信息 417 | return 418 | #清除旧显示内容 419 | out.clear('video_info') 420 | with out.use_scope('main'): 421 | #显示加载提示 422 | with out.use_scope('load'): 423 | out.put_row([ 424 | None, 425 | out.put_loading(), 426 | out.put_text('解析中'), 427 | None 428 | ],size='1fr auto auto 1fr') 429 | 430 | #解析url 431 | out_url = get_video_id(url) 432 | get_video_info = core.info(out_url[0],out_url[1]) 433 | return_data = print_video_info(get_video_info)#显示视频信息,返回视频信息 434 | if return_data == 'out':#如果选择退出界面或未勾选任何视频 435 | out.clear('video_info')#清空video_info域 436 | return 437 | #开始下载任务 438 | video_data = return_data[2]['video'] 439 | audio_data = return_data[2]['audio'] 440 | #滚动到最底下 441 | out.scroll_to('main','buttom') 442 | with out.use_scope('posting'): 443 | out.put_column([ 444 | out.put_text('任务发送中').style('line-height: 0.5'), 445 | out.put_processbar('loading',init=0)#设置进度条 446 | ]).style('border: 1px solid #e9ecef;border-radius: .25rem') 447 | num = 0 448 | for need_video in return_data[1]: 449 | return_down_data:dict = core.post_new_task(need_video['page_av'],need_video['page_cid'],video_data,audio_data,make_down_name(return_data,need_video)) 450 | set_info['need_down_list'].append({'control_name':return_down_data['data']['control_name'],'video_info':need_video})#添加控制字节段至下载列表 451 | core.save_json(set_info)#刷新设置里的下载列表 452 | num += 1 453 | out.set_processbar('loading',num / len(return_data[1]))#设置进度条进度 454 | out.clear('loading') 455 | out.put_text('任务发送完成') 456 | return 457 | 458 | #主函数 459 | def main():#主函数 460 | print('主函数启动') 461 | if not start_jiji_core.is_alive(): 462 | print('启动核心') 463 | io.session.register_thread(start_jiji_core) 464 | start_jiji_core.setDaemon(True)#设为守护进程 465 | start_jiji_core.start() 466 | with out.use_scope('main'):#创建并进入main域 467 | out.scroll_to('main','top') 468 | #等待核心响应提示 469 | with out.use_scope('load'): 470 | out.put_row([ 471 | None, 472 | out.put_loading(color='primary').style('width:4rem; height:4rem'), 473 | None, 474 | out.put_column([ 475 | None, 476 | out.put_text('等待核心响应,请检查核心是否启动').style('line-height: 0'), 477 | ],size='1fr 1fr'), 478 | None 479 | ],size='1fr auto 20px auto 1fr') 480 | while core.check() != 'ok':#当响应不正确 481 | time.sleep(1) 482 | out.clear('load') 483 | out.remove('load') 484 | #初始化 485 | out.scroll_to('main','top') 486 | out.clear('main') 487 | #检查登录 488 | user_info = core.get_user_info() 489 | if user_info['code'] == True:#如果已登录 490 | with out.use_scope('user_info'):#切换到用户信息域 491 | face = requests.get(user_info['face']).content#缓存图片 492 | out.put_row([ 493 | None, 494 | out.put_image(face,height='50px').style('margin-top: 1rem;margin-bottom: 1rem;border:1px solid;border-radius:50%;box-shadow: 5px 5px 5px #A9A9A9'), 495 | None, 496 | out.put_column([ 497 | None, 498 | out.put_text('当前登录用户:'+user_info['uname']+user_info['vip_label_text']).style('font-size:1.25em;line-height: 0'), 499 | ],size='1fr 1fr'), 500 | None 501 | ],size='1fr 50px 25px auto 1fr').style('border: 1px solid #e9ecef;border-radius: .25rem')#限制用户头像大小 502 | else: 503 | return_data = core.get_login_status()#获取二维码 504 | with out.use_scope('login'): 505 | out.put_row([ 506 | None, 507 | out.put_column([ 508 | out.put_text('请扫描二维码登录').style('line-height: 1;text-align:center'), 509 | out.put_image(return_data['image']), 510 | None 511 | ],size='auto 1fr 1rem').style('border: 1px solid #e9ecef;border-radius: .25rem;box-shadow: 5px 5px 5px #A9A9A9'), 512 | None 513 | ],size='1fr auto 1fr') 514 | while 1==1: 515 | check_user_info = core.get_user_info()#循环检查是否扫描 516 | if check_user_info['code'] == True:#如果登录成功 517 | out.clear('login') 518 | out.remove('login') 519 | return 520 | time.sleep(1) 521 | 522 | #创建横向标签栏 523 | scope_url = out.put_scope('url')#创建url域 524 | scope_set = out.put_scope('set')#创建set域 525 | scope_down = out.put_scope('down')#创建down域 526 | out.put_tabs([{'title':'链接解析','content':scope_url},{'title':'下载列表','content':scope_down},{'title':'设置','content':scope_set}])#创建 527 | 528 | 529 | #创建url输入框 530 | with out.use_scope('url'):#进入域 531 | pin.put_input('url_input',label='请输入链接',type='text')#限制类型为url,使用check_input_url检查内容 532 | out.put_button(label='解析链接',onclick=start_url).style('width: 100%')#创建按键 533 | out.put_scope('video_info') 534 | 535 | #创建下载列表 536 | with out.use_scope('down'): 537 | scope_down_work = out.put_scope('down_work') 538 | scope_down_fin = out.put_scope('down_fin') 539 | out.put_tabs([{'title':'下载中','content':scope_down_work},{'title':'下载完成','content':scope_down_fin}])#创建横向标签栏 540 | 541 | # 开启新线程 542 | thread1 = threading.Thread(target=show_down_list) 543 | thread2 = threading.Thread(target=show_fin_list) 544 | if not thread1.is_alive(): 545 | print('启动线程1') 546 | io.session.register_thread(thread1) 547 | thread1.setDaemon(True)#设为守护进程 548 | thread1.start() 549 | if not thread2.is_alive(): 550 | print('启动线程2') 551 | io.session.register_thread(thread2) 552 | thread2.setDaemon(True)#设为守护进程 553 | thread2.start() 554 | 555 | #创建设置 556 | with out.use_scope('set'):#进入域 557 | out.put_row([out.put_text('目前下载地址:'),pin.put_input(name='change_dir',value=core.get_down_dir()),out.put_button('确认修改',onclick=check_dir)]) 558 | 559 | print('自动更新核心') 560 | core.update_core(system_type,system_bit)#更新核心 561 | start_jiji_core = threading.Thread(target=start_core) 562 | io.config(title='Jithon 2.0 Beta',description='本应用为唧唧2.0基于python的webui实现',theme='yeti') 563 | print('主程序启动,如未自动跳转请打开http://127.0.0.1:8080') 564 | io.start_server(main,host='127.0.0.1',port=8080,debug=True,cdn=False,auto_open_webbrowser=True) -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "e365201c60e6770681d9dd93dcfff6bba303d27c64cd54b8d6215e57b662e517" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.11" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "altgraph": { 20 | "hashes": [ 21 | "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd", 22 | "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe" 23 | ], 24 | "version": "==0.17.3" 25 | }, 26 | "certifi": { 27 | "hashes": [ 28 | "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", 29 | "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" 30 | ], 31 | "markers": "python_version >= '3.6'", 32 | "version": "==2023.7.22" 33 | }, 34 | "charset-normalizer": { 35 | "hashes": [ 36 | "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96", 37 | "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c", 38 | "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710", 39 | "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706", 40 | "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020", 41 | "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252", 42 | "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad", 43 | "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329", 44 | "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a", 45 | "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f", 46 | "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6", 47 | "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4", 48 | "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a", 49 | "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46", 50 | "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2", 51 | "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23", 52 | "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace", 53 | "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd", 54 | "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982", 55 | "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10", 56 | "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2", 57 | "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea", 58 | "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09", 59 | "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5", 60 | "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149", 61 | "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489", 62 | "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9", 63 | "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80", 64 | "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592", 65 | "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3", 66 | "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6", 67 | "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed", 68 | "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c", 69 | "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200", 70 | "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a", 71 | "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e", 72 | "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d", 73 | "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6", 74 | "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623", 75 | "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669", 76 | "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3", 77 | "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa", 78 | "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9", 79 | "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2", 80 | "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f", 81 | "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1", 82 | "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4", 83 | "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a", 84 | "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8", 85 | "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3", 86 | "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029", 87 | "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f", 88 | "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959", 89 | "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22", 90 | "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7", 91 | "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952", 92 | "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346", 93 | "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e", 94 | "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d", 95 | "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299", 96 | "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd", 97 | "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a", 98 | "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3", 99 | "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037", 100 | "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94", 101 | "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c", 102 | "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858", 103 | "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a", 104 | "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449", 105 | "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c", 106 | "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918", 107 | "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1", 108 | "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c", 109 | "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac", 110 | "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa" 111 | ], 112 | "markers": "python_full_version >= '3.7.0'", 113 | "version": "==3.2.0" 114 | }, 115 | "colorama": { 116 | "hashes": [ 117 | "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", 118 | "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" 119 | ], 120 | "markers": "sys_platform == 'win32'", 121 | "version": "==0.4.6" 122 | }, 123 | "decorator": { 124 | "hashes": [ 125 | "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", 126 | "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" 127 | ], 128 | "markers": "python_version >= '3.5'", 129 | "version": "==5.1.1" 130 | }, 131 | "grpcio": { 132 | "hashes": [ 133 | "sha256:00258cbe3f5188629828363ae8ff78477ce976a6f63fb2bb5e90088396faa82e", 134 | "sha256:092fa155b945015754bdf988be47793c377b52b88d546e45c6a9f9579ac7f7b6", 135 | "sha256:0f80bf37f09e1caba6a8063e56e2b87fa335add314cf2b78ebf7cb45aa7e3d06", 136 | "sha256:20ec6fc4ad47d1b6e12deec5045ec3cd5402d9a1597f738263e98f490fe07056", 137 | "sha256:2313b124e475aa9017a9844bdc5eafb2d5abdda9d456af16fc4535408c7d6da6", 138 | "sha256:23e7d8849a0e58b806253fd206ac105b328171e01b8f18c7d5922274958cc87e", 139 | "sha256:2f708a6a17868ad8bf586598bee69abded4996b18adf26fd2d91191383b79019", 140 | "sha256:2f7349786da979a94690cc5c2b804cab4e8774a3cf59be40d037c4342c906649", 141 | "sha256:34950353539e7d93f61c6796a007c705d663f3be41166358e3d88c45760c7d98", 142 | "sha256:40b72effd4c789de94ce1be2b5f88d7b9b5f7379fe9645f198854112a6567d9a", 143 | "sha256:4b089f7ad1eb00a104078bab8015b0ed0ebcb3b589e527ab009c53893fd4e613", 144 | "sha256:4faea2cfdf762a664ab90589b66f416274887641ae17817de510b8178356bf73", 145 | "sha256:5371bcd861e679d63b8274f73ac281751d34bd54eccdbfcd6aa00e692a82cd7b", 146 | "sha256:5613a2fecc82f95d6c51d15b9a72705553aa0d7c932fad7aed7afb51dc982ee5", 147 | "sha256:57b183e8b252825c4dd29114d6c13559be95387aafc10a7be645462a0fc98bbb", 148 | "sha256:5b7a4ce8f862fe32b2a10b57752cf3169f5fe2915acfe7e6a1e155db3da99e79", 149 | "sha256:5e5b58e32ae14658085c16986d11e99abd002ddbf51c8daae8a0671fffb3467f", 150 | "sha256:60fe15288a0a65d5c1cb5b4a62b1850d07336e3ba728257a810317be14f0c527", 151 | "sha256:6907b1cf8bb29b058081d2aad677b15757a44ef2d4d8d9130271d2ad5e33efca", 152 | "sha256:76c44efa4ede1f42a9d5b2fed1fe9377e73a109bef8675fb0728eb80b0b8e8f2", 153 | "sha256:7a635589201b18510ff988161b7b573f50c6a48fae9cb567657920ca82022b37", 154 | "sha256:7b400807fa749a9eb286e2cd893e501b110b4d356a218426cb9c825a0474ca56", 155 | "sha256:82640e57fb86ea1d71ea9ab54f7e942502cf98a429a200b2e743d8672171734f", 156 | "sha256:871f9999e0211f9551f368612460442a5436d9444606184652117d6a688c9f51", 157 | "sha256:9338bacf172e942e62e5889b6364e56657fbf8ac68062e8b25c48843e7b202bb", 158 | "sha256:a8a8e560e8dbbdf29288872e91efd22af71e88b0e5736b0daf7773c1fecd99f0", 159 | "sha256:aed90d93b731929e742967e236f842a4a2174dc5db077c8f9ad2c5996f89f63e", 160 | "sha256:b363bbb5253e5f9c23d8a0a034dfdf1b7c9e7f12e602fc788c435171e96daccc", 161 | "sha256:b4098b6b638d9e0ca839a81656a2fd4bc26c9486ea707e8b1437d6f9d61c3941", 162 | "sha256:b53333627283e7241fcc217323f225c37783b5f0472316edcaa4479a213abfa6", 163 | "sha256:b670c2faa92124b7397b42303e4d8eb64a4cd0b7a77e35a9e865a55d61c57ef9", 164 | "sha256:bb396952cfa7ad2f01061fbc7dc1ad91dd9d69243bcb8110cf4e36924785a0fe", 165 | "sha256:c60b83c43faeb6d0a9831f0351d7787a0753f5087cc6fa218d78fdf38e5acef0", 166 | "sha256:c6ebecfb7a31385393203eb04ed8b6a08f5002f53df3d59e5e795edb80999652", 167 | "sha256:d78d8b86fcdfa1e4c21f8896614b6cc7ee01a2a758ec0c4382d662f2a62cf766", 168 | "sha256:d7f8df114d6b4cf5a916b98389aeaf1e3132035420a88beea4e3d977e5f267a5", 169 | "sha256:e1cb52fa2d67d7f7fab310b600f22ce1ff04d562d46e9e0ac3e3403c2bb4cc16", 170 | "sha256:e3fdf04e402f12e1de8074458549337febb3b45f21076cc02ef4ff786aff687e", 171 | "sha256:e503cb45ed12b924b5b988ba9576dc9949b2f5283b8e33b21dcb6be74a7c58d0", 172 | "sha256:f19ac6ac0a256cf77d3cc926ef0b4e64a9725cc612f97228cd5dc4bd9dbab03b", 173 | "sha256:f1fb0fd4a1e9b11ac21c30c169d169ef434c6e9344ee0ab27cfa6f605f6387b2", 174 | "sha256:fada6b07ec4f0befe05218181f4b85176f11d531911b64c715d1875c4736d73a", 175 | "sha256:fd173b4cf02b20f60860dc2ffe30115c18972d7d6d2d69df97ac38dee03be5bf", 176 | "sha256:fe752639919aad9ffb0dee0d87f29a6467d1ef764f13c4644d212a9a853a078d", 177 | "sha256:fee387d2fab144e8a34e0e9c5ca0f45c9376b99de45628265cfa9886b1dbe62b" 178 | ], 179 | "index": "pypi", 180 | "version": "==1.57.0" 181 | }, 182 | "grpcio-tools": { 183 | "hashes": [ 184 | "sha256:02d78c034109f46032c7217260066d49d41e6bcaf588fa28fa40fe2f83445347", 185 | "sha256:0cf5fc0a1c23f8ea34b408b72fb0e90eec0f404ad4dba98e8f6da3c9ce34e2ed", 186 | "sha256:1c0e8a1a32973a5d59fbcc19232f925e5c48116e9411f788033a31c5ca5130b4", 187 | "sha256:1f9e917a9f18087f6c14b4d4508fb94fca5c2f96852363a89232fb9b2124ac1f", 188 | "sha256:26e69d08a515554e0cfe1ec4d31568836f4b17f0ff82294f957f629388629eb9", 189 | "sha256:2b417c97936d94874a3ce7ed8deab910f2233e3612134507cfee4af8735c38a6", 190 | "sha256:2db25f15ed44327f2e02d0c4fe741ac966f9500e407047d8a7c7fccf2df65616", 191 | "sha256:2f16130d869ce27ecd623194547b649dd657333ec7e8644cc571c645781a9b85", 192 | "sha256:34b36217b17b5bea674a414229913e1fd80ede328be51e1b531fcc62abd393b0", 193 | "sha256:35bf0dad8a3562043345236c26d0053a856fb06c04d7da652f2ded914e508ae7", 194 | "sha256:495e2946406963e0b9f063f76d5af0f2a19517dac2b367b5b044432ac9194296", 195 | "sha256:4a7ad7f328e28fc97c356d0f10fb10d8b5151bb65aa7cf14bf8084513f0b7306", 196 | "sha256:4fb8a8468031f858381a576078924af364a08833d8f8f3237018252c4573a802", 197 | "sha256:5bc3e6d338aefb052e19cedabe00452be46d0c10a4ed29ee77abb00402e438fe", 198 | "sha256:6fa52972c9647876ea35f6dc2b51002a74ed900ec7894586cbb2fe76f64f99de", 199 | "sha256:76c0eea89d7542719594e50e2283f51a072978b953e8b3e9fd7c59a2c762d4c1", 200 | "sha256:784574709b9690dc28696617ea69352e2132352fdfc9bc89afa8e39f99ae538e", 201 | "sha256:7b46fc6aa8eb7edd18cafcd21fd98703cb6c09e46b507de335fca7f0161dfccb", 202 | "sha256:81ec4dbb696e095057b2528d11a8da04be6bbe2b967fa07d4ea9ba6354338cbf", 203 | "sha256:850cbda0ec5d24c39e7215ede410276040692ca45d105fbbeada407fa03f0ac0", 204 | "sha256:85ac4e62eb44428cde025fd9ab7554002315fc7880f791c553fc5a0015cc9931", 205 | "sha256:8a42dc220eb5305f470855c9284f4c8e85ae59d6d742cd07946b0cbe5e9ca186", 206 | "sha256:9053c2f655589545be08b9d6a673e92970173a4bf11a4b9f18cd6e9af626b587", 207 | "sha256:90d10d9038ba46a595a223a34f136c9230e3d6d7abc2433dbf0e1c31939d3a8b", 208 | "sha256:9867f2817b1a0c93c523f89ac6c9d8625548af4620a7ce438bf5a76e23327284", 209 | "sha256:9a3d60fb8d46ede26c1907c146561b3a9caa20a7aff961bc661ef8226f85a2e9", 210 | "sha256:9f2aefa8a37bd2c4db1a3f1aca11377e2766214520fb70e67071f4ff8d8b0fa5", 211 | "sha256:a0256f8786ac9e4db618a1aa492bb3472569a0946fd3ee862ffe23196323da55", 212 | "sha256:aac98ecad8f7bd4301855669d42a5d97ef7bb34bec2b1e74c7a0641d47e313cf", 213 | "sha256:c026bdf5c1366ce88b7bbe2d8207374d675afd3fd911f60752103de3da4a41d2", 214 | "sha256:c39a3656576b6fdaaf28abe0467f7a7231df4230c1bee132322dbc3209419e7f", 215 | "sha256:cdd020cb68b51462983b7c2dfbc3eb6ede032b8bf438d4554df0c3f08ce35c76", 216 | "sha256:d2a134756f4db34759a5cc7f7e43f7eb87540b68d1cca62925593c6fb93924f7", 217 | "sha256:dbde4004a0688400036342ff73e3706e8940483e2871547b1354d59e93a38277", 218 | "sha256:dc771d4db5701f280957bbcee91745e0686d00ed1c6aa7e05ba30a58b02d70a1", 219 | "sha256:dfb6f6120587b8e228a3cae5ee4985b5bdc18501bad05c49df61965dfc9d70a9", 220 | "sha256:e868cd6feb3ef07d4b35be104fe1fd0657db05259ff8f8ec5e08f4f89ca1191d", 221 | "sha256:ec9aab2fb6783c7fc54bc28f58eb75f1ca77594e6b0fd5e5e7a8114a95169fe0", 222 | "sha256:ed85a0291fff45b67f2557fe7f117d3bc7af8b54b8619d27bf374b5c8b7e3ca2", 223 | "sha256:f3ac06703c412f8167a9062eaf6099409967e33bf98fa5b02be4b4689b6bdf39", 224 | "sha256:f3da5240211252fc70a6451fe00c143e2ab2f7bfc2445695ad2ed056b8e48d96", 225 | "sha256:f54081b08419a39221cd646363b5708857c696b3ad4784f1dcf310891e33a5f7", 226 | "sha256:f64f8ab22d27d4a5693310748d35a696061c3b5c7b8c4fb4ab3b4bc1068b6b56", 227 | "sha256:f717cce5093e6b6049d9ea6d12fdf3658efdb1a80772f7737db1f8510b876df6", 228 | "sha256:fb81ff861692111fa81bd85f64584e624cb4013bd66fbce8a209b8893f5ce398" 229 | ], 230 | "index": "pypi", 231 | "version": "==1.57.0" 232 | }, 233 | "idna": { 234 | "hashes": [ 235 | "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", 236 | "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" 237 | ], 238 | "markers": "python_version >= '3.5'", 239 | "version": "==3.4" 240 | }, 241 | "loguru": { 242 | "hashes": [ 243 | "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1", 244 | "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3" 245 | ], 246 | "index": "pypi", 247 | "version": "==0.7.0" 248 | }, 249 | "pefile": { 250 | "hashes": [ 251 | "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", 252 | "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6" 253 | ], 254 | "markers": "sys_platform == 'win32'", 255 | "version": "==2023.2.7" 256 | }, 257 | "plyer": { 258 | "hashes": [ 259 | "sha256:1b1772060df8b3045ed4f08231690ec8f7de30f5a004aa1724665a9074eed113", 260 | "sha256:65b7dfb7e11e07af37a8487eb2aa69524276ef70dad500b07228ce64736baa61" 261 | ], 262 | "index": "pypi", 263 | "version": "==2.1.0" 264 | }, 265 | "protobuf": { 266 | "hashes": [ 267 | "sha256:44825e963008f8ea0d26c51911c30d3e82e122997c3c4568fd0385dd7bacaedf", 268 | "sha256:567fe6b0647494845d0849e3d5b260bfdd75692bf452cdc9cb660d12457c055d", 269 | "sha256:5ab19ee50037d4b663c02218a811a5e1e7bb30940c79aac385b96e7a4f9daa61", 270 | "sha256:5d0ceb9de6e08311832169e601d1fc71bd8e8c779f3ee38a97a78554945ecb85", 271 | "sha256:6c817cf4a26334625a1904b38523d1b343ff8b637d75d2c8790189a4064e51c3", 272 | "sha256:81cb9c4621d2abfe181154354f63af1c41b00a4882fb230b4425cbaed65e8f52", 273 | "sha256:82e6e9ebdd15b8200e8423676eab38b774624d6a1ad696a60d86a2ac93f18201", 274 | "sha256:8bb52a2be32db82ddc623aefcedfe1e0eb51da60e18fcc908fb8885c81d72109", 275 | "sha256:a38400a692fd0c6944c3c58837d112f135eb1ed6cdad5ca6c5763336e74f1a04", 276 | "sha256:a6b1ca92ccabfd9903c0c7dde8876221dc7d8d87ad5c42e095cc11b15d3569c7", 277 | "sha256:ae7a1835721086013de193311df858bc12cd247abe4ef9710b715d930b95b33e", 278 | "sha256:ae97b5de10f25b7a443b40427033e545a32b0e9dda17bcd8330d70033379b3e5", 279 | "sha256:e8834ef0b4c88666ebb7c7ec18045aa0f4325481d724daa624a4cf9f28134653" 280 | ], 281 | "markers": "python_version >= '3.7'", 282 | "version": "==4.24.0" 283 | }, 284 | "pyinstaller": { 285 | "hashes": [ 286 | "sha256:0df43697c4914285ecd333be968d2cd042ab9b2670124879ee87931d2344eaf5", 287 | "sha256:1fde4381155f21d6354dc450dcaa338cd8a40aaacf6bd22b987b0f3e1f96f3ee", 288 | "sha256:24009eba63cfdbcde6d2634e9c87f545eb67249ddf3b514e0cd3b2cdaa595828", 289 | "sha256:28d9742c37e9fb518444b12f8c8ab3cb4ba212d752693c34475c08009aa21ccf", 290 | "sha256:2d03419904d1c25c8968b0ad21da0e0f33d8d65716e29481b5bd83f7f342b0c5", 291 | "sha256:3a331951f9744bc2379ea5d65d36f3c828eaefe2785f15039592cdc08560b262", 292 | "sha256:5e446df41255e815017d96318e39f65a3eb807e74a796c7e7ff7f13b6366a2e9", 293 | "sha256:78975043edeb628e23a73fb3ef0a273cda50e765f1716f75212ea3e91b09dede", 294 | "sha256:7fdd319828de679f9c5e381eff998ee9b4164bf4457e7fca56946701cf002c3f", 295 | "sha256:9fc27c5a853b14a90d39c252707673c7a0efec921cd817169aff3af0fca8c127", 296 | "sha256:cd7d5c06f2847195a23d72ede17c60857d6f495d6f0727dc6c9bc1235f2eb79c", 297 | "sha256:e5fb17de6c325d3b2b4ceaeb55130ad7100a79096490e4c5b890224406fa42f4" 298 | ], 299 | "index": "pypi", 300 | "version": "==5.13.0" 301 | }, 302 | "pyinstaller-hooks-contrib": { 303 | "hashes": [ 304 | "sha256:596a72009d8692b043e0acbf5e1b476d93149900142ba01845dded91a0770cb5", 305 | "sha256:aa6d7d038814df6aa7bec7bdbebc7cb4c693d3398df858f6062957f0797d397b" 306 | ], 307 | "markers": "python_version >= '3.7'", 308 | "version": "==2023.6" 309 | }, 310 | "pyuac": { 311 | "hashes": [ 312 | "sha256:cf0e1de597388744daa308e4f83019885caa0c646246a7e94713a77652701600", 313 | "sha256:fd11caf47246e0a5149284ced014c346439ae3a28c6bad0b622b239fe30d2d5b" 314 | ], 315 | "index": "pypi", 316 | "version": "==0.0.3" 317 | }, 318 | "pywebio": { 319 | "hashes": [ 320 | "sha256:6177b20b17de821ea580515438ca9dbcdc436a6e1eb17f9c5e357531fb87a855" 321 | ], 322 | "index": "pypi", 323 | "version": "==1.8.2" 324 | }, 325 | "pywin32": { 326 | "hashes": [ 327 | "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", 328 | "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65", 329 | "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", 330 | "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", 331 | "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4", 332 | "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", 333 | "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", 334 | "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36", 335 | "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", 336 | "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", 337 | "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802", 338 | "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a", 339 | "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", 340 | "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0" 341 | ], 342 | "index": "pypi", 343 | "version": "==306" 344 | }, 345 | "pywin32-ctypes": { 346 | "hashes": [ 347 | "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60", 348 | "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7" 349 | ], 350 | "markers": "sys_platform == 'win32'", 351 | "version": "==0.2.2" 352 | }, 353 | "requests": { 354 | "hashes": [ 355 | "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", 356 | "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" 357 | ], 358 | "index": "pypi", 359 | "version": "==2.31.0" 360 | }, 361 | "setuptools": { 362 | "hashes": [ 363 | "sha256:d59c97e7b774979a5ccb96388efc9eb65518004537e85d52e81eaee89ab6dd91", 364 | "sha256:e13e1b0bc760e9b0127eda042845999b2f913e12437046e663b833aa96d89715" 365 | ], 366 | "markers": "python_version >= '3.8'", 367 | "version": "==68.1.0" 368 | }, 369 | "tee": { 370 | "hashes": [ 371 | "sha256:16fdbb34b1e89a308094d86d2d497f5fa5ac47df9202f3dde913070d31699fad", 372 | "sha256:801dea18fe5046b57c26e87aa5c7ca0d1b8590cdc0e4e84158d07cdfa6628cdb" 373 | ], 374 | "version": "==0.0.3" 375 | }, 376 | "tornado": { 377 | "hashes": [ 378 | "sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f", 379 | "sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5", 380 | "sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d", 381 | "sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3", 382 | "sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2", 383 | "sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a", 384 | "sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16", 385 | "sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a", 386 | "sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17", 387 | "sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0", 388 | "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe" 389 | ], 390 | "markers": "python_version >= '3.8'", 391 | "version": "==6.3.3" 392 | }, 393 | "tqdm": { 394 | "hashes": [ 395 | "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386", 396 | "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7" 397 | ], 398 | "index": "pypi", 399 | "version": "==4.66.1" 400 | }, 401 | "ua-parser": { 402 | "hashes": [ 403 | "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950", 404 | "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e" 405 | ], 406 | "version": "==0.18.0" 407 | }, 408 | "urllib3": { 409 | "hashes": [ 410 | "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11", 411 | "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4" 412 | ], 413 | "markers": "python_version >= '3.7'", 414 | "version": "==2.0.4" 415 | }, 416 | "user-agents": { 417 | "hashes": [ 418 | "sha256:a98c4dc72ecbc64812c4534108806fb0a0b3a11ec3fd1eafe807cee5b0a942e7", 419 | "sha256:d36d25178db65308d1458c5fa4ab39c9b2619377010130329f3955e7626ead26" 420 | ], 421 | "version": "==2.2.0" 422 | }, 423 | "win32-setctime": { 424 | "hashes": [ 425 | "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2", 426 | "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad" 427 | ], 428 | "markers": "sys_platform == 'win32'", 429 | "version": "==1.1.0" 430 | } 431 | }, 432 | "develop": {} 433 | } 434 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | import os 4 | import time 5 | import subprocess 6 | import threading #多进程库 7 | from pathlib import Path #路径库 8 | import platform#获取系统信息 9 | 10 | from pyuac import isUserAdmin,runAsAdmin#获取管理员权限 11 | import grpc 12 | from loguru import logger#日志库 13 | import requests 14 | import pywebio as io 15 | out = io.output 16 | ioin = io.input 17 | pin = io.pin 18 | 19 | import core #导入python核心接口 20 | 21 | from grpc_core import task_pb2 22 | from grpc_core import task_pb2_grpc 23 | ######TODO 防止核心和前端任务不同步,暂时删除下载列表保存功能 24 | data = {} 25 | data['need_down_list'] = [] 26 | data['fin_down_list'] = [] 27 | #获取当前平台 28 | system_type = platform.system()#系统名称 29 | system_bit = platform.machine()#操作系统位数 30 | #启动时间 31 | local_time = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime()) 32 | #设置窗口标题 33 | if system_type == 'Windows': 34 | os.system('title Jithon 3.2.2 Beta') 35 | 36 | #挂载核心 37 | @logger.catch 38 | def start_core(): 39 | """ 40 | 挂载核心 41 | """ 42 | while 1==1: 43 | logger.info("核心已启动") 44 | #os.popen('taskkill /f /t /im "JiJiDownCore-win64.exe"')#关闭核心 45 | log_time = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime()) 46 | if system_type == 'Windows':#如果平台为windows 47 | exe_path = str(Path('resources/JiJiDownCore-win64.exe').resolve()) 48 | elif system_type == 'Linux': 49 | if system_bit == 'AMD64' or system_bit == 'x86_64':#如果系统为x86平台 50 | exe_path = str(Path('resources/JiJiDownCore-linux-amd64').resolve()) 51 | elif system_bit == 'aarch64':#如果系统为arm64平台 52 | exe_path = str(Path('resources/JiJiDownCore-linux-arm64').resolve()) 53 | log_path = str(Path('log/core_'+log_time+'.log').resolve()) 54 | #os.chdir(str(Path('resources').resolve())) 55 | #input(exe_path) 56 | with open(log_path,'w+',encoding='UTF-8') as f: 57 | subprocess.run(exe_path,shell=True,stdout=f,text=True,cwd=str(Path('resources').resolve()))#启动核心且设置工作目录为resources 58 | logger.error('核心关闭,尝试重启,检查异常信息') # log 59 | with open(log_path,'r',encoding='UTF-8') as f: 60 | return_data = f.read() 61 | error = 0 62 | if 'nice try' in return_data: 63 | logger.warning('系统时间错误,尝试校正时间') 64 | logger.info('启动w32time') 65 | os.popen('net start w32time&w32tm /config /manualpeerlist:"cn.ntp.org.cn ntp.ntsc.ac.cn" /syncfromflags:manual /reliable:yes /update') 66 | logger.info('添加授时服务器') 67 | os.popen('w32tm /config /manualpeerlist:"cn.ntp.org.cn ntp.ntsc.ac.cn" /syncfromflags:manual /reliable:yes /update') 68 | elif 'An attempt was made to access a socket in a way forbidden by its access permissions.' in return_data: 69 | logger.warning('系统抽风,端口被占用,尝试关闭ICS服务(共享网络或虚拟网卡会导致此错误)') 70 | os.popen('net stop SharedAccess') 71 | elif 'External controller gRPC listen error' in return_data: 72 | logger.warning('核心gRPC端口被占用(localhost:64000)') 73 | error = 1 74 | elif 'External controller listen error' in return_data: 75 | logger.warning('核心RESTful端口被占用(localhost:64001)') 76 | error = 1 77 | elif error == 1:#端口被占用 78 | logger.info('尝试修复端口占用问题') 79 | if system_type == 'Windows':#如果平台为windows 80 | os.popen('taskkill /f /t /im "JiJiDownCore-win64.exe"')#关闭核心 81 | logger.info('修复完成,尝试重启核心') 82 | time.sleep(3) 83 | continue 84 | 85 | #检查下载路径 86 | @logger.catch 87 | def check_dir(): 88 | """ 89 | 检查下载路径 90 | """ 91 | new_dir = pin.pin['change_dir'] 92 | if os.path.exists(new_dir): 93 | core.change_down_dir(new_dir)#修改路径 94 | out.toast('修改成功',color='success',duration='0')#弹出错误弹窗 95 | return 96 | out.toast('路径无效',color='error',duration='0')#弹出错误弹窗 97 | return 98 | 99 | #校验输入是否正确 100 | @logger.catch 101 | def check_input_url(url): 102 | #判断输入url类型 103 | if "BV" in url: 104 | video_info = re.findall('BV(..........)',url)[0] 105 | return 106 | elif "av" in url: 107 | video_info = re.findall('av(\d*)',url)[0] 108 | return 109 | elif "b23.tv" in url: 110 | url = re.findall('(https://b23.tv/.......)',url)[0] 111 | video_info = requests.get(url,allow_redirects=False).text#转换为网页版链接 112 | check_input_url(video_info)#重新检查 113 | elif "ep" in url: 114 | video_info = re.findall('ep(\d*)',url)[0] 115 | return 116 | elif "ss" in url: 117 | video_info = re.findall('ss(\d*)',url)[0] 118 | return 119 | elif "media/md" in url:#https://www.bilibili.com/bangumi/media/md28234644 120 | video_info = re.findall('md(\d*)',url)[0] 121 | return 122 | elif "space.bilibili.com" in url:#https://space.bilibili.com/1118188465 123 | video_info = re.findall('space.bilibili.com/(\d*)',url)[0] 124 | return 125 | #收藏夹处理 126 | elif "favlist?fid=" in url:#https://space.bilibili.com/1118188465/favlist?fid=1228786865 127 | video_info = re.findall('fid=(\d*)',url)[0] 128 | return 129 | #up主合集处理 130 | elif "?sid=" in url:#https://space.bilibili.com/1118188465/channel/collectiondetail?sid=28413 131 | video_info = re.findall('?sid=(\d*)',url)[0] 132 | return 133 | return '链接无效' 134 | 135 | #校验cookies是否正确 136 | @logger.catch 137 | def check_cookies(cookies:str): 138 | pass 139 | 140 | #检查任务状态类型 141 | @logger.catch 142 | def get_task_status(data:int) -> str: 143 | """ 144 | 从代码查找对应的任务状态 145 | """ 146 | if data == 0: 147 | return '错误' 148 | if data == 1: 149 | return '暂停' 150 | if data == 2: 151 | return '运行中' 152 | if data == 3: 153 | return '等待' 154 | if data == 4: 155 | return '正在进行合并' 156 | if data == 5: 157 | return '正在提取MP3' 158 | if data == 6: 159 | return '完成' 160 | 161 | #获取被勾选视频数据 162 | @logger.catch 163 | def get_video_list_info(num:int) -> list: 164 | """ 165 | 获取被勾选的视频数据 166 | """ 167 | info_list = [] 168 | for checkbox_one in range(1,num+1): 169 | if len(pin.pin['check_'+str(checkbox_one)]) != 0:#判断是否勾选了至少一个 170 | info_list.append(pin.pin['check_'+str(checkbox_one)][0]) 171 | return info_list 172 | 173 | #获取选择的清晰度 174 | @logger.catch 175 | def get_choice_quality() -> dict: 176 | """ 177 | 获取选择的视频清晰度 178 | 返回值为{'video','audio'} 179 | """ 180 | return_dict = {} 181 | return_dict['video'] = pin.pin['select_video'] 182 | return_dict['audio'] = pin.pin['select_audio'] 183 | return return_dict 184 | 185 | #创建清晰度列表 186 | @logger.catch 187 | def get_video_quality_list(data:dict) -> list: 188 | """ 189 | 返回[视频清晰度,音频清晰度] 190 | """ 191 | out_audio_list = [] 192 | out_video_list = [] 193 | out_audio_list.append({'label':'默认最高音质','value':{'quality_id':30280,'codec':'AAC'}})#默认选中 194 | out_video_list.append({'label':'默认最高画质','value':{'quality_id':0,'codec':2,'api_type':0,'codec_text':'H265'}})#默认选中 195 | for type in ['WEB','TV','APP']:#遍历不同接口 196 | for one_info in data['audio'][type]:#处理返回数据 197 | label = type+' '+one_info['quality_text']+' - ['+one_info['codec']+'] '+one_info['bit_rate']+' '+one_info['stream_size']#选项标签 198 | value = one_info#选项值 199 | return_dict = {'label':label,'value':value} 200 | out_audio_list.append(return_dict) 201 | for one_info in data['video'][type]:#处理返回数据 202 | label = type+' '+one_info['quality_text']+' - ['+one_info['codec_text']+'] '+one_info['bit_rate']+' '+one_info['stream_size']#选项标签 203 | if type == 'WEB': 204 | one_info['api_type'] = 0 205 | elif type == 'TV': 206 | one_info['api_type'] = 1 207 | elif type == 'APP': 208 | one_info['api_type'] = 2 209 | value = one_info#选项值 210 | return_dict = {'label':label,'value':value} 211 | out_video_list.append(return_dict) 212 | return [out_video_list,out_audio_list] 213 | 214 | #处理下载文件名 215 | @logger.catch 216 | def make_down_name(info_date:dict,video_data:dict) -> str: 217 | """ 218 | 下载文件名处理 219 | 预留接口 220 | """ 221 | info_date['title'] = re.sub(r'[\/:*?"<>|]', "_", info_date['title']) 222 | return info_date['title'] 223 | 224 | #显示视频下载选择界面 225 | @logger.catch 226 | def print_video_info(info) -> list: 227 | """ 228 | 创建视频下载界面 229 | 返回[视频信息,需下载视频列表,清晰度] 230 | """ 231 | def enchange(list):#rpc对象转json对象 232 | return_all_list = [] 233 | for one in list: 234 | return_list = {} 235 | return_list['avid'] = one.page_av 236 | return_list['bvid'] = one.page_bv 237 | return_list['cid'] = one.page_cid 238 | return_list['index'] = one.page_index 239 | return_list['info'] = one.page_info 240 | return_list['title'] = one.page_title 241 | return_all_list.append(return_list) 242 | return return_all_list 243 | 244 | with out.use_scope('video_info'):#进入视频信息域 245 | data = info 246 | with out.use_scope('up_info'):#切换到up信息域 247 | face = data['up_face']#缓存图片 248 | out.put_row([ 249 | None, 250 | out.put_image(face,height='50px').style('margin-top: 1rem;margin-bottom: 1rem;border:1px solid;border-radius:50%;box-shadow: 5px 5px 5px #A9A9A9'), 251 | None, 252 | out.put_column([ 253 | None, 254 | out.put_text(data['up_name']).style('font-size:1.25em;line-height: 0'), 255 | ],size='1fr 1fr'), 256 | None 257 | ],size='1fr 50px 25px auto 1fr').style('border: 1px solid #e9ecef;border-radius: .25rem')#限制up头像大小 258 | info_title = out.put_text(data['video_title']).style('border: 1px solid #e9ecef;border-radius: .25rem;font-size:2em;font-weight:bold;text-align:center')#视频标题 259 | if data['video_cover'] != '': 260 | cover = data['video_cover']#缓存图片 261 | info_cover = out.put_image(cover).style('border-radius: 30px;box-shadow: 15px 15px 30px #bebebe,-15px -15px 30px #ffffff;')#视频封面 262 | info_desc = out.put_text(data['video_desc']).style('border: 1px solid #e9ecef;border-radius: .25rem')#视频简介 263 | info_two = out.put_column([ 264 | None, 265 | out.put_text('类型 '+str(data['sort'])+'-'+str(data['sub_sort'])).style('text-align:center'), 266 | None, 267 | out.put_text('发布时间 '+data['bili_pubdate_str']).style('text-align:center;line-height:0.5'), 268 | None 269 | ],size='0.5em auto 0.5em auto 0.5em').style('border: 1px solid #e9ecef;border-radius: .25rem') 270 | if data['video_cover'] != '': 271 | out.put_column([info_title,None,info_cover,None,info_desc,None,info_two],size='auto 1em auto 2em auto 1em auto').style('margin-top: 0.5rem;margin-bottom: 0.5rem')#设置为垂直排布 272 | else: 273 | out.put_column([info_title,None,info_desc,None,info_two],size='auto 10px auto 10px auto').style('margin-top: 0.5rem;margin-bottom: 0.5rem')#设置为垂直排布 274 | 275 | #创建清晰度选择 276 | get_video_quality_info = get_video_quality_list(core.quality(bvid=data['block'][0].list[0].page_bv,cid=data['block'][0].list[0].page_cid))#读取视频列表第一个的清晰度 277 | with out.use_scope('quality'):#创建清晰度选择域 278 | out.put_column([ 279 | pin.put_select(name='select_video',options=get_video_quality_info[0],value={'quality_id':1000,'codec':2,'api_type':2}),#创建视频选择 280 | pin.put_select(name='select_audio',options=get_video_quality_info[1],value={'quality_id':30280})#创建音频选择 281 | ]) 282 | #创建列表 283 | table_list = [] 284 | num = 0 285 | table_list.append(['选择','标题']) 286 | for one in data['list']:#遍历列表 287 | num += 1 288 | list_one = [] 289 | need_data = {'bvid':one.page_bv,'cid':one.page_cid,'title':one.page_title,'info':[one.page_info[0],one.page_info[1]]}#把rpc数据json化 290 | list_one.append(pin.put_checkbox(name='check_'+str(num),options=[{'label':num,'value':need_data}]).style('line-height:0.5'))#创建单选框 291 | list_one.append(out.put_text(one.page_title).style('width:100%'))#标题 292 | table_list.append(list_one) 293 | out.put_table(table_list,position=-1) 294 | 295 | #移除加载提示 296 | out.clear('load') 297 | out.remove('load') 298 | #使页面滚动到分辨率选择置顶 299 | out.scroll_to('quality','top') 300 | #创建下载控制组件 301 | control_buttom = ioin.actions(buttons=[{'label':'开始下载','value':'0'},{'label':'下载全部','value':'1'},{'label':'退出界面','value':'2','color':'danger'}])#创建按键 302 | need_quality:dict = get_choice_quality()#获取选择的清晰度 303 | if control_buttom == '0':#如果选择下载被勾选部分 304 | need_video_list:list = get_video_list_info(len(data['list']))#获取被勾选的视频列表 305 | logger.debug('有 '+str(len(need_video_list))+'/'+str(len(data['list']))+' 个视频被勾选') # log 306 | elif control_buttom == '1':#如果选择下载全部 307 | logger.debug('所有视频被选中') # log 308 | need_video_list:list = data['list'] 309 | need_video_list = enchange(need_video_list) 310 | elif control_buttom == '2':#如果选择退出 311 | logger.debug('选择退出') # log 312 | return 'out' 313 | if len(need_video_list) == 0:#如果未选择任何视频 314 | logger.debug('未选择任何视频') # log 315 | out.toast('未选择任何视频',color='info',duration='0')#弹出错误弹窗 316 | return 'out' 317 | return [data,need_video_list,need_quality] 318 | 319 | #监视下载任务进程 320 | @logger.catch 321 | def watch_status(task_id:str,name:str): 322 | def del_task(): 323 | core.delete_task(task_id)#向核心发送删除任务消息 324 | out.remove(task_id) 325 | return 326 | def resume_task(): 327 | core.patch_resume_task(task_id)#继续任务 328 | watch_status(task_id=task_id,name=name) 329 | #连接到核心 330 | try: 331 | stub = task_pb2_grpc.TaskStub(core.channel) 332 | response = stub.Status(task_pb2.TaskStatusReq(task_id=task_id),metadata=core.metadata) 333 | logger.info(name+' 加入任务监视阵列')# log 334 | #with out.use_scope('down_work'):#进入downwork建立显示区域 335 | #out.put_scope(task_id)# 定义域 336 | need_print = out.put_row([ 337 | None, 338 | out.put_column([ 339 | None, 340 | out.put_text(name).style('text-align:left;font-size:1em;max-height:3em'),#TODO 创建标题 341 | out.put_scope('sta_text_'+task_id).style('text-align:center;font-size:0.875em;max-height:2em'),#创建用于进度提示的域,控制值为'sta_text_'+list_one['control_name'] 342 | out.put_processbar('bar_'+task_id,init=0).style('width:100%;height=1em'),#下载列表,控制值为list_one['control_name'] 343 | ],size='0.5fr 1fr 1fr 1fr 0.5fr'), 344 | out.put_column([ 345 | None, 346 | out.put_scope('sta_worktype_'+task_id), 347 | None 348 | ],size='1fr auto 1fr').style('height=100%'),#创建用于显示任务状态的域,控制值为'sta_worktype_'+list_one['control_name'] 349 | out.put_column([ 350 | None, 351 | out.put_button('暂停',onclick=lambda:core.patch_pause_task(task_id),color='warning').style('margin-left: auto;margin-right: auto'),#暂停按钮 352 | out.put_button('继续',onclick=lambda:resume_task(),color='success'),#继续按钮 353 | out.put_button('删除',onclick=lambda:del_task(),color='danger'), 354 | None 355 | ])#删除按钮 356 | ],size='10px 80% 10% 10%').style('border: 1px solid #ccc')# 创建显示 357 | 358 | with out.use_scope('down_work'): 359 | with out.use_scope(task_id,clear=True): 360 | out.put_scope('bar_'+task_id,need_print)#写入初始标题及进度条 361 | for sec_print in response: 362 | try:#监视流 363 | out.set_processbar('bar_'+task_id,value= sec_print.progress / 100)#刷新进度条状态 364 | with out.use_scope('sta_text_'+task_id,clear=True):#进入用于进度提示的域并清空 365 | out.put_text(sec_print.text)#显示进度提示文本 366 | with out.use_scope('sta_worktype_'+task_id,clear=True):#进入用于显示任务状态提示文本的域并清空 367 | out.put_column([ 368 | out.put_text(get_task_status(int(sec_print.task_status))).style('text-align:center;font-size:0.5em'), 369 | None, 370 | out.put_text(sec_print.average_speed).style('text-align:center;font-size:0.5em'),#显示任务状态提示文本 371 | ],size='auto auto auto auto auto') 372 | if sec_print.task_status == 6:#任务完成 373 | logger.success(name+' 下载完成')# log 374 | core.notify(title='主人~下载完成了喵~',message=name+' 下载完成~') 375 | return 376 | 377 | except Exception as e:#如果核心突然断联 378 | if e.details() == "Stream removed":#流终止 379 | logger.error('线程出现致命错误 '+e.details()) 380 | time.sleep(1) 381 | continue#试图重试 382 | except Exception as e: 383 | logger.error('线程出现错误,无法获取下载进度') 384 | #time.sleep(1) 385 | #watch_status(task_id=task_id,name=name)#试图重试 386 | 387 | #解析输入的url 388 | @logger.catch 389 | def start_url(): 390 | url = pin.pin['url_input']#获取输入 391 | check_return = check_input_url(url) 392 | if check_return != None: 393 | out.toast(check_return,duration=3,color='error')#显示错误信息 394 | return 395 | #清除旧显示内容 396 | out.clear('video_info') 397 | out.clear('posting')#任务发送进度条 398 | with out.use_scope('main'): 399 | #显示加载提示 400 | with out.use_scope('load'): 401 | out.put_row([ 402 | out.put_loading(), 403 | None, 404 | out.put_text('解析中'), 405 | ],size='1fr 20px 1fr') 406 | 407 | #解析url 408 | get_video_info = core.info(url) 409 | out.remove('load')#移除加载提示 410 | if get_video_info['error']:#出现权限问题 411 | logger.warning('解析失败') 412 | out.toast('解析失败',duration=3,position='center',color='warn') 413 | out.remove('posting') 414 | out.clear('video_info')#清空video_info域 415 | return 416 | return_data = print_video_info(get_video_info)#显示视频信息,返回视频信息 417 | if return_data == 'out':#如果选择退出界面或未勾选任何视频 418 | out.remove('posting') 419 | out.clear('video_info')#清空video_info域 420 | return 421 | #开始下载任务 422 | video_data = return_data[2]['video'] 423 | audio_data = return_data[2]['audio'] 424 | #滚动到最底下 425 | out.scroll_to('main','buttom') 426 | with out.use_scope('posting'): 427 | out.put_column([ 428 | out.put_text('任务发送中').style('line-height: 0.5'), 429 | out.put_processbar('loading',init=0)#设置进度条 430 | ]).style('border: 1px solid #e9ecef;border-radius: .25rem') 431 | num = 0 432 | for need_video in return_data[1]: 433 | return_down_data:dict = core.post_new_task(bvid=need_video['bvid'],cid=need_video['cid'],video_quality=video_data['quality_id'],audio_quality=audio_data['quality_id'],api_type=video_data['api_type'],video_codec=video_data['codec'],save_filename=make_down_name(need_video,return_data[2])) 434 | #set_info['need_down_list'].append({'task_id':return_down_data['task_id'],'video_info':need_video})#添加控制字节段至下载列表 435 | #core.save_json(set_info)#刷新设置里的下载列表 436 | num += 1 437 | out.set_processbar('loading',num / len(return_data[1]))#设置进度条进度 438 | #建立下载任务监视线程 439 | start_watch_status = threading.Thread(target=watch_status,args=(return_down_data['task_id'],need_video['title'],)) 440 | io.session.register_thread(start_watch_status) 441 | start_watch_status.start()#启动监视进程 442 | #写入下载列表 443 | data['need_down_list'].append([return_down_data['task_id'],need_video['title']]) 444 | 445 | out.clear('loading') 446 | out.put_text('任务发送完成') 447 | out.remove('posting') 448 | return 449 | 450 | #主函数 451 | @logger.catch 452 | def main():#主函数 453 | try: 454 | logger.info('网页服务器启动') 455 | if not start_jiji_core.is_alive(): 456 | io.session.register_thread(start_jiji_core) 457 | logger.info('启动核心') 458 | start_jiji_core.start() 459 | with out.use_scope('main'):#创建并进入main域 460 | out.scroll_to('main','top') 461 | #等待核心响应提示 462 | with out.use_scope('load'): 463 | out.put_row([ 464 | None, 465 | out.put_loading(color='primary').style('width:4rem; height:4rem;padding: 15px 0'), 466 | None, 467 | out.put_column([ 468 | None, 469 | out.put_text('等待核心响应,请检查核心是否启动').style('line-height: 0'), 470 | ],size='1fr 1fr'), 471 | None 472 | ],size='1fr auto 20px auto 1fr') 473 | #检查登录 474 | while 1==1: 475 | user_info = core.get_user_info() 476 | if user_info['code'] != -1:#服务器启动失败 477 | break 478 | logger.debug('核心启动失败,重试') 479 | time.sleep(3) 480 | if not start_jiji_core.is_alive(): 481 | io.session.register_thread(start_jiji_core) 482 | logger.info('启动核心') 483 | start_jiji_core.start() 484 | #获取操作系统 485 | system_info = core.status_ping() 486 | logger.info('当前服务器名称 '+system_info['server_name']) # log 487 | logger.info('当前操作系统名称 '+system_info['os_system_name']) # log 488 | out.clear('load')#清空加载界面 489 | out.remove('load')#删除加载界面 490 | #初始化 491 | out.scroll_to('main','top') 492 | out.clear('main') 493 | if user_info['code'] == 0:#如果已登录 494 | with out.use_scope('user_info'):#切换到用户信息域 495 | logger.debug('核心已连接') # log 496 | face = user_info['face']#缓存图片 497 | put_user = '当前登录用户: '+user_info['uname'] 498 | if user_info['vip_label_text'] != '': 499 | put_user += ' ['+user_info['vip_label_text']+']' 500 | if user_info['badge'] != '': 501 | put_user += ' ['+user_info['badge']+']' 502 | logger.debug(put_user) # log 503 | out.put_row([ 504 | None, 505 | out.put_image(face,height='50px').style('margin-top: 1rem;margin-bottom: 1rem;border:1px solid;border-radius:50%;box-shadow: 5px 5px 5px #A9A9A9'), 506 | None, 507 | out.put_column([ 508 | None, 509 | out.put_text(put_user).style('font-size:1.25em;line-height: 0'), 510 | ],size='1fr 1fr'), 511 | None 512 | ],size='1fr 50px 25px auto 1fr').style('border: 1px solid #e9ecef;border-radius: .25rem')#限制用户头像大小 513 | elif user_info['code'] == 1: 514 | logger.debug('核心已连接') # log 515 | #创建选择弹窗 516 | qr_type = ioin.actions('选择登录接口',[ 517 | {'label':'WEB接口','value':0,'color':'primary','type':'submit'}, 518 | {'label':'TV接口','value':1,'color':'primary','type':'submit'}, 519 | {'label':'cookies登录','value':2,'color':'primary','type':'submit'} 520 | ],help_text='WEB接口仅支持WEB接口下载,TV接口支持全接口下载') 521 | if qr_type == 0 or qr_type == 1: 522 | return_data = core.get_login_status(qr_type)#获取二维码 523 | with out.use_scope('login'): 524 | out.put_row([ 525 | None, 526 | out.put_column([ 527 | None, 528 | out.put_text('请扫描二维码登录').style('line-height: 1;text-align:center'), 529 | None, 530 | out.put_image(return_data['image'],width='50%').style('margin: auto'), 531 | ],size='10px auto 10px auto'), 532 | None 533 | ],size='20px auto 20px').style('border: 1px solid #e9ecef;border-radius: .25rem;box-shadow: 5px 5px 5px #A9A9A9') 534 | check_user_info = core.get_qr_status(id=return_data['id'])#循环检查是否扫描 535 | if check_user_info == 1:#如果登录成功 536 | out.clear('login') 537 | out.remove('login') 538 | return 539 | elif check_user_info == 2:#如果二维码失效 540 | logger.warning('二维码失效,1秒后重试') # log 541 | time.sleep(1) 542 | main() 543 | elif check_user_info == 0:#如果出现未知错误 544 | logger.warning('二维码出现未知错误,1秒后重试') # log 545 | time.sleep(1) 546 | main() 547 | elif qr_type == 2:#cookies登录 548 | def check(): 549 | if core.check_cookies(pin.pin['cookies_input'],pin.pin['accesstoken_input']) == 1:#检查不通过 550 | return 551 | core.send_cookies(pin.pin['cookies_input'],pin.pin['accesstoken_input']) 552 | out.close_popup() 553 | main() 554 | def auto(): 555 | login_auto() 556 | main() 557 | with out.popup('cookies登录',closable=False) as s: 558 | def login_auto(): 559 | core.get_hack_cookies() 560 | out.close_popup() 561 | main() 562 | out.put_column([ 563 | pin.put_textarea(name='cookies_input',placeholder='cookies值',help_text='cookies值为必填内容'), 564 | pin.put_textarea(name='accesstoken_input',placeholder='AccessToken值',rows=1,help_text='用于登录 TV、APP 接口,可选填入'), 565 | out.put_row([ 566 | None, 567 | out.put_button('提交cookies',onclick=lambda:check()), 568 | None, 569 | out.put_button('自动登录',onclick=lambda:login_auto()), 570 | None 571 | ],size='1fr auto 10px auto 1fr'), 572 | ],size='auto auto auto') 573 | 574 | #创建横向标签栏 575 | scope_url = out.put_scope('url')#创建url域 576 | scope_set = out.put_scope('set')#创建set域 577 | scope_down = out.put_scope('down')#创建down域 578 | out.put_tabs([{'title':'链接解析','content':scope_url},{'title':'下载列表','content':scope_down},{'title':'设置','content':scope_set}])#创建 579 | 580 | #创建url输入框 581 | with out.use_scope('url'):#进入域 582 | pin.put_input('url_input',label='请输入链接',type='text')#限制类型为url,使用check_input_url检查内容 583 | out.put_button(label='解析链接',onclick=start_url).style('width: 100%')#创建按键 584 | out.put_scope('video_info') 585 | 586 | #创建下载列表 587 | with out.use_scope('down'): 588 | scope_down_work = out.put_scope('down_work') 589 | scope_down_fin = out.put_scope('down_fin') 590 | out.put_tabs([{'title':'下载中','content':scope_down_work}])#创建横向标签栏 591 | 592 | #创建设置 593 | with out.use_scope('set'):#进入域 594 | out.put_text('由于下载地址接口未更新到新协议,暂时不提供实时修改方式') 595 | #out.put_row([out.put_text('目前下载地址:'),pin.put_input(name='change_dir',value=core.get_down_dir()),out.put_button('确认修改',onclick=check_dir)]) 596 | 597 | 598 | #错误处理 599 | except AssertionError:#核心未工作 600 | out.toast('核心貌似没有启动,10秒后重试',position='center',color='error') 601 | logger.error('无法与核心通讯,10秒后重试') 602 | time.sleep(5) 603 | main() 604 | except grpc._channel._MultiThreadedRendezvous as e: 605 | if e.details() == "Stream removed": 606 | logger.warning('核心异常停止') 607 | time.sleep(1) 608 | main() 609 | 610 | if not isUserAdmin(): 611 | logger.warning("当前不是管理员权限,核心错误修复无法工作") 612 | if system_type == 'Windows': 613 | logger.info("以管理员权限重启") 614 | #runAsAdmin() 615 | #sys.exit() 616 | elif system_type == 'Linux': 617 | logger.info("请以管理员权限启动软件") 618 | elif isUserAdmin(): 619 | logger.info("当前是管理员权限") 620 | logger.info('自动更新核心') 621 | core.update_core(system_type,system_bit)#更新核心 622 | core.check_ffmpeg()#检查ffmpeg可用性 623 | logger.info('生成配置文件') 624 | core.make_yaml() 625 | start_jiji_core = threading.Thread(target=start_core)#设置核心线程 626 | io.config(title='Jithon 3.2.2 Beta',description='本应用为唧唧2.0基于python的webui实现',theme='yeti') 627 | logger.info('主程序启动,如未自动跳转请打开http://127.0.0.1:8080') 628 | core.notify(title='启动完成~',message='主程序启动,如未自动跳转请打开http://127.0.0.1:8080') 629 | try: 630 | io.start_server(main,host='0.0.0.0',port=8090,debug=True,cdn=False,auto_open_webbrowser=True) 631 | except KeyboardInterrupt:#程序被手动关闭 632 | logger.info('程序已终止') 633 | exit() --------------------------------------------------------------------------------