podflow 2025.1.26__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. podflow-2025.1.26/PKG-INFO +202 -0
  2. podflow-2025.1.26/Podflow/__init__.py +137 -0
  3. podflow-2025.1.26/Podflow/basic/__init__.py +2 -0
  4. podflow-2025.1.26/Podflow/basic/file_save.py +21 -0
  5. podflow-2025.1.26/Podflow/basic/folder_build.py +16 -0
  6. podflow-2025.1.26/Podflow/basic/get_duration.py +18 -0
  7. podflow-2025.1.26/Podflow/basic/get_file_list.py +57 -0
  8. podflow-2025.1.26/Podflow/basic/get_html_dict.py +30 -0
  9. podflow-2025.1.26/Podflow/basic/http_client.py +75 -0
  10. podflow-2025.1.26/Podflow/basic/list_merge_tidy.py +15 -0
  11. podflow-2025.1.26/Podflow/basic/qr_code.py +55 -0
  12. podflow-2025.1.26/Podflow/basic/split_dict.py +14 -0
  13. podflow-2025.1.26/Podflow/basic/time_format.py +16 -0
  14. podflow-2025.1.26/Podflow/basic/time_stamp.py +61 -0
  15. podflow-2025.1.26/Podflow/basic/vary_replace.py +11 -0
  16. podflow-2025.1.26/Podflow/basic/write_log.py +43 -0
  17. podflow-2025.1.26/Podflow/bilibili/__init__.py +2 -0
  18. podflow-2025.1.26/Podflow/bilibili/build.py +201 -0
  19. podflow-2025.1.26/Podflow/bilibili/get.py +477 -0
  20. podflow-2025.1.26/Podflow/bilibili/login.py +307 -0
  21. podflow-2025.1.26/Podflow/config/__init__.py +2 -0
  22. podflow-2025.1.26/Podflow/config/build_original.py +41 -0
  23. podflow-2025.1.26/Podflow/config/channge_icon.py +163 -0
  24. podflow-2025.1.26/Podflow/config/correct_channelid.py +230 -0
  25. podflow-2025.1.26/Podflow/config/correct_config.py +103 -0
  26. podflow-2025.1.26/Podflow/config/get_channelid.py +22 -0
  27. podflow-2025.1.26/Podflow/config/get_channelid_id.py +21 -0
  28. podflow-2025.1.26/Podflow/config/get_config.py +34 -0
  29. podflow-2025.1.26/Podflow/download/__init__.py +2 -0
  30. podflow-2025.1.26/Podflow/download/convert_bytes.py +18 -0
  31. podflow-2025.1.26/Podflow/download/delete_part.py +20 -0
  32. podflow-2025.1.26/Podflow/download/dl_aideo_video.py +307 -0
  33. podflow-2025.1.26/Podflow/download/show_progress.py +46 -0
  34. podflow-2025.1.26/Podflow/download/wait_animation.py +34 -0
  35. podflow-2025.1.26/Podflow/download/youtube_and_bilibili_download.py +30 -0
  36. podflow-2025.1.26/Podflow/ffmpeg_judge.py +45 -0
  37. podflow-2025.1.26/Podflow/httpfs/__init__.py +2 -0
  38. podflow-2025.1.26/Podflow/httpfs/app_bottle.py +212 -0
  39. podflow-2025.1.26/Podflow/httpfs/port_judge.py +21 -0
  40. podflow-2025.1.26/Podflow/main.py +248 -0
  41. podflow-2025.1.26/Podflow/makeup/__init__.py +2 -0
  42. podflow-2025.1.26/Podflow/makeup/del_makeup_yt_format_fail.py +19 -0
  43. podflow-2025.1.26/Podflow/makeup/make_up_file.py +51 -0
  44. podflow-2025.1.26/Podflow/makeup/make_up_file_format_mod.py +96 -0
  45. podflow-2025.1.26/Podflow/makeup/make_up_file_mod.py +30 -0
  46. podflow-2025.1.26/Podflow/message/__init__.py +2 -0
  47. podflow-2025.1.26/Podflow/message/backup_zip_save.py +45 -0
  48. podflow-2025.1.26/Podflow/message/create_main_rss.py +44 -0
  49. podflow-2025.1.26/Podflow/message/display_qrcode_and_url.py +36 -0
  50. podflow-2025.1.26/Podflow/message/fail_message_initialize.py +165 -0
  51. podflow-2025.1.26/Podflow/message/format_time.py +27 -0
  52. podflow-2025.1.26/Podflow/message/get_original_rss.py +65 -0
  53. podflow-2025.1.26/Podflow/message/get_video_format.py +111 -0
  54. podflow-2025.1.26/Podflow/message/get_video_format_multithread.py +42 -0
  55. podflow-2025.1.26/Podflow/message/get_youtube_and_bilibili_video_format.py +87 -0
  56. podflow-2025.1.26/Podflow/message/media_format.py +195 -0
  57. podflow-2025.1.26/Podflow/message/original_rss_fail_print.py +15 -0
  58. podflow-2025.1.26/Podflow/message/rss_create_hash.py +26 -0
  59. podflow-2025.1.26/Podflow/message/title_correction.py +30 -0
  60. podflow-2025.1.26/Podflow/message/update_information_display.py +72 -0
  61. podflow-2025.1.26/Podflow/message/update_youtube_bilibili_rss.py +116 -0
  62. podflow-2025.1.26/Podflow/message/want_retry.py +21 -0
  63. podflow-2025.1.26/Podflow/message/xml_item.py +83 -0
  64. podflow-2025.1.26/Podflow/message/xml_original_item.py +92 -0
  65. podflow-2025.1.26/Podflow/message/xml_rss.py +46 -0
  66. podflow-2025.1.26/Podflow/netscape/__init__.py +2 -0
  67. podflow-2025.1.26/Podflow/netscape/bulid_netscape.py +44 -0
  68. podflow-2025.1.26/Podflow/netscape/get_cookie_dict.py +21 -0
  69. podflow-2025.1.26/Podflow/parse_arguments.py +80 -0
  70. podflow-2025.1.26/Podflow/remove/__init__.py +2 -0
  71. podflow-2025.1.26/Podflow/remove/remove_dir.py +33 -0
  72. podflow-2025.1.26/Podflow/remove/remove_file.py +23 -0
  73. podflow-2025.1.26/Podflow/youtube/__init__.py +2 -0
  74. podflow-2025.1.26/Podflow/youtube/build.py +287 -0
  75. podflow-2025.1.26/Podflow/youtube/get.py +376 -0
  76. podflow-2025.1.26/Podflow/youtube/login.py +39 -0
  77. podflow-2025.1.26/README.md +187 -0
  78. podflow-2025.1.26/podflow.egg-info/PKG-INFO +202 -0
  79. podflow-2025.1.26/podflow.egg-info/SOURCES.txt +83 -0
  80. podflow-2025.1.26/podflow.egg-info/dependency_links.txt +1 -0
  81. podflow-2025.1.26/podflow.egg-info/entry_points.txt +6 -0
  82. podflow-2025.1.26/podflow.egg-info/requires.txt +10 -0
  83. podflow-2025.1.26/podflow.egg-info/top_level.txt +1 -0
  84. podflow-2025.1.26/setup.cfg +4 -0
  85. podflow-2025.1.26/setup.py +42 -0
