podflow 2025.1.26__py3-none-any.whl

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 (80) hide show
  1. Podflow/__init__.py +137 -0
  2. Podflow/basic/__init__.py +2 -0
  3. Podflow/basic/file_save.py +21 -0
  4. Podflow/basic/folder_build.py +16 -0
  5. Podflow/basic/get_duration.py +18 -0
  6. Podflow/basic/get_file_list.py +57 -0
  7. Podflow/basic/get_html_dict.py +30 -0
  8. Podflow/basic/http_client.py +75 -0
  9. Podflow/basic/list_merge_tidy.py +15 -0
  10. Podflow/basic/qr_code.py +55 -0
  11. Podflow/basic/split_dict.py +14 -0
  12. Podflow/basic/time_format.py +16 -0
  13. Podflow/basic/time_stamp.py +61 -0
  14. Podflow/basic/vary_replace.py +11 -0
  15. Podflow/basic/write_log.py +43 -0
  16. Podflow/bilibili/__init__.py +2 -0
  17. Podflow/bilibili/build.py +201 -0
  18. Podflow/bilibili/get.py +477 -0
  19. Podflow/bilibili/login.py +307 -0
  20. Podflow/config/__init__.py +2 -0
  21. Podflow/config/build_original.py +41 -0
  22. Podflow/config/channge_icon.py +163 -0
  23. Podflow/config/correct_channelid.py +230 -0
  24. Podflow/config/correct_config.py +103 -0
  25. Podflow/config/get_channelid.py +22 -0
  26. Podflow/config/get_channelid_id.py +21 -0
  27. Podflow/config/get_config.py +34 -0
  28. Podflow/download/__init__.py +2 -0
  29. Podflow/download/convert_bytes.py +18 -0
  30. Podflow/download/delete_part.py +20 -0
  31. Podflow/download/dl_aideo_video.py +307 -0
  32. Podflow/download/show_progress.py +46 -0
  33. Podflow/download/wait_animation.py +34 -0
  34. Podflow/download/youtube_and_bilibili_download.py +30 -0
  35. Podflow/ffmpeg_judge.py +45 -0
  36. Podflow/httpfs/__init__.py +2 -0
  37. Podflow/httpfs/app_bottle.py +212 -0
  38. Podflow/httpfs/port_judge.py +21 -0
  39. Podflow/main.py +248 -0
  40. Podflow/makeup/__init__.py +2 -0
  41. Podflow/makeup/del_makeup_yt_format_fail.py +19 -0
  42. Podflow/makeup/make_up_file.py +51 -0
  43. Podflow/makeup/make_up_file_format_mod.py +96 -0
  44. Podflow/makeup/make_up_file_mod.py +30 -0
  45. Podflow/message/__init__.py +2 -0
  46. Podflow/message/backup_zip_save.py +45 -0
  47. Podflow/message/create_main_rss.py +44 -0
  48. Podflow/message/display_qrcode_and_url.py +36 -0
  49. Podflow/message/fail_message_initialize.py +165 -0
  50. Podflow/message/format_time.py +27 -0
  51. Podflow/message/get_original_rss.py +65 -0
  52. Podflow/message/get_video_format.py +111 -0
  53. Podflow/message/get_video_format_multithread.py +42 -0
  54. Podflow/message/get_youtube_and_bilibili_video_format.py +87 -0
  55. Podflow/message/media_format.py +195 -0
  56. Podflow/message/original_rss_fail_print.py +15 -0
  57. Podflow/message/rss_create_hash.py +26 -0
  58. Podflow/message/title_correction.py +30 -0
  59. Podflow/message/update_information_display.py +72 -0
  60. Podflow/message/update_youtube_bilibili_rss.py +116 -0
  61. Podflow/message/want_retry.py +21 -0
  62. Podflow/message/xml_item.py +83 -0
  63. Podflow/message/xml_original_item.py +92 -0
  64. Podflow/message/xml_rss.py +46 -0
  65. Podflow/netscape/__init__.py +2 -0
  66. Podflow/netscape/bulid_netscape.py +44 -0
  67. Podflow/netscape/get_cookie_dict.py +21 -0
  68. Podflow/parse_arguments.py +80 -0
  69. Podflow/remove/__init__.py +2 -0
  70. Podflow/remove/remove_dir.py +33 -0
  71. Podflow/remove/remove_file.py +23 -0
  72. Podflow/youtube/__init__.py +2 -0
  73. Podflow/youtube/build.py +287 -0
  74. Podflow/youtube/get.py +376 -0
  75. Podflow/youtube/login.py +39 -0
  76. podflow-2025.1.26.dist-info/METADATA +214 -0
  77. podflow-2025.1.26.dist-info/RECORD +80 -0
  78. podflow-2025.1.26.dist-info/WHEEL +5 -0
  79. podflow-2025.1.26.dist-info/entry_points.txt +6 -0
  80. podflow-2025.1.26.dist-info/top_level.txt +1 -0
