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
Podflow/__init__.py ADDED
@@ -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 & 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
@@ -0,0 +1,16 @@
1
+ # Podflow/basic/time_format.py
2
+ # coding: utf-8
3
+
4
+
5
+ # 格式化时间模块
6
+ def time_format(duration):
7
+ if duration is None:
8
+ return "Unknown"
9
+ duration = int(duration)
10
+ hours, remaining_seconds = divmod(duration, 3600)
11
+ minutes = remaining_seconds // 60
12
+ remaining_seconds = remaining_seconds % 60
13
+ if hours > 0:
14
+ return "{:02}:{:02}:{:02}".format(hours, minutes, remaining_seconds)
15
+ else:
16
+ return "{:02}:{:02}".format(minutes, remaining_seconds)
@@ -0,0 +1,61 @@
1
+ # Podflow/basic/time_stamp.py
2
+ # coding: utf-8
3
+
4
+ import time
5
+ import threading
6
+ import contextlib
7
+ from datetime import datetime
8
+ from Podflow.basic.http_client import http_client
9
+
10
+
11
+ # 时间戳模块
12
+ def time_stamp():
13
+ time_stamps = []
14
+
15
+ # 获取时间戳淘宝
16
+ def time_stamp_taobao():
17
+ if response := http_client(
18
+ "http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp",
19
+ "",
20
+ 1,
21
+ 0,
22
+ ):
23
+ response_json = response.json()
24
+ with contextlib.suppress(KeyError):
25
+ time_stamps.append(int(response_json["data"]["t"]))
26
+
27
+ # 获取时间戳美团
28
+ def time_stamp_meituan():
29
+ if response := http_client(
30
+ "https://cube.meituan.com/ipromotion/cube/toc/component/base/getServerCurrentTime",
31
+ "",
32
+ 1,
33
+ 0,
34
+ ):
35
+ response_json = response.json()
36
+ with contextlib.suppress(KeyError):
37
+ time_stamps.append(int(response_json["data"]))
38
+
39
+ # 获取时间戳苏宁
40
+ def time_stamp_suning():
41
+ if response := http_client("https://f.m.suning.com/api/ct.do", "", 1, 0):
42
+ response_json = response.json()
43
+ with contextlib.suppress(KeyError):
44
+ time_stamps.append(int(response_json["currentTime"]))
45
+
46
+ # 创建线程
47
+ thread1 = threading.Thread(target=time_stamp_taobao)
48
+ thread2 = threading.Thread(target=time_stamp_meituan)
49
+ thread3 = threading.Thread(target=time_stamp_suning)
50
+ # 启动线程
51
+ thread1.start()
52
+ thread2.start()
53
+ thread3.start()
54
+ # 等待线程结束
55
+ thread1.join()
56
+ thread2.join()
57
+ thread3.join()
58
+ if time_stamps:
59
+ return int(sum(time_stamps) / len(time_stamps))
60
+ print(f"{datetime.now().strftime('%H:%M:%S')}|\033[31m获取时间戳api失败\033[0m")
61
+ return round(time.time() * 1000)
@@ -0,0 +1,11 @@
1
+ # Podflow/basic/vary_replace.py
2
+ # coding: utf-8
3
+
4
+ import re
5
+
6
+
7
+ # 批量正则表达式替换删除模块
8
+ def vary_replace(varys, text):
9
+ for vary in varys:
10
+ text = re.sub(vary, "", text)
11
+ return text
@@ -0,0 +1,43 @@
1
+ # Podflow/basic/write_log.py
2
+ # coding: utf-8
3
+
4
+ import re
5
+ from datetime import datetime
6
+ from Podflow.basic.file_save import file_save
7
+
8
+
9
+ # 日志模块
10
+ def write_log(
11
+ log,
12
+ suffix=None,
13
+ display=True,
14
+ time_display=True,
15
+ only_log="",
16
+ file_name="Podflow.log",
17
+ ):
18
+ # 获取当前的具体时间
19
+ current_time = datetime.now()
20
+ # 格式化输出, 只保留年月日时分秒
21
+ formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S")
22
+ # 打开文件, 并读取原有内容
23
+ try:
24
+ with open(file_name, "r", encoding="utf-8") as file:
25
+ contents = file.read()
26
+ except FileNotFoundError:
27
+ contents = ""
28
+ # 将新的日志内容添加在原有内容之前
29
+ log_in = re.sub(r"\033\[[0-9;]+m", "", log)
30
+ log_in = re.sub(r"\n", "", log_in)
31
+ only_log = re.sub(r"\033\[[0-9;]+m", "", only_log)
32
+ new_contents = (
33
+ f"{formatted_time} {log_in}{only_log}\n{contents}"
34
+ if only_log
35
+ else f"{formatted_time} {log_in}\n{contents}"
36
+ )
37
+ # 将新的日志内容写入文件
38
+ file_save(new_contents, file_name)
39
+ if display:
40
+ formatted_time_mini = current_time.strftime("%H:%M:%S")
41
+ log_print = f"{formatted_time_mini}|{log}" if time_display else f"{log}"
42
+ log_print = f"{log_print}|{suffix}" if suffix else f"{log_print}"
43
+ print(log_print)
@@ -0,0 +1,2 @@
1
+ # Podflow/bilibili/__init__.py
2
+ # coding: utf-8