@@ -0,0 +1,202 @@
1
+ Metadata-Version: 2.1
2
+ Name: podflow
3
+ Version: 2025.1.26
4
+ Summary: A podcast server that includes YouTube and BiliBili
5
+ Home-page: https://github.com/gruel-zxz/podflow
6
+ Author: gruel_zxz
7
+ Author-email: zhuxizhouzxz@gmail.com
8
+ License: UNKNOWN
9
+ Description: # Podflow
10
+
11
+ <img src='https://raw.githubusercontent.com/gruel-zxz/podflow/main/Podflow.png' alt='logo' style='width: 150px; height: 150px;'/>
12
+
13
+ 建立Podcast服务器,用于下载YouTube和哔哩哔哩的音视频并导入到Podcast中。
14
+
15
+ 需要新建[配置文件](#主配置), 或通过首次运行后自动生成
16
+
17
+ YouTube的cookies需要使用chrome插件导出Netscape格式并保存到channel_data文件夹中
18
+
19
+ PS:可能存在大量未知bug,改进中并尝试加入抖音……
20
+
21
+ ### 安装方法
22
+
23
+ 需要`Python 3.8`以上环境,并安装ffmpeg (安装方法: <https://ffmpeg.org/>)。
24
+
25
+ Podflow可以使用以下命令进行安装
26
+
27
+ ```
28
+ pip install Podflow
29
+ ```
30
+
31
+ 在iOS上可以使用[Shortcuts](<https://apps.apple.com/us/app/shortcuts/id915249334/>)运行Podflow, 需要用到[a-shell](<https://apps.apple.com/us/app/a-shell/id1473805438/>)
32
+ 和[捷径脚本](<https://www.icloud.com/shortcuts/54213ea7e46b4b21b7a0bce02f9c64a1/>)
33
+
34
+ ### 命令行参数说明
35
+
36
+ | 参数 | 选项 | 类型 | 默认值 | 描述 |
37
+ |----------------|-------------------|-----------------|-----------------|--------------------------------------------------|
38
+ | `-n` | `--times` | `int` | +∞ | 次数 |
39
+ | `-d` | `--delay` | `int` | 1500 | 延迟(单位: 秒) |
40
+ | `-c` | `--config` | `string` | "config.json" | 配置文件的路径 |
41
+ | `--shortcuts` | | `string` | [] | 仅适用于捷径APP |
42
+ | `--httpfs` | | `boolean` | 无 | 仅启用服务器功能, 不更新频道 |
43
+
44
+ ### 使用示例
45
+
46
+ 你可以使用以下命令行格式来运行程序:
47
+
48
+ ```
49
+ Podflow -n 24 -d 3600
50
+ ```
51
+
52
+ ---
53
+
54
+ ### 主配置
55
+
56
+ | 参数 | 类型 | 默认值 | 描述 |
57
+ | --------------------- | ----------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
58
+ | `channelid_youtube` | `dict` | `{...}` | 可以按[YouTube频道配置](#YouTube频道配置)编写, 也可以按`{"youtube": "UCBR8-60-B28hp2BmDPdntcQ"}`编写, 其他参数会按默认值自动补全 |
59
+ | `channelid_bilibili` | `dict` | `{...}` | 可以按[哔哩哔哩频道配置](#哔哩哔哩频道配置)编写, 也可以按`{"哔哩哔哩弹幕网": "8047632"}`编写, 其他参数会按默认值自动补全 |
60
+ | `preparation_per_count` | `int` | `100` | 获取媒体信息每组数量 |
61
+ | `completion_count` | `int` | `100` | 媒体缺失时最大补全数量 |
62
+ | `retry_count` | `int` | `5` | 媒体下载重试次数 |
63
+ | `url` | `string` | `"http://127.0.0.1"` | HTTP共享地址 |
64
+ | `port` | `int` | `8000` | HTTP共享端口 |
65
+ | `port_in_url` | `boolean` | `true` | HTTP共享地址是否包含端口 |
66
+ | `httpfs` | `boolean` | `false` | HTTP共享日志 |
67
+ | `title` | `string` | `"Podflow"` | 博客的名称 |
68
+ | `filename` | `string` | `"Podflow"` | 主XML的文件名称 |
69
+ | `link` | `string` | `"https://github.com/gruel-zxz/podflow"` | 博客主页 |
70
+ | `description` | `string` | `"在iOS平台上借助workflow和a-shell搭建专属的播客服务器。"` | 博客信息 |
71
+ | `icon` | `string` | `"https://raw.githubusercontent.com/gruel-zxz/podflow/main/Podflow.png"` | 博客图标 |
72
+ | `category` | `string` | `"TV &amp; Film"` | 博客类型 |
73
+ | `token` | `string` | `""` | token认证, 如为null或""将不启用token |
74
+ | `delete_incompletement` | `boolean` | `false` | 是否删除下载中断媒体(下载前处理流程) |
75
+ | `remove_media` | `boolean` | `true` | 是否删除无用的媒体文件 |
76
+
77
+ ---
78
+
79
+ ### YouTube频道配置
80
+
81
+ | 参数 | 类型 | 默认值 | 描述 |
82
+ | --------------------- | ----------- | ------------------------------------- | -------------------------------------------------------------- |
83
+ | `id` | `string` | `"UCBR8-60-B28hp2BmDPdntcQ"` | 频道ID |
84
+ | `title` | `string` | `"YouTube"` | 频道名称 |
85
+ | `update_size` | `int` | `15` | 每次获取频道媒体数量 |
86
+ | `quality` | `string` | `"480"` | 媒体分辨率(仅在media为视频时有效) |
87
+ | `last_size` | `int` | `50` | 媒体保留数量 |
88
+ | `media` | `string` | `"m4a"` | 下载媒体类型 |
89
+ | `DisplayRSSaddress` | `boolean` | `false` | 是否在Print中显示子博客地址 |
90
+ | `InmainRSS` | `boolean` | `true` | 是否在主博客中 |
91
+ | `QRcode` | `boolean` | `false` | 是否显示子博客地址二维码(仅在DisplayRSSaddress为True时有效) |
92
+ | `BackwardUpdate` | `boolean` | `false` | 是否向后更新 |
93
+ | `BackwardUpdate_size` | `int` | `3` | 向后更新数量(仅在BackwardUpdate为True时有效) |
94
+ | `want_retry_count` | `int` | `8` | 媒体获取失败后多少次后重试 |
95
+ | `NoShorts` | `boolean` | `false` | 是否不下载Shorts媒体 |
96
+ | `title_change` | `list` | `[]` | 标题文本修改规则 |
97
+
98
+ ---
99
+
100
+ ### 哔哩哔哩频道配置
101
+
102
+ | 参数 | 类型 | 默认值 | 描述 |
103
+ | --------------------- | ----------- | ------------------------------------- | -------------------------------------------------------------- |
104
+ | `id` | `string` | `"8047632"` | 频道ID |
105
+ | `title` | `string` | `"哔哩哔哩弹幕网"` | 频道名称 |
106
+ | `update_size` | `int` | `25` | 每次获取频道媒体数量 |
107
+ | `quality` | `string` | `"480"` | 媒体分辨率(仅在media为视频时有效) |
108
+ | `last_size` | `int` | `100` | 媒体保留数量 |
109
+ | `media` | `string` | `"m4a"` | 下载媒体类型 |
110
+ | `DisplayRSSaddress` | `boolean` | `false` | 是否在Print中显示子博客地址 |
111
+ | `InmainRSS` | `boolean` | `true` | 是否在主博客中 |
112
+ | `QRcode` | `boolean` | `false` | 是否显示子博客地址二维码(仅在DisplayRSSaddress为True时有效) |
113
+ | `BackwardUpdate` | `boolean` | `false` | 是否向后更新 |
114
+ | `BackwardUpdate_size` | `int` | `3` | 向后更新数量(仅在BackwardUpdate为True时有效) |
115
+ | `want_retry_count` | `int` | `8` | 媒体获取失败后多少次后重试 |
116
+ | `AllPartGet` | `boolean` | `false` | 是否提前获取分P或互动视频 |
117
+ | `title_change` | `dict` | `{}` | 标题文本修改规则 |
118
+
119
+ ---
120
+
121
+ ### 配置文件参考
122
+
123
+ ```json
124
+ {
125
+ "preparation_per_count": 100,
126
+ "completion_count": 100,
127
+ "retry_count": 5,
128
+ "url": "http://127.0.0.1",
129
+ "port": 8000,
130
+ "port_in_url": true,
131
+ "httpfs": false,
132
+ "title": "Podflow",
133
+ "filename": "Podflow",
134
+ "link": "https://github.com/gruel-zxz/podflow",
135
+ "description": "在iOS平台上借助workflow和a-shell搭建专属的播客服务器。",
136
+ "icon": "https://raw.githubusercontent.com/gruel-zxz/podflow/main/Podflow.png",
137
+ "category": "TV &amp; Film",
138
+ "token": "",
139
+ "delete_incompletement": false,
140
+ "remove_media": true,
141
+ "channelid_youtube": {
142
+ "youtube": {
143
+ "update_size": 15,
144
+ "id": "UCBR8-60-B28hp2BmDPdntcQ",
145
+ "title": "YouTube",
146
+ "quality": "480",
147
+ "last_size": 50,
148
+ "media": "m4a",
149
+ "DisplayRSSaddress": false,
150
+ "InmainRSS": true,
151
+ "QRcode": false,
152
+ "BackwardUpdate": false,
153
+ "BackwardUpdate_size": 3,
154
+ "want_retry_count": 8,
155
+ "title_change": [
156
+ {
157
+ "mode": "add-left",
158
+ "match": "",
159
+ "url": "https://www.youtube.com/playlist?list=...",
160
+ "text": ""
161
+ },
162
+ {
163
+ "mode": "add-right",
164
+ "match": "",
165
+ "url": "",
166
+ "text": ""
167
+ }
168
+ ],
169
+ "NoShorts": false
170
+ }
171
+ },
172
+ "channelid_bilibili": {
173
+ "哔哩哔哩弹幕网": {
174
+ "update_size": 25,
175
+ "id": "8047632",
176
+ "title": "哔哩哔哩弹幕网",
177
+ "quality": "480",
178
+ "last_size": 100,
179
+ "media": "m4a",
180
+ "DisplayRSSaddress": false,
181
+ "InmainRSS": true,
182
+ "QRcode": false,
183
+ "BackwardUpdate": false,
184
+ "BackwardUpdate_size": 3,
185
+ "want_retry_count": 8,
186
+ "title_change": {
187
+ "mode": "replace",
188
+ "match": "",
189
+ "text": ""
190
+ },
191
+ "AllPartGet": false
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ Platform: UNKNOWN
198
+ Classifier: Programming Language :: Python :: 3
199
+ Classifier: License :: OSI Approved :: MIT License
200
+ Classifier: Operating System :: OS Independent
201
+ Requires-Python: >=3.8
202
+ Description-Content-Type: text/markdown
@@ -0,0 +1,137 @@
1
+ # Podflow/__init__.py
2
+ # coding: utf-8
3
+
4
+ # 默认参数
5
+ default_config = {
6
+ "preparation_per_count": 100, # 获取媒体信息每组数量
7
+ "completion_count": 100, # 媒体缺失时最大补全数量
8
+ "retry_count": 5, # 媒体下载重试次数
9
+ "url": "http://127.0.0.1", # HTTP共享地址
10
+ "port": 8000, # HTTP共享端口
11
+ "port_in_url": True, # HTTP共享地址是否包含端口
12
+ "httpfs": False, # HTTP共享日志
13
+ "title": "Podflow", # 博客的名称
14
+ "filename": "Podflow", # 主XML的文件名称
15
+ "link": "https://github.com/gruel-zxz/podflow", # 博客主页
16
+ "description": "在iOS平台上借助workflow和a-shell搭建专属的播客服务器。", # 博客信息
17
+ "icon": "https://raw.githubusercontent.com/gruel-zxz/podflow/main/Podflow.png", # 博客图标
18
+ "category": "TV &amp; Film", # 博客类型
19
+ "token": "", # token认证, 如为null或""将不启用token
20
+ "delete_incompletement": False, # 是否删除下载中断媒体(下载前处理流程)
21
+ "remove_media": True, # 是否删除无用的媒体文件
22
+ "channelid_youtube": { # Youtube频道列表
23
+ "youtube": {
24
+ "update_size": 15, # 每次获取频道媒体数量
25
+ "id": "UCBR8-60-B28hp2BmDPdntcQ", # 频道ID
26
+ "title": "YouTube", # 频道名称
27
+ "quality": "480", # 媒体分辨率(仅在media为视频时有效)
28
+ "last_size": 50, # 媒体保留数量
29
+ "media": "m4a", # 下载媒体类型
30
+ "DisplayRSSaddress": False, # 是否在Print中显示子博客地址
31
+ "InmainRSS": True, # 是否在主博客中
32
+ "QRcode": False, # 是否显示子博客地址二维码(仅在DisplayRSSaddress为True时有效)
33
+ "BackwardUpdate": False, # 是否向后更新
34
+ "BackwardUpdate_size": 3, # 向后更新数量(仅在BackwardUpdate为True时有效)
35
+ "want_retry_count": 8, # 媒体获取失败后多少次后重试(小于等于该数量时将一直重试)
36
+ "title_change": [ # 标题文本修改(默认为无, 可多个条件,以列表形式存在)
37
+ { # match和url参数至少有一个, 如都有将同时生效
38
+ "mode": "add-left", # 修改模式(add-left: 开头添加, add-right: 结尾添加, replace: 内容替换)
39
+ "match": "", # 需要匹配的规则(为正则表达式)
40
+ "url": "https://www.youtube.com/playlist?list=...", # 播放列表网址(只适用于YouTube频道, 并且不适用replace模式, 选择后会失效)
41
+ "text": "", # 需要替换或添加的文本
42
+ },
43
+ {
44
+ "mode": "add-right",
45
+ "match": "",
46
+ "url": "",
47
+ "text": "",
48
+ },
49
+ ],
50
+ "NoShorts": False, # 是否不下载Shorts媒体
51
+ },
52
+ },
53
+ "channelid_bilibili": { # 哔哩哔哩频道列表
54
+ "哔哩哔哩弹幕网": {
55
+ "update_size": 25,
56
+ "id": "8047632",
57
+ "title": "哔哩哔哩弹幕网",
58
+ "quality": "480",
59
+ "last_size": 100,
60
+ "media": "m4a",
61
+ "DisplayRSSaddress": False,
62
+ "InmainRSS": True,
63
+ "QRcode": False,
64
+ "BackwardUpdate": False,
65
+ "BackwardUpdate_size": 3,
66
+ "want_retry_count": 8,
67
+ "title_change": {
68
+ "mode": "replace",
69
+ "match": "",
70
+ "text": "",
71
+ },
72
+ "AllPartGet": False, # 是否提前获取分P或互动视频(建议update_size大于5时使用, 如果该变量不存在时, 默认update_size大于5时开启)
73
+ },
74
+ },
75
+ }
76
+ # 如果InmainRSS为False或频道有更新则无视DisplayRSSaddress的状态, 都会变为True。
77
+
78
+
79
+ # 全局变量
80
+ class Application_gVar:
81
+ def __init__(self):
82
+ self.config = {} # 配置文件字典
83
+ self.channelid_youtube = {} # YouTube频道字典
84
+ self.channelid_bilibili = {} # 哔哩哔哩频道字典
85
+ self.channelid_youtube_ids = {} # YouTube频道ID字典
86
+ self.channelid_youtube_ids_original = {} # 原始YouTube频道ID字典
87
+ self.channelid_bilibili_ids = {} # 哔哩哔哩频道ID字典
88
+ self.channelid_bilibili_ids_original = {} # 原始哔哩哔哩频道ID字典
89
+
90
+ self.server_process_print_flag = ["keep"] # httpserver进程打印标志列表
91
+ self.update_generate_rss = True # 更新并生成rss布朗值
92
+ self.displayed_QRcode = [] # 已显示二维码列表
93
+
94
+ self.bilibili_data = {} # 哔哩哔哩data字典
95
+ self.youtube_cookie = {} # YouTube cookie字典
96
+ self.channelid_youtube_ids_update = {} # 需更新的YouTube频道字典
97
+ self.youtube_content_ytid_update = {} # 需下载YouTube视频字典
98
+ self.youtube_content_ytid_backward_update = {} # 向后更新需下载YouTube视频字典
99
+ self.channelid_youtube_rss = {} # YouTube频道最新Rss Response字典
100
+ self.channelid_bilibili_ids_update = {} # 需更新的哔哩哔哩频道字典
101
+ self.bilibili_content_bvid_update = {} # 需下载哔哩哔哩视频字典
102
+ self.channelid_bilibili_rss = {} # 哔哩哔哩频道最新Rss Response字典
103
+ self.bilibili_content_bvid_backward_update = (
104
+ {}
105
+ ) # 向后更新需下载哔哩哔哩视频字典
106
+ self.video_id_failed = [] # YouTube&哔哩哔哩视频下载失败列表
107
+ self.video_id_update_format = {} # YouTube和哔哩哔哩视频下载的详细信息字典
108
+ self.hash_rss_original = "" # 原始rss哈希值文本
109
+ self.xmls_original = {} # 原始xml信息字典
110
+ self.xmls_original_fail = [] # 未获取原始xml频道列表
111
+ self.youtube_xml_get_tree = {} # YouTube频道简介和图标字典
112
+ self.all_youtube_content_ytid = {} # 所有YouTube视频id字典
113
+ self.all_bilibili_content_bvid = {} # 所有哔哩哔哩视频id字典
114
+ self.all_items = [] # 更新后所有item明细列表
115
+ self.overall_rss = "" # 更新后的rss文本
116
+ self.make_up_file_format = {} # 补全缺失媒体字典
117
+ self.make_up_file_format_fail = {} # 补全缺失媒体失败字典
118
+
119
+ self.shortcuts_url = {} # 输出至shortcut的url字典
120
+
121
+
122
+ # 参数变量
123
+ class Application_parse:
124
+ def __init__(self):
125
+ self.shortcuts_url_original = []
126
+ self.argument = ""
127
+ self.update_num = -1
128
+ self.time_delay = 0
129
+ self.config = ""
130
+ self.period = 1
131
+ self.file = ""
132
+ self.httpfs = False
133
+
134
+
135
+ # 创建 Application 类的实例
136
+ gVar = Application_gVar()
137
+ parse = Application_parse()
@@ -0,0 +1,2 @@
1
+ # Podflow/basic/__init__.py
2
+ # coding: utf-8
@@ -0,0 +1,21 @@
1
+ # Podflow/basic/file_save.py
2
+ # coding: utf-8
3
+
4
+ import os
5
+ import json
6
+
7
+
8
+ # 文件保存模块
9
+ def file_save(content, file_name, folder=None):
10
+ # 如果指定了文件夹则将文件保存到指定的文件夹中
11
+ if folder:
12
+ file_path = os.path.join(os.getcwd(), folder, file_name)
13
+ else:
14
+ # 如果没有指定文件夹则将文件保存在当前工作目录中
15
+ file_path = os.path.join(os.getcwd(), file_name)
16
+ # 保存文件
17
+ with open(file_path, "w", encoding="utf-8") as file:
18
+ if "." in file_name and file_name.split(".")[-1] == "json":
19
+ json.dump(content, file, ensure_ascii=False, indent=4)
20
+ else:
21
+ file.write(content)
@@ -0,0 +1,16 @@
1
+ # Podflow/basic/folder_build.py
2
+ # coding: utf-8
3
+
4
+ import os
5
+ from Podflow.basic.write_log import write_log
6
+
7
+
8
+ # 构建文件夹模块
9
+ def folder_build(folder_name, parent_folder_name=None):
10
+ if parent_folder_name:
11
+ folder_path = os.path.join(os.getcwd(), parent_folder_name, folder_name)
12
+ else:
13
+ folder_path = os.path.join(os.getcwd(), folder_name)
14
+ if not os.path.exists(folder_path): # 判断文件夹是否存在
15
+ os.makedirs(folder_path) # 创建文件夹
16
+ write_log(f"文件夹{folder_name}创建成功")
@@ -0,0 +1,18 @@
1
+ # Podflow/basic/get_duration.py
2
+ # coding: utf-8
3
+
4
+ import math
5
+ import ffmpeg
6
+ from Podflow.basic.write_log import write_log
7
+
8
+
9
+ # 获取已下载视频时长模块
10
+ def get_duration(file_path):
11
+ try:
12
+ # 调用ffmpeg获取视频文件的时长信息
13
+ probe = ffmpeg.probe(file_path)
14
+ duration = float(probe['format']['duration'])
15
+ return math.ceil(duration)
16
+ except ffmpeg.Error as e:
17
+ error_note = e.stderr.decode('utf-8').splitlines()[-1]
18
+ write_log(f"\033[31mError:\033[0m {error_note}")
@@ -0,0 +1,57 @@
1
+ # Podflow/basic/get_file_list.py
2
+ # coding: utf-8
3
+
4
+ import os
5
+ import re
6
+ from Podflow.basic.list_merge_tidy import list_merge_tidy
7
+
8
+
9
+ # 获取文件列表和分P列表
10
+ def get_file_list(video_key, video_media="m4a", length=12):
11
+ media = ("m4a", "mp4", "part") if video_media == "m4a" else ("mp4", "part")
12
+ try:
13
+ content_id = [
14
+ file # 获取文件名(包括扩展名)
15
+ for file in os.listdir(
16
+ f"channel_audiovisual/{video_key}"
17
+ ) # 遍历指定目录下的所有文件
18
+ if file.endswith(media) # 筛选出以 media 结尾的文件
19
+ ]
20
+ content_id_items = []
21
+ items_counts = {}
22
+ for id_num in content_id:
23
+ if len(id_num) > length + 4:
24
+ content_id_items.append(id_num)
25
+ if ".part" in id_num:
26
+ items_counts[id_num[:length]] = 0
27
+ content_id_items = [id_num[:length] for id_num in content_id_items]
28
+ for id_num in content_id_items:
29
+ if id_num not in items_counts:
30
+ qtys = content_id_items.count(id_num)
31
+ if video_media == "m4a":
32
+ pattern = re.compile(rf"{id_num}_[0-9]*\.(m4a|mp4)")
33
+ else:
34
+ pattern = re.compile(rf"{id_num}_[0-9]*\.(mp4)")
35
+ if len([item for item in content_id if pattern.search(item)]) == qtys:
36
+ items_counts[id_num] = qtys
37
+ else:
38
+ fail = False
39
+ for qty in range(qtys):
40
+ if video_media == "m4a":
41
+ if (
42
+ f"{id_num}_p{qty + 1}.m4a" not in content_id
43
+ and f"{id_num}_p{qty + 1}.mp4" not in content_id
44
+ ):
45
+ fail = True
46
+ elif f"{id_num}_p{qty + 1}.mp4" not in content_id:
47
+ fail = True
48
+ items_counts[id_num] = 0 if fail else qtys
49
+ content_id = list_merge_tidy(content_id, [], length)
50
+ for id_num, num in items_counts.copy().items():
51
+ if num in [1, 0] and id_num in content_id:
52
+ content_id.remove(id_num)
53
+ del items_counts[id_num]
54
+ except Exception:
55
+ content_id = []
56
+ items_counts = {}
57
+ return content_id, items_counts
@@ -0,0 +1,30 @@
1
+ # Podflow/basic/get_html_dict.py
2
+ # coding: utf-8
3
+
4
+ import re
5
+ import json
6
+ from bs4 import BeautifulSoup
7
+ from Podflow.basic.http_client import http_client
8
+
9
+
10
+ # 通过bs4获取html中字典模块
11
+ def get_html_dict(url, name, script_label):
12
+ if not (response := http_client(url, name)):
13
+ return None
14
+ html_content = response.text
15
+ # 使用Beautiful Soup解析HTML
16
+ soup = BeautifulSoup(html_content, 'html.parser')
17
+ if not (
18
+ data_script := soup.find(
19
+ 'script', string=lambda t: t and script_label in t
20
+ )
21
+ ):
22
+ return None
23
+ try:
24
+ # 使用正则表达式提取 JSON 数据
25
+ pattern = re.compile(r'\{.*\}', re.DOTALL)
26
+ if match := pattern.search(data_script.text):
27
+ data_str = match.group()
28
+ return json.loads(data_str)
29
+ except json.JSONDecodeError:
30
+ return None
@@ -0,0 +1,75 @@
1
+ # Podflow/basic/http_client.py
2
+ # coding: utf-8
3
+
4
+ import time
5
+ from datetime import datetime
6
+ import requests
7
+
8
+
9
+ # HTTP 请求重试模块
10
+ def http_client(
11
+ url,
12
+ name="",
13
+ max_retries=10,
14
+ retry_delay=4,
15
+ headers_possess=False,
16
+ cookies=None,
17
+ data=None,
18
+ mode="get",
19
+ ):
20
+ user_agent = {
21
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
22
+ }
23
+ if "bilibili" in url:
24
+ user_agent["Referer"] = "https://www.bilibili.com/"
25
+ elif "youtube" in url:
26
+ user_agent["Referer"] = "https://www.youtube.com/"
27
+ elif "douyin" in url:
28
+ headers_douyin = {
29
+ "authority": "sso.douyin.com",
30
+ "accept": "application/json, text/plain, */*",
31
+ "accept-language": "zh-CN,zh;q=0.9",
32
+ "origin": "https://www.douyin.com",
33
+ "referer": "https://www.douyin.com/",
34
+ "sec-ch-ua": '"Google Chrome";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
35
+ "sec-ch-ua-mobile": "?0",
36
+ "sec-ch-ua-platform": '"Windows"',
37
+ "sec-fetch-dest": "empty",
38
+ "sec-fetch-mode": "cors",
39
+ "sec-fetch-site": "same-site",
40
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
41
+ }
42
+ user_agent |= headers_douyin
43
+ err = None # 初始化 err 变量
44
+ response = None # 初始化 response 变量
45
+ # 创建一个Session对象
46
+ session = requests.Session()
47
+ if headers_possess:
48
+ session.headers.update(user_agent)
49
+ if cookies:
50
+ session.cookies.update(cookies)
51
+ if data:
52
+ session.params.update(data)
53
+ for num in range(max_retries):
54
+ try:
55
+ if mode != "post":
56
+ response = session.get(url, timeout=8)
57
+ else:
58
+ response = session.post(url, timeout=8)
59
+ response.raise_for_status()
60
+ except Exception as http_get_error:
61
+ if response is not None and response.status_code in {404}:
62
+ return response
63
+ if name:
64
+ print(
65
+ f"{datetime.now().strftime('%H:%M:%S')}|{name}|\033[31m连接异常重试中...\033[97m{num + 1}\033[0m"
66
+ )
67
+ err = f":\n{str(http_get_error)}" if err else ""
68
+ else:
69
+ return response
70
+ time.sleep(retry_delay)
71
+ if name:
72
+ print(
73
+ f"{datetime.now().strftime('%H:%M:%S')}|{name}|\033[31m达到最大重试次数\033[97m{max_retries}\033[0m{err}"
74
+ )
75
+ return response
@@ -0,0 +1,15 @@
1
+ # Podflow/basic/list_merge_tidy.py
2
+ # coding: utf-8
3
+
4
+
5
+ # 合并整形列表模块
6
+ def list_merge_tidy(list1, list2=None, length=None):
7
+ if list2 is None:
8
+ list2 = []
9
+ final_list = []
10
+ for item in list1 + list2:
11
+ if item:
12
+ item = item[:length]
13
+ if item not in final_list:
14
+ final_list.append(item)
15
+ return final_list
@@ -0,0 +1,55 @@
1
+ # Podflow/basic/qr_code.py
2
+ # coding: utf-8
3
+
4
+ import math
5
+ import qrcode
6
+
7
+
8
+ # 网址二维码模块
9
+ def qr_code(data):
10
+ # 创建一个QRCode对象
11
+ qr = qrcode.QRCode(
12
+ version=1,
13
+ error_correction=qrcode.constants.ERROR_CORRECT_L,
14
+ box_size=1,
15
+ border=0,
16
+ )
17
+ # 设置二维码的数据
18
+ qr.add_data(data)
19
+ # 获取QR Code矩阵
20
+ qr.make(fit=True)
21
+ matrix = qr.make_image(fill_color="black", back_color="white").modules
22
+ # 获取图像的宽度和高度
23
+ width, height = len(matrix), len(matrix)
24
+ height_double = math.ceil(height / 2)
25
+ # 转换图像为ASCII字符
26
+ fonts = ["▀", "▄", "█", " "]
27
+ ascii_art = ""
28
+ for y in range(height_double):
29
+ if (y + 1) * 2 - 1 >= height:
30
+ for x in range(width):
31
+ ascii_art += (
32
+ fonts[0] if matrix[(y + 1) * 2 - 2][x] is True else fonts[3]
33
+ )
34
+ else:
35
+ for x in range(width):
36
+ if (
37
+ matrix[(y + 1) * 2 - 2][x] is True
38
+ and matrix[(y + 1) * 2 - 1][x] is True
39
+ ):
40
+ ascii_art += fonts[2]
41
+ elif (
42
+ matrix[(y + 1) * 2 - 2][x] is True
43
+ and matrix[(y + 1) * 2 - 1][x] is False
44
+ ):
45
+ ascii_art += fonts[0]
46
+ elif (
47
+ matrix[(y + 1) * 2 - 2][x] is False
48
+ and matrix[(y + 1) * 2 - 1][x] is True
49
+ ):
50
+ ascii_art += fonts[1]
51
+ else:
52
+ ascii_art += fonts[3]
53
+ ascii_art += "\n"
54
+ print(ascii_art)
55
+ return height_double
@@ -0,0 +1,14 @@
1
+ # Podflow/basic/split_dict.py
2
+ # coding: utf-8
3
+
4
+
5
+ # 字典拆分模块
6
+ def split_dict(data, chunk_size=100, firse_item_only=False):
7
+ if chunk_size == 0:
8
+ return [{}]
9
+ end_value = chunk_size if firse_item_only else len(data)
10
+ chunks = []
11
+ for i in range(0, end_value, chunk_size):
12
+ chunk = dict(list(data.items())[i:i+chunk_size])
13
+ chunks.append(chunk)
14
+ return chunks