@@ -0,0 +1,230 @@
1
+ # Podflow/config/correct_channelid.py
2
+ # coding: utf-8
3
+
4
+ import re
5
+ from Podflow import gVar, default_config, parse
6
+ from Podflow.basic.write_log import write_log
7
+
8
+
9
+ # channelid修正模块
10
+ def correct_channelid(channelid, website):
11
+ config = gVar.config
12
+ channelid_name = ""
13
+ output_name = ""
14
+ if website == "youtube":
15
+ channelid_name = "youtube"
16
+ output_name = "YouTube"
17
+ elif website == "bilibili":
18
+ channelid_name = "哔哩哔哩弹幕网"
19
+ output_name = "BiliBili"
20
+ # 音视频格式及分辨率常量
21
+ video_media = [
22
+ "m4v",
23
+ "mov",
24
+ "qt",
25
+ "avi",
26
+ "flv",
27
+ "wmv",
28
+ "asf",
29
+ "mpeg",
30
+ "mpg",
31
+ "vob",
32
+ "mkv",
33
+ "rm",
34
+ "rmvb",
35
+ "vob",
36
+ "ts",
37
+ "dat",
38
+ ]
39
+ dpi = [
40
+ "144",
41
+ "180",
42
+ "216",
43
+ "240",
44
+ "360",
45
+ "480",
46
+ "720",
47
+ "1080",
48
+ "1440",
49
+ "2160",
50
+ "4320",
51
+ ]
52
+ media = ["m4a", "mp4"]
53
+
54
+ # 判断正则表达式是否有效
55
+ def is_valid_regex(pattern):
56
+ try:
57
+ re.compile(pattern)
58
+ return True
59
+ except re.error:
60
+ return False
61
+
62
+ # 复制字典channelid, 遍历复制后的字典进行操作以避免在循环中删除元素导致的迭代错误
63
+ channelid_copy = channelid.copy()
64
+ # 对channelid的错误进行更正
65
+ for channelid_key, channeli_value in channelid_copy.items():
66
+ # 判断是否为字典
67
+ if not isinstance(channeli_value, dict):
68
+ channeli_value = {"id": channeli_value}
69
+ channelid[channelid_key] = channeli_value
70
+ # 判断id是否正确
71
+ if (
72
+ "id" not in channeli_value
73
+ or (
74
+ website == "youtube"
75
+ and not re.search(r"^UC.{22}", channeli_value["id"])
76
+ )
77
+ or (website == "bilibili" and not channeli_value["id"].isdigit())
78
+ ):
79
+ # 删除错误的
80
+ del channelid[channelid_key]
81
+ write_log(f"{output_name}频道 {channelid_key} ID不正确")
82
+ else:
83
+ # 对update_size进行纠正
84
+ if (
85
+ "update_size" not in channeli_value
86
+ or not isinstance(channeli_value["update_size"], int)
87
+ or channeli_value["update_size"] <= 0
88
+ ):
89
+ channelid[channelid_key]["update_size"] = default_config[
90
+ f"channelid_{website}"
91
+ ][channelid_name]["update_size"]
92
+ # 对id进行纠正
93
+ if website == "youtube":
94
+ channelid[channelid_key]["id"] = re.search(
95
+ r"UC.{22}", channeli_value["id"]
96
+ ).group()
97
+ # 对last_size进行纠正
98
+ if (
99
+ "last_size" not in channeli_value
100
+ or not isinstance(channeli_value["last_size"], int)
101
+ or channeli_value["last_size"] <= 0
102
+ ):
103
+ channelid[channelid_key]["last_size"] = default_config[
104
+ f"channelid_{website}"
105
+ ][channelid_name]["last_size"]
106
+ channelid[channelid_key]["last_size"] = max(
107
+ channelid[channelid_key]["last_size"],
108
+ channelid[channelid_key]["update_size"],
109
+ )
110
+ # 对title进行纠正
111
+ if "title" not in channeli_value:
112
+ channelid[channelid_key]["title"] = channelid_key
113
+ # 对quality进行纠正
114
+ if (
115
+ (
116
+ "quality" not in channeli_value
117
+ or channeli_value["quality"] not in dpi
118
+ )
119
+ and "media" in channeli_value
120
+ and channeli_value["media"] == "mp4"
121
+ ):
122
+ channelid[channelid_key]["quality"] = default_config[
123
+ f"channelid_{website}"
124
+ ][channelid_name]["quality"]
125
+ # 对media进行纠正
126
+ if (
127
+ "media" in channeli_value
128
+ and channeli_value["media"] not in media
129
+ and channeli_value["media"] in video_media
130
+ ):
131
+ channelid[channelid_key]["media"] = "mp4"
132
+ elif (
133
+ "media" in channeli_value
134
+ and channeli_value["media"] not in media
135
+ or "media" not in channeli_value
136
+ ):
137
+ channelid[channelid_key]["media"] = "m4a"
138
+ # 对DisplayRSSaddress进行纠正
139
+ if "DisplayRSSaddress" not in channeli_value or not isinstance(
140
+ channeli_value["DisplayRSSaddress"], bool
141
+ ):
142
+ channelid[channelid_key]["DisplayRSSaddress"] = False
143
+ # 对InmainRSS进行纠正
144
+ if "InmainRSS" in channeli_value and isinstance(
145
+ channeli_value["InmainRSS"], bool
146
+ ):
147
+ if channeli_value["InmainRSS"] is False:
148
+ channelid[channelid_key]["DisplayRSSaddress"] = True
149
+ else:
150
+ channelid[channelid_key]["InmainRSS"] = True
151
+ # 对QRcode进行纠正
152
+ if "QRcode" not in channeli_value or not isinstance(
153
+ channeli_value["QRcode"], bool
154
+ ):
155
+ channelid[channelid_key]["QRcode"] = False
156
+ # 对BackwardUpdate进行纠正
157
+ if "BackwardUpdate" not in channeli_value or not isinstance(
158
+ channeli_value["BackwardUpdate"], bool
159
+ ):
160
+ channelid[channelid_key]["BackwardUpdate"] = False
161
+ # 对BackwardUpdate_size进行纠正
162
+ if channelid[channelid_key]["BackwardUpdate"] and (
163
+ "BackwardUpdate_size" not in channeli_value
164
+ or not isinstance(channeli_value["BackwardUpdate_size"], int)
165
+ or channeli_value["BackwardUpdate_size"] <= 0
166
+ ):
167
+ channelid[channelid_key]["BackwardUpdate_size"] = default_config[
168
+ f"channelid_{website}"
169
+ ][channelid_name]["BackwardUpdate_size"]
170
+ # 对want_retry_count进行纠正
171
+ if (
172
+ "want_retry_count" not in channeli_value
173
+ or not isinstance(channeli_value["want_retry_count"], int)
174
+ or channeli_value["want_retry_count"] <= 0
175
+ ):
176
+ channelid[channelid_key]["want_retry_count"] = default_config[
177
+ f"channelid_{website}"
178
+ ][channelid_name]["want_retry_count"]
179
+ # 对title_change进行纠正
180
+ if "title_change" in channeli_value:
181
+ title_changes = channeli_value["title_change"]
182
+ uphold_title_changes = []
183
+ if isinstance(title_changes, list):
184
+ for title_change in title_changes:
185
+ if website == "bilibli" and "url" in title_change:
186
+ del title_change["url"]
187
+ if (
188
+ isinstance(title_change, dict)
189
+ and "mode" in title_change
190
+ and "text" in title_change
191
+ and ("url" in title_change or "match" in title_change)
192
+ ):
193
+ mode = title_change["mode"]
194
+ match_url_pattern = r"https://www\.youtube\.com/playlist\?list=PL[0-9a-zA-Z_-]{32}"
195
+ if "url" in title_change and (
196
+ mode not in ["add-left", "add-right"]
197
+ or not re.match(match_url_pattern, title_change["url"])
198
+ ):
199
+ break
200
+ if "match" in title_change and (
201
+ mode not in ["add-left", "add-right", "replace"]
202
+ or not is_valid_regex(title_change["match"])
203
+ ):
204
+ break
205
+ uphold_title_changes.append(title_change)
206
+ if uphold_title_changes:
207
+ channelid[channelid_key]["title_change"] = uphold_title_changes
208
+ else:
209
+ del channelid[channelid_key]["title_change"]
210
+ if website == "bilibili" and (
211
+ "AllPartGet" not in channeli_value
212
+ or not isinstance(channeli_value["AllPartGet"], bool)
213
+ ):
214
+ channelid[channelid_key]["AllPartGet"] = (
215
+ channelid[channelid_key]["update_size"] > 5
216
+ )
217
+ if website == "youtube" and (
218
+ "NoShorts" not in channeli_value
219
+ or not isinstance(channeli_value["NoShorts"], bool)
220
+ ):
221
+ channelid[channelid_key]["NoShorts"] = False
222
+ if (
223
+ channelid[channelid_key]["InmainRSS"] is False
224
+ and f"{config['address']}/channel_rss/{channeli_value['id']}.xml"
225
+ not in parse.shortcuts_url_original
226
+ ):
227
+ gVar.shortcuts_url[channelid_key] = (
228
+ f"{config['address']}/channel_rss/{channeli_value['id']}.xml"
229
+ )
230
+ return channelid
@@ -0,0 +1,103 @@
1
+ # Podflow/config/correct_config.py
2
+ # coding: utf-8
3
+
4
+ import re
5
+ from Podflow import gVar, default_config, parse
6
+
7
+
8
+ # 纠正配置信息config模块
9
+ def correct_config():
10
+ config = gVar.config
11
+ # 对completion_count进行纠正
12
+ if (
13
+ "completion_count" not in config
14
+ or not isinstance(config["completion_count"], int)
15
+ or config["completion_count"] < 0
16
+ ):
17
+ config["completion_count"] = default_config["completion_count"]
18
+ # 对preparation_per_count进行纠正
19
+ if (
20
+ "preparation_per_count" not in config
21
+ or not isinstance(config["preparation_per_count"], int)
22
+ or config["preparation_per_count"] <= 0
23
+ ):
24
+ config["preparation_per_count"] = default_config["preparation_per_count"]
25
+ # 对retry_count进行纠正
26
+ if (
27
+ "retry_count" not in config
28
+ or not isinstance(config["retry_count"], int)
29
+ or config["retry_count"] <= 0
30
+ ):
31
+ config["retry_count"] = default_config["retry_count"]
32
+ # 对url进行纠正
33
+ match_url = re.search(r"^(https?|ftp)://([^/\s:]+)", config["url"])
34
+ if "url" in config and match_url:
35
+ config["url"] = match_url.group()
36
+ else:
37
+ config["url"] = default_config["url"]
38
+ # 对port进行纠正
39
+ if (
40
+ "port" not in config
41
+ or not isinstance(config["port"], int)
42
+ or config["port"] < 0
43
+ or config["port"] > 65535
44
+ ):
45
+ config["port"] = default_config["port"]
46
+ # 对port_in_url进行纠正
47
+ if "port_in_url" not in config or not isinstance(config["port_in_url"], bool):
48
+ config["port_in_url"] = default_config["port_in_url"]
49
+ # 合并地址和端口
50
+ if config["port_in_url"]:
51
+ config["address"] = f"{config['url']}:{config['port']}"
52
+ else:
53
+ config["address"] = config["url"]
54
+ # 对httpfs进行纠正
55
+ if "httpfs" not in config or not isinstance(config["httpfs"], bool):
56
+ config["httpfs"] = default_config["httpfs"]
57
+ # 对title进行纠正
58
+ if "title" not in config:
59
+ config["title"] = default_config["title"]
60
+ # 对filename进行纠正
61
+ if "filename" not in config:
62
+ config["filename"] = default_config["filename"]
63
+ # 对link进行纠正
64
+ if "link" not in config or not re.search(
65
+ r"^(https?|ftp)://[^\s/$.?#].[^\s]*$", config["link"]
66
+ ):
67
+ config["link"] = default_config["link"]
68
+ # 对description进行纠正
69
+ if "description" not in config:
70
+ config["description"] = default_config["description"]
71
+ # 对icon进行纠正
72
+ if "icon" not in config or not re.search(
73
+ r"^(https?|ftp)://[^\s/$.?#].[^\s]*$", config["icon"]
74
+ ):
75
+ config["icon"] = default_config["icon"]
76
+ # 对category进行纠正
77
+ if "category" not in config:
78
+ config["category"] = default_config["category"]
79
+ # 对IOS中shortcuts自动关注进行纠正
80
+ if (
81
+ f"{config['address']}/{config['filename']}.xml"
82
+ not in parse.shortcuts_url_original
83
+ ):
84
+ gVar.shortcuts_url[f"{config['filename']}(Main RSS)"] = (
85
+ f"{config['address']}/{config['filename']}.xml"
86
+ )
87
+ # 对token进行纠正
88
+ if "token" not in config:
89
+ config["token"] = default_config["token"]
90
+ if config["token"] in [None, ""]:
91
+ config["token"] = ""
92
+ else:
93
+ config["token"] = str(config["token"])
94
+ # 对delete_incompletement进行纠正
95
+ if "delete_incompletement" not in config or not isinstance(
96
+ config["delete_incompletement"], bool
97
+ ):
98
+ config["delete_incompletement"] = default_config["delete_incompletement"]
99
+ # 对remove_media进行纠正
100
+ if "remove_media" not in config or not isinstance(
101
+ config["remove_media"], bool
102
+ ):
103
+ config["remove_media"] = default_config["remove_media"]
@@ -0,0 +1,22 @@
1
+ # Podflow/config/get_channelid.py
2
+ # coding: utf-8
3
+
4
+ from datetime import datetime
5
+ from Podflow import gVar
6
+ from Podflow.basic.write_log import write_log
7
+
8
+
9
+ # 从配置文件中获取频道模块
10
+ def get_channelid(name):
11
+ config = gVar.config
12
+ output_name = ""
13
+ if name == "youtube":
14
+ output_name = "YouTube"
15
+ elif name == "bilibili":
16
+ output_name = "BiliBili"
17
+ if f"channelid_{name}" in config:
18
+ print(f"{datetime.now().strftime('%H:%M:%S')}|已读取{output_name}频道信息")
19
+ return config[f"channelid_{name}"]
20
+ else:
21
+ write_log(f"{output_name}频道信息不存在")
22
+ return {}
@@ -0,0 +1,21 @@
1
+ # Podflow/config/get_channelid_id.py
2
+ # coding: utf-8
3
+
4
+ from datetime import datetime
5
+
6
+
7
+ # 读取频道ID模块
8
+ def get_channelid_id(channelid, idname):
9
+ output_name = ""
10
+ if idname == "youtube":
11
+ output_name = "YouTube"
12
+ elif idname == "bilibili":
13
+ output_name = "BiliBili"
14
+ if channelid:
15
+ channelid_ids = dict(
16
+ {channel["id"]: key for key, channel in channelid.items()}
17
+ )
18
+ print(f"{datetime.now().strftime('%H:%M:%S')}|读取{output_name}频道的channelid成功")
19
+ else:
20
+ channelid_ids = {}
21
+ return channelid_ids
@@ -0,0 +1,34 @@
1
+ # Podflow/config/get_config.py
2
+ # coding: utf-8
3
+
4
+ import os
5
+ import sys
6
+ import json
7
+ from datetime import datetime
8
+ from Podflow import default_config
9
+ from Podflow.basic.write_log import write_log
10
+
11
+
12
+ # 获取配置信息config模块
13
+ def get_config(file_name="config.json"):
14
+ # 检查当前文件夹中是否存在config文件
15
+ if file_name != "config.json" and not os.path.exists(file_name):
16
+ if os.path.exists("config.json"):
17
+ write_log(f"不存在配置文件{file_name}, 将使用原始配置文件")
18
+ file_name = "config.json"
19
+ else:
20
+ # 如果文件不存在, 创建并写入默认字典
21
+ with open("config.json", "w") as file:
22
+ json.dump(default_config, file, indent=4)
23
+ write_log("不存在配置文件, 已新建, 默认频道")
24
+ return default_config
25
+ # 如果文件存在, 读取字典并保存到config变量中
26
+ try:
27
+ with open(file_name, "r", encoding="utf-8") as file:
28
+ config = json.load(file)
29
+ print(f"{datetime.now().strftime('%H:%M:%S')}|已读取配置文件")
30
+ return config
31
+ # 如果config格式有问题, 停止运行并报错
32
+ except Exception as config_error:
33
+ write_log(f"配置文件有误, 请检查{file_name}, {str(config_error)}")
34
+ sys.exit(0)
@@ -0,0 +1,2 @@
1
+ # Podflow/download/__init__.py
2
+ # coding: utf-8
@@ -0,0 +1,18 @@
1
+ # Podflow/download/convert_bytes.py
2
+ # coding: utf-8
3
+
4
+
5
+ # 格式化字节模块
6
+ def convert_bytes(byte_size, units=None, outweigh=1024):
7
+ if units is None:
8
+ units = [" B", "KB", "MB", "GB"]
9
+ if byte_size is None:
10
+ byte_size = 0
11
+ # 初始单位是字节
12
+ unit_index = 0
13
+ # 将字节大小除以1024直到小于1024为止
14
+ while byte_size > outweigh and unit_index < len(units) - 1:
15
+ byte_size /= 1024.0
16
+ unit_index += 1
17
+ # 格式化结果并返回
18
+ return f"{byte_size:.2f}{units[unit_index]}"
@@ -0,0 +1,20 @@
1
+ # Podflow/download/delete_part.py
2
+ # coding: utf-8
3
+
4
+ import os
5
+ import fnmatch
6
+ from Podflow.basic.write_log import write_log
7
+
8
+
9
+ # 删除下载失败媒体模块
10
+ def delete_part(channelid_ids):
11
+ relative_path = "channel_audiovisual"
12
+ parent_folder_path = os.path.abspath(relative_path)
13
+ for root, _, filenames in os.walk(parent_folder_path):
14
+ for filename in fnmatch.filter(filenames, '*.part'):
15
+ file_path = os.path.join(root, filename)
16
+ os.remove(file_path)
17
+ ids = os.path.basename(root).split('/')[-1]
18
+ if ids in channelid_ids:
19
+ ids = channelid_ids[ids]
20
+ write_log(f"{ids}|{filename}已删除")