podflow 20250221__py3-none-any.whl → 20250301__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.
@@ -16,7 +16,7 @@ def http_client(
16
16
  cookies=None,
17
17
  data=None,
18
18
  mode="get",
19
- filename=None
19
+ filename=None,
20
20
  ):
21
21
  user_agent = {
22
22
  "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"
Podflow/bilibili/login.py CHANGED
@@ -8,9 +8,9 @@ import re
8
8
  import time
9
9
  from datetime import datetime
10
10
  import requests
11
- from Cryptodome.Cipher import PKCS1_OAEP
12
- from Cryptodome.Hash import SHA256
13
- from Cryptodome.PublicKey import RSA
11
+ from Crypto.Cipher import PKCS1_OAEP
12
+ from Crypto.Hash import SHA256
13
+ from Crypto.PublicKey import RSA
14
14
  from Podflow.basic.file_save import file_save
15
15
  from Podflow.basic.http_client import http_client
16
16
  from Podflow.basic.qr_code import qr_code
@@ -8,7 +8,7 @@ from Podflow.download.dl_aideo_video import dl_aideo_video
8
8
 
9
9
  # 下载YouTube和哔哩哔哩视频
10
10
  def youtube_and_bilibili_download():
11
- for video_id, format_value in gVar.video_id_update_format.items():
11
+ for video_id, format_value in gVar.video_id_update_format.copy().items():
12
12
  if isinstance(format_value, dict) and format_value["main"] not in gVar.video_id_failed:
13
13
  output_dir_name = format_value["name"]
14
14
  display_color = "\033[35m" if format_value["backward_update"] else "\033[95m"
@@ -28,3 +28,4 @@ def youtube_and_bilibili_download():
28
28
  write_log(
29
29
  f"{display_color}{output_dir_name}\033[0m|{video_id} \033[31m无法下载\033[0m"
30
30
  )
31
+ del gVar.video_id_update_format[video_id]
@@ -0,0 +1,24 @@
1
+ # Podflow/download_and_build.py
2
+ # coding: utf-8
3
+
4
+ import threading
5
+ from Podflow.youtube.build import get_youtube_introduction
6
+ from Podflow.message.create_main_rss import create_main_rss
7
+ from Podflow.download.youtube_and_bilibili_download import youtube_and_bilibili_download
8
+
9
+
10
+ def get_and_duild():
11
+ get_youtube_introduction()
12
+ create_main_rss()
13
+
14
+
15
+ # 下载并构建YouTube和哔哩哔哩视频模块
16
+ def download_and_build():
17
+ thread_download = threading.Thread(target=youtube_and_bilibili_download)
18
+ thread_build = threading.Thread(target=get_and_duild)
19
+
20
+ thread_download.start()
21
+ thread_build.start()
22
+
23
+ thread_download.join()
24
+ thread_build.join()
@@ -4,18 +4,11 @@
4
4
  import socket
5
5
 
6
6
 
7
- def port_judge(host, port):
8
- # 创建一个新的 socket
9
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
10
- # 设置 socket 为可重用
11
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
7
+ def port_judge(hostip, port):
12
8
  try:
13
- # 尝试绑定到指定端口
14
- sock.bind((host, port))
9
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
10
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
11
+ sock.bind((hostip, port))
12
+ return True
15
13
  except OSError:
16
- # 如果绑定失败,说明端口被占用
17
14
  return False
18
- else:
19
- # 如果绑定成功,端口可用,关闭 socket
20
- sock.close()
21
- return True
Podflow/main.py CHANGED
@@ -1,63 +1,10 @@
1
1
  # Podflow/main.py
2
2
  # coding: utf-8
3
3
 
4
- import sys
5
- import json
6
- import time
7
- import urllib
8
- import subprocess
9
4
  from datetime import datetime
10
5
  from importlib.metadata import version
11
- import cherrypy
12
-
13
- # 基本功能模块
14
- from Podflow import gVar, parse
6
+ from Podflow.main_podcast import main_podcast
15
7
  from Podflow.parse_arguments import parse_arguments
16
- from Podflow.basic.split_dict import split_dict
17
-
18
- # 网络和 HTTP 模块
19
- from Podflow.httpfs.port_judge import port_judge
20
- from Podflow.httpfs.app_bottle import bottle_app_instance
21
-
22
- # 下载和视频处理模块
23
- from Podflow.download.delete_part import delete_part
24
- from Podflow.download.youtube_and_bilibili_download import youtube_and_bilibili_download
25
- from Podflow.ffmpeg_judge import ffmpeg_judge
26
-
27
- # RSS 和消息处理模块
28
- from Podflow.message.save_rss import save_rss
29
- from Podflow.message.create_main_rss import create_main_rss
30
- from Podflow.message.get_original_rss import get_original_rss
31
- from Podflow.message.original_rss_fail_print import original_rss_fail_print
32
- from Podflow.message.update_information_display import update_information_display
33
- from Podflow.message.update_youtube_bilibili_rss import update_youtube_bilibili_rss
34
- from Podflow.message.get_video_format import get_video_format
35
-
36
- # 登录模块
37
- from Podflow.bilibili.login import get_bilibili_data
38
- from Podflow.youtube.login import get_youtube_cookie
39
-
40
- # 配置和图标模块
41
- from Podflow.config.channge_icon import channge_icon
42
- from Podflow.config.build_original import build_original
43
-
44
- # 制作和修改文件模块
45
- from Podflow.makeup.make_up_file import make_up_file
46
- from Podflow.makeup.make_up_file_mod import make_up_file_mod
47
- from Podflow.makeup.make_up_file_format_mod import make_up_file_format_mod
48
- from Podflow.makeup.del_makeup_format_fail import del_makeup_format_fail
49
-
50
- # 移除模块
51
- from Podflow.remove.remove_file import remove_file
52
- from Podflow.remove.remove_dir import remove_dir
53
-
54
- # 处理 YouTube 信息模块
55
- from Podflow.youtube.build import get_youtube_introduction
56
-
57
- # 长期媒体进行上传模块
58
- from Podflow.upload.add_upload import add_upload
59
- from Podflow.upload.update_upload import update_upload
60
- from Podflow.upload.get_upload_original import get_upload_original
61
8
 
62
9
 
63
10
  def main():
@@ -67,165 +14,7 @@ def main():
67
14
  print(
68
15
  f"{datetime.now().strftime('%H:%M:%S')}|Podflow|{version('Podflow')}开始运行....."
69
16
  )
70
- # 判断是否安装ffmpeg
71
- ffmpeg_judge()
72
- # 初始化
73
- build_original()
74
- # http共享
75
- port = gVar.config["port"]
76
- host = "0.0.0.0"
77
- if port_judge(host, port):
78
- # 启动 CherryPy 服务器
79
- cherrypy.tree.graft(
80
- bottle_app_instance.app_bottle
81
- ) # 将 Bottle 应用嵌入到 CherryPy 中
82
- cherrypy.config.update(
83
- {
84
- "global": {
85
- "tools.sessions.on": True, # 启用会话支持
86
- "server.socket_host": host, # 监听所有 IP 地址
87
- "server.socket_port": port, # 设置监听端口
88
- "log.screen": False, # 禁用屏幕日志输出
89
- "log.access_file": "", # 关闭访问日志
90
- "log.error_file": "", # 关闭错误日志
91
- }
92
- }
93
- )
94
- cherrypy.engine.start() # 启动 CherryPy 服务器
95
- print(
96
- f"{datetime.now().strftime('%H:%M:%S')}|HTTP服务器启动, 端口: \033[32m{port}\033[0m"
97
- )
98
- if parse.httpfs: # HttpFS参数判断, 是否继续运行
99
- cherrypy.engine.block() # 阻止程序退出, 保持HTTP服务运行
100
- else:
101
- print(
102
- f"{datetime.now().strftime('%H:%M:%S')}|HTTP服务器端口: \033[32m{port}\033[0m, \033[31m被占用\033[0m"
103
- )
104
- if parse.httpfs:
105
- sys.exit(0)
106
- # 主流程
107
- while parse.update_num > 0 or parse.update_num == -1: # 循环主更新
108
- # 暂停进程打印
109
- gVar.server_process_print_flag[0] = "pause"
110
- # 获取YouTube cookie
111
- gVar.youtube_cookie = get_youtube_cookie(gVar.channelid_youtube_ids_original)
112
- # 更新哔哩哔哩data
113
- gVar.channelid_bilibili_ids, gVar.bilibili_data = get_bilibili_data(
114
- gVar.channelid_bilibili_ids_original
115
- )
116
- # 恢复进程打印
117
- bottle_app_instance.cherry_print()
118
- # 获取原始xml字典和rss文本
119
- gVar.xmls_original, gVar.hash_rss_original, gVar.xmls_original_fail = (
120
- get_original_rss()
121
- )
122
- # 初始化原始上传信息
123
- get_upload_original()
124
- # 更新Youtube和哔哩哔哩频道xml
125
- update_youtube_bilibili_rss()
126
- # 判断是否有更新内容
127
- if gVar.channelid_youtube_ids_update or gVar.channelid_bilibili_ids_update:
128
- gVar.update_generate_rss = True
129
- if gVar.update_generate_rss:
130
- # 根据日出日落修改封面(只适用原封面)
131
- channge_icon()
132
- # 输出需要更新的信息
133
- update_information_display(
134
- gVar.channelid_youtube_ids_update,
135
- gVar.youtube_content_ytid_update,
136
- gVar.youtube_content_ytid_backward_update,
137
- "YouTube",
138
- )
139
- update_information_display(
140
- gVar.channelid_bilibili_ids_update,
141
- gVar.bilibili_content_bvid_update,
142
- gVar.bilibili_content_bvid_backward_update,
143
- "BiliBili",
144
- )
145
- # 暂停进程打印
146
- gVar.server_process_print_flag[0] = "pause"
147
- # 获取视频格式信息
148
- get_video_format()
149
- # 恢复进程打印
150
- bottle_app_instance.cherry_print()
151
- # 删除中断下载的媒体文件
152
- if gVar.config["delete_incompletement"]:
153
- delete_part(gVar.channelid_youtube_ids | gVar.channelid_bilibili_ids)
154
- # 暂停进程打印
155
- gVar.server_process_print_flag[0] = "pause"
156
- # 下载YouTube和哔哩哔哩视频
157
- youtube_and_bilibili_download()
158
- # 添加新媒体至上传列表
159
- add_upload()
160
- # 恢复进程打印
161
- bottle_app_instance.cherry_print()
162
- # 打印无法保留原节目信息
163
- original_rss_fail_print(gVar.xmls_original_fail)
164
- # 获取YouTube频道简介
165
- get_youtube_introduction()
166
- # 暂停进程打印
167
- gVar.server_process_print_flag[0] = "pause"
168
- # 生成分和主rss
169
- create_main_rss()
170
- # 恢复进程打印
171
- bottle_app_instance.cherry_print()
172
- if gVar.config["remove_media"]:
173
- # 删除不在rss中的媒体文件
174
- remove_file()
175
- # 删除已抛弃的媒体文件夹
176
- remove_dir()
177
- # 补全缺失媒体文件到字典
178
- make_up_file()
179
- # 按参数获取需要补全的最大个数
180
- gVar.make_up_file_format = split_dict(
181
- gVar.make_up_file_format,
182
- gVar.config["completion_count"],
183
- True,
184
- )[0]
185
- # 暂停进程打印
186
- gVar.server_process_print_flag[0] = "pause"
187
- # 补全在rss中缺失的媒体格式信息
188
- make_up_file_format_mod()
189
- # 恢复进程打印
190
- bottle_app_instance.cherry_print()
191
- # 删除无法补全的媒体
192
- del_makeup_format_fail()
193
- # 暂停进程打印
194
- gVar.server_process_print_flag[0] = "pause"
195
- # 保存rss文件模块
196
- save_rss()
197
- # 下载补全Youtube和哔哩哔哩视频模块
198
- make_up_file_mod()
199
- # 恢复进程打印
200
- bottle_app_instance.cherry_print()
201
- # 更新并保存上传列表
202
- update_upload()
203
- else:
204
- print(f"{datetime.now().strftime('%H:%M:%S')}|频道无更新内容")
205
-
206
- # 将需要更新转为否
207
- gVar.update_generate_rss = False
208
- if parse.update_num != -1:
209
- parse.update_num -= 1
210
- if parse.argument == "a-shell":
211
- openserver_process = subprocess.Popen(
212
- [
213
- "open",
214
- f"shortcuts://run-shortcut?name=Podflow&input=text&text={urllib.parse.quote(json.dumps(gVar.shortcuts_url))}",
215
- ]
216
- )
217
- # 延时
218
- time.sleep(60 + len(gVar.shortcuts_url) * 5)
219
- openserver_process.terminate()
220
- break
221
- elif parse.update_num == 0:
222
- break
223
- else:
224
- # 延时
225
- time.sleep(parse.time_delay)
226
- # 关闭CherryPy服务器
227
- cherrypy.engine.exit()
228
- print(f"{datetime.now().strftime('%H:%M:%S')}|Podflow运行结束")
17
+ main_podcast()
229
18
 
230
19
 
231
20
  if __name__ == "__main__":
@@ -0,0 +1,214 @@
1
+ # Podflow/main_podcast.py
2
+ # coding: utf-8
3
+
4
+ import sys
5
+ import json
6
+ import time
7
+ import urllib
8
+ import subprocess
9
+ from datetime import datetime
10
+ import cherrypy
11
+
12
+ # 基本功能模块
13
+ from Podflow import gVar, parse
14
+ from Podflow.basic.split_dict import split_dict
15
+
16
+ # 网络和 HTTP 模块
17
+ from Podflow.httpfs.port_judge import port_judge
18
+ from Podflow.httpfs.app_bottle import bottle_app_instance
19
+
20
+ # 下载和视频处理模块
21
+ from Podflow.download.delete_part import delete_part
22
+ from Podflow.download_and_build import download_and_build
23
+ from Podflow.ffmpeg_judge import ffmpeg_judge
24
+
25
+ # RSS 和消息处理模块
26
+ from Podflow.message.save_rss import save_rss
27
+ from Podflow.message.get_original_rss import get_original_rss
28
+ from Podflow.message.original_rss_fail_print import original_rss_fail_print
29
+ from Podflow.message.update_information_display import update_information_display
30
+ from Podflow.message.update_youtube_bilibili_rss import update_youtube_bilibili_rss
31
+ from Podflow.message.get_video_format import get_video_format
32
+
33
+ # 登录模块
34
+ from Podflow.bilibili.login import get_bilibili_data
35
+ from Podflow.youtube.login import get_youtube_cookie
36
+
37
+ # 配置和图标模块
38
+ from Podflow.config.channge_icon import channge_icon
39
+ from Podflow.config.build_original import build_original
40
+
41
+ # 制作和修改文件模块
42
+ from Podflow.makeup.make_up_file import make_up_file
43
+ from Podflow.makeup.make_up_file_mod import make_up_file_mod
44
+ from Podflow.makeup.make_up_file_format_mod import make_up_file_format_mod
45
+ from Podflow.makeup.del_makeup_format_fail import del_makeup_format_fail
46
+
47
+ # 移除模块
48
+ from Podflow.remove.remove_file import remove_file
49
+ from Podflow.remove.remove_dir import remove_dir
50
+
51
+ # 处理 YouTube 信息模块
52
+ from Podflow.youtube.build import print_fail_youtube_introduction
53
+
54
+ # 长期媒体进行上传模块
55
+ from Podflow.upload.add_upload import add_upload
56
+ from Podflow.upload.update_upload import update_upload
57
+ from Podflow.upload.get_upload_original import get_upload_original
58
+
59
+
60
+ def main_podcast():
61
+ # 判断是否安装ffmpeg
62
+ ffmpeg_judge()
63
+ # 初始化
64
+ build_original()
65
+ # http共享
66
+ port = gVar.config["port"]
67
+ hostip = "0.0.0.0"
68
+ if port_judge(hostip, port):
69
+ # 启动 CherryPy 服务器
70
+ cherrypy.tree.graft(
71
+ bottle_app_instance.app_bottle
72
+ ) # 将 Bottle 应用嵌入到 CherryPy 中
73
+ cherrypy.config.update(
74
+ {
75
+ "global": {
76
+ "tools.sessions.on": True, # 启用会话支持
77
+ "server.socket_host": hostip, # 监听所有 IP 地址
78
+ "server.socket_port": port, # 设置监听端口
79
+ "log.screen": False, # 禁用屏幕日志输出
80
+ "log.access_file": "", # 关闭访问日志
81
+ "log.error_file": "", # 关闭错误日志
82
+ }
83
+ }
84
+ )
85
+ cherrypy.engine.start() # 启动 CherryPy 服务器
86
+ print(
87
+ f"{datetime.now().strftime('%H:%M:%S')}|HTTP服务器启动, 端口: \033[32m{port}\033[0m"
88
+ )
89
+ if parse.httpfs: # HttpFS参数判断, 是否继续运行
90
+ cherrypy.engine.block() # 阻止程序退出, 保持HTTP服务运行
91
+ sys.exit(0)
92
+ else:
93
+ print(
94
+ f"{datetime.now().strftime('%H:%M:%S')}|HTTP服务器端口: \033[32m{port}\033[0m, \033[31m被占用\033[0m"
95
+ )
96
+ if parse.httpfs:
97
+ sys.exit(0)
98
+ # 主流程
99
+ while parse.update_num > 0 or parse.update_num == -1: # 循环主更新
100
+ # 暂停进程打印
101
+ gVar.server_process_print_flag[0] = "pause"
102
+ # 获取YouTube cookie
103
+ gVar.youtube_cookie = get_youtube_cookie(gVar.channelid_youtube_ids_original)
104
+ # 更新哔哩哔哩data
105
+ gVar.channelid_bilibili_ids, gVar.bilibili_data = get_bilibili_data(
106
+ gVar.channelid_bilibili_ids_original
107
+ )
108
+ # 恢复进程打印
109
+ bottle_app_instance.cherry_print()
110
+ # 获取原始xml字典和rss文本
111
+ gVar.xmls_original, gVar.hash_rss_original, gVar.xmls_original_fail = (
112
+ get_original_rss()
113
+ )
114
+ # 初始化原始上传信息
115
+ get_upload_original()
116
+ # 更新Youtube和哔哩哔哩频道xml
117
+ update_youtube_bilibili_rss()
118
+ # 判断是否有更新内容
119
+ if gVar.channelid_youtube_ids_update or gVar.channelid_bilibili_ids_update:
120
+ gVar.update_generate_rss = True
121
+ if gVar.update_generate_rss:
122
+ # 根据日出日落修改封面(只适用原封面)
123
+ channge_icon()
124
+ # 输出需要更新的信息
125
+ update_information_display(
126
+ gVar.channelid_youtube_ids_update,
127
+ gVar.youtube_content_ytid_update,
128
+ gVar.youtube_content_ytid_backward_update,
129
+ "YouTube",
130
+ )
131
+ update_information_display(
132
+ gVar.channelid_bilibili_ids_update,
133
+ gVar.bilibili_content_bvid_update,
134
+ gVar.bilibili_content_bvid_backward_update,
135
+ "BiliBili",
136
+ )
137
+ # 暂停进程打印
138
+ gVar.server_process_print_flag[0] = "pause"
139
+ # 获取视频格式信息
140
+ get_video_format()
141
+ # 恢复进程打印
142
+ bottle_app_instance.cherry_print()
143
+ # 删除中断下载的媒体文件
144
+ if gVar.config["delete_incompletement"]:
145
+ delete_part(gVar.channelid_youtube_ids | gVar.channelid_bilibili_ids)
146
+ # 暂停进程打印
147
+ gVar.server_process_print_flag[0] = "pause"
148
+ # 下载并构建YouTube和哔哩哔哩视频
149
+ download_and_build()
150
+ # 添加新媒体至上传列表
151
+ add_upload()
152
+ # 恢复进程打印
153
+ bottle_app_instance.cherry_print()
154
+ # 打印无法保留原节目信息
155
+ original_rss_fail_print(gVar.xmls_original_fail)
156
+ # 打印无法获取youtube频道简介
157
+ print_fail_youtube_introduction()
158
+ if gVar.config["remove_media"]:
159
+ # 删除不在rss中的媒体文件
160
+ remove_file()
161
+ # 删除已抛弃的媒体文件夹
162
+ remove_dir()
163
+ # 补全缺失媒体文件到字典
164
+ make_up_file()
165
+ # 按参数获取需要补全的最大个数
166
+ gVar.make_up_file_format = split_dict(
167
+ gVar.make_up_file_format,
168
+ gVar.config["completion_count"],
169
+ True,
170
+ )[0]
171
+ # 暂停进程打印
172
+ gVar.server_process_print_flag[0] = "pause"
173
+ # 补全在rss中缺失的媒体格式信息
174
+ make_up_file_format_mod()
175
+ # 恢复进程打印
176
+ bottle_app_instance.cherry_print()
177
+ # 删除无法补全的媒体
178
+ del_makeup_format_fail()
179
+ # 暂停进程打印
180
+ gVar.server_process_print_flag[0] = "pause"
181
+ # 保存rss文件模块
182
+ save_rss()
183
+ # 下载补全Youtube和哔哩哔哩视频模块
184
+ make_up_file_mod()
185
+ # 恢复进程打印
186
+ bottle_app_instance.cherry_print()
187
+ # 更新并保存上传列表
188
+ update_upload()
189
+ else:
190
+ print(f"{datetime.now().strftime('%H:%M:%S')}|频道无更新内容")
191
+
192
+ # 将需要更新转为否
193
+ gVar.update_generate_rss = False
194
+ if parse.update_num != -1:
195
+ parse.update_num -= 1
196
+ if parse.argument == "a-shell":
197
+ openserver_process = subprocess.Popen(
198
+ [
199
+ "open",
200
+ f"shortcuts://run-shortcut?name=Podflow&input=text&text={urllib.parse.quote(json.dumps(gVar.shortcuts_url))}",
201
+ ]
202
+ )
203
+ # 延时
204
+ time.sleep(60 + len(gVar.shortcuts_url) * 5)
205
+ openserver_process.terminate()
206
+ break
207
+ elif parse.update_num == 0:
208
+ break
209
+ else:
210
+ # 延时
211
+ time.sleep(parse.time_delay)
212
+ # 关闭CherryPy服务器
213
+ cherrypy.engine.exit()
214
+ print(f"{datetime.now().strftime('%H:%M:%S')}|Podflow运行结束")
Podflow/main_upload.py ADDED
@@ -0,0 +1,28 @@
1
+ # Podflow/main_upload.py
2
+ # coding: utf-8
3
+
4
+ import sys
5
+ import threading
6
+ from Podflow.upload.upload_print import upload_print
7
+ from Podflow.upload.linked_server import handle_discovery, usable_port
8
+
9
+
10
+ def main_upload():
11
+ # 服务发现相关配置
12
+ broadcast_port = 37001 # 服务发现用端口
13
+ service_port = 5000 # 实际服务端口
14
+
15
+ broadcast_port = usable_port(broadcast_port, 37100)
16
+ service_port = usable_port(service_port, 5050)
17
+ if broadcast_port and service_port:
18
+ discovery_thread = threading.Thread(
19
+ target=handle_discovery,
20
+ args=(broadcast_port, service_port),
21
+ )
22
+ discovery_thread.start()
23
+ else:
24
+ if not broadcast_port:
25
+ upload_print("\033[31m广播端口被占用\033[0m")
26
+ if not service_port:
27
+ upload_print("\033[31m服务端口被占用\033[0m")
28
+ sys.exit(0)
@@ -1,34 +1,69 @@
1
1
  # Podflow/message/create_main_rss.py
2
2
  # coding: utf-8
3
3
 
4
+ import time
4
5
  from Podflow import gVar
5
6
  from Podflow.youtube.build import youtube_xml_items
6
7
  from Podflow.bilibili.build import bilibili_xml_items
7
8
  from Podflow.message.get_media_name import get_media_name
8
9
 
9
10
 
11
+ def update_output_dir():
12
+ output_dirs = []
13
+ for format_value in gVar.video_id_update_format.values():
14
+ if (
15
+ isinstance(format_value, dict)
16
+ and format_value["main"] not in gVar.video_id_failed
17
+ ):
18
+ output_dirs.append(format_value["id"])
19
+ return output_dirs
20
+
21
+
10
22
  # 生成主rss模块
11
23
  def create_main_rss():
12
24
  channelid_youtube_ids = gVar.channelid_youtube_ids
13
- for output_dir, output_dir_youtube in channelid_youtube_ids.items():
14
- channelid_youtube_value = gVar.channelid_youtube[output_dir_youtube]
15
- items = youtube_xml_items(output_dir)
16
- items["DisplayRSSaddress"] = channelid_youtube_value["DisplayRSSaddress"]
17
- items["QRcode"] = channelid_youtube_value["QRcode"]
18
- items["ID_Name"] = output_dir_youtube
19
- items["InmainRSS"] = channelid_youtube_value["InmainRSS"]
20
- items["type"] = "youtube"
21
- gVar.all_youtube_content_ytid[output_dir] = get_media_name("youtube", items["items"])
22
- gVar.all_items[output_dir] = items
23
-
24
25
  channelid_bilibili_ids = gVar.channelid_bilibili_ids
25
- for output_dir, output_dir_bilibili in channelid_bilibili_ids.items():
26
- channelid_bilibili_value = gVar.channelid_bilibili[output_dir_bilibili]
27
- items = bilibili_xml_items(output_dir)
28
- items["DisplayRSSaddress"] = channelid_bilibili_value["DisplayRSSaddress"]
29
- items["QRcode"] = channelid_bilibili_value["QRcode"]
30
- items["ID_Name"] = output_dir_bilibili
31
- items["InmainRSS"] = channelid_bilibili_value["InmainRSS"]
32
- items["type"] = "bilibili"
33
- gVar.all_bilibili_content_bvid[output_dir] = get_media_name("bilibili", items["items"])
34
- gVar.all_items[output_dir] = items
26
+ gVar.all_items = {
27
+ key: {} for key in channelid_youtube_ids | channelid_bilibili_ids
28
+ }
29
+ all_channelid = list(gVar.all_items.keys())
30
+
31
+ while all_channelid:
32
+ for index, output_dir in enumerate(all_channelid):
33
+ if output_dir in update_output_dir():
34
+ time.sleep(1)
35
+ else:
36
+ if output_dir in channelid_youtube_ids:
37
+ output_dir_youtube = channelid_youtube_ids[output_dir]
38
+ channelid_youtube_value = gVar.channelid_youtube[output_dir_youtube]
39
+ items = youtube_xml_items(output_dir)
40
+ items["DisplayRSSaddress"] = channelid_youtube_value[
41
+ "DisplayRSSaddress"
42
+ ]
43
+ items["QRcode"] = channelid_youtube_value["QRcode"]
44
+ items["ID_Name"] = output_dir_youtube
45
+ items["InmainRSS"] = channelid_youtube_value["InmainRSS"]
46
+ items["type"] = "youtube"
47
+ gVar.all_youtube_content_ytid[output_dir] = get_media_name(
48
+ "youtube", items["items"]
49
+ )
50
+ gVar.all_items[output_dir] = items
51
+ elif output_dir in channelid_bilibili_ids:
52
+ output_dir_bilibili = channelid_bilibili_ids[output_dir]
53
+ channelid_bilibili_value = gVar.channelid_bilibili[
54
+ output_dir_bilibili
55
+ ]
56
+ items = bilibili_xml_items(output_dir)
57
+ items["DisplayRSSaddress"] = channelid_bilibili_value[
58
+ "DisplayRSSaddress"
59
+ ]
60
+ items["QRcode"] = channelid_bilibili_value["QRcode"]
61
+ items["ID_Name"] = output_dir_bilibili
62
+ items["InmainRSS"] = channelid_bilibili_value["InmainRSS"]
63
+ items["type"] = "bilibili"
64
+ gVar.all_bilibili_content_bvid[output_dir] = get_media_name(
65
+ "bilibili", items["items"]
66
+ )
67
+ gVar.all_items[output_dir] = items
68
+ del all_channelid[index]
69
+ break
@@ -0,0 +1,74 @@
1
+ # Podflow/upload/linked_client.py
2
+ # coding: utf-8
3
+
4
+ import time
5
+ import socket
6
+ from datetime import datetime
7
+ from Podflow.upload.upload_print import upload_print
8
+
9
+
10
+ BROADCAST_PORT = 37000
11
+ TIMEOUT = 1 # 搜索超时时间(秒)
12
+ MAX_BROADCAST_PORT = 37101 # 尝试广播的最大端口
13
+
14
+
15
+ # 发现局域网内的服务器
16
+ def discover_server(broadcast_port, timeout):
17
+ servers = []
18
+
19
+ # 创建UDP socket
20
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
21
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
22
+ sock.settimeout(timeout)
23
+
24
+ try:
25
+ # 发送广播请求
26
+ sock.sendto(b"DISCOVER_SERVER_REQUEST", ("<broadcast>", broadcast_port))
27
+ except Exception as e:
28
+ upload_print(f"广播请求发送失败: {e}")
29
+ return servers
30
+
31
+ # 等待响应
32
+ start_time = time.time()
33
+ while time.time() - start_time < timeout:
34
+ try:
35
+ data, addr = sock.recvfrom(1024)
36
+ if data.startswith(b"SERVER_INFO|"):
37
+ try:
38
+ port = int(data.decode().split("|")[1])
39
+ servers.append((addr[0], port))
40
+ except (IndexError, ValueError):
41
+ upload_print(f"收到来自 {addr} 的服务响应格式不正确")
42
+ except socket.timeout:
43
+ break
44
+ except Exception as e:
45
+ upload_print(f"接收数据出错: {e}")
46
+ break
47
+
48
+ return servers
49
+
50
+
51
+ # 自动发现并连接服务器模块
52
+ def connect_server():
53
+ upload_print("正在搜索服务器...")
54
+
55
+ current_port = BROADCAST_PORT
56
+ servers = []
57
+ time_print = f"{datetime.now().strftime('%H:%M:%S')}|"
58
+
59
+ # 在允许的端口范围内尝试发现服务器
60
+ while current_port < MAX_BROADCAST_PORT:
61
+ print(f"\r{time_print}正在尝试广播端口 {current_port}...", end="")
62
+ servers = discover_server(current_port, TIMEOUT)
63
+ if servers:
64
+ print("")
65
+ break
66
+ current_port += 1
67
+
68
+ if not servers:
69
+ upload_print("找不到服务器")
70
+ return
71
+
72
+ # 选择第一个找到的服务器
73
+ server_ip, server_port = servers[0]
74
+ upload_print(f"正在连接到{server_ip}:{server_port}")
@@ -0,0 +1,32 @@
1
+ # Podflow/upload/linked_server.py
2
+ # coding: utf-8
3
+
4
+ import socket
5
+ from Podflow.httpfs.port_judge import port_judge
6
+ from Podflow.upload.upload_print import upload_print
7
+
8
+
9
+ def usable_port(port, max_num):
10
+ hostip = "0.0.0.0"
11
+ while port <= max_num:
12
+ if port_judge(hostip, port):
13
+ return port
14
+ else:
15
+ port += 1
16
+ return None
17
+
18
+
19
+ # 处理服务发现请求的UDP服务模块
20
+ def handle_discovery(broadcast_port, service_port):
21
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
22
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
23
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
24
+ sock.bind(("", broadcast_port))
25
+
26
+ upload_print("发现服务已启动...")
27
+ while True:
28
+ data, addr = sock.recvfrom(1024)
29
+ if data == b"DISCOVER_SERVER_REQUEST":
30
+ upload_print(f"来自{addr}的发现请求")
31
+ response = f"SERVER_INFO|{service_port}".encode()
32
+ sock.sendto(response, addr)
@@ -0,0 +1,8 @@
1
+ # Podflow/upload/upload_print.py
2
+ # coding: utf-8
3
+
4
+ from datetime import datetime
5
+
6
+
7
+ def upload_print(text):
8
+ print(f"{datetime.now().strftime('%H:%M:%S')}|{text}")
Podflow/youtube/build.py CHANGED
@@ -8,8 +8,6 @@ import contextlib
8
8
  import xml.etree.ElementTree as ET
9
9
  from datetime import datetime, timezone
10
10
  from Podflow import gVar
11
- from Podflow.message.xml_rss import xml_rss
12
- from Podflow.basic.file_save import file_save
13
11
  from Podflow.message.xml_item import xml_item
14
12
  from Podflow.basic.http_client import http_client
15
13
  from Podflow.message.format_time import format_time
@@ -25,10 +23,9 @@ def get_youtube_introduction():
25
23
  # 使用http获取youtube频道简介和图标模块
26
24
  def youtube_xml_get(output_dir):
27
25
  if channel_about := http_client(
28
- f"https://www.youtube.com/channel/{output_dir}/about",
29
- f"{gVar.channelid_youtube_ids[output_dir]} 简介",
30
- 2,
31
- 5,
26
+ url=f"https://www.youtube.com/channel/{output_dir}/about",
27
+ max_retries=2,
28
+ retry_delay=5,
32
29
  ):
33
30
  channel_about = channel_about.text
34
31
  xml_tree = {
@@ -46,6 +43,8 @@ def get_youtube_introduction():
46
43
  channel_about,
47
44
  flags=re.DOTALL,
48
45
  ).group()
46
+ else:
47
+ xml_tree = False
49
48
  with youtube_xml_get_lock:
50
49
  gVar.youtube_xml_get_tree[output_dir] = xml_tree
51
50
 
@@ -60,6 +59,15 @@ def get_youtube_introduction():
60
59
  thread.join()
61
60
 
62
61
 
62
+ # 打印无法获取youtube频道简介模块
63
+ def print_fail_youtube_introduction():
64
+ for output_dir, xml_tree in gVar.youtube_xml_get_tree:
65
+ if isinstance(xml_tree, bool) and not xml_tree:
66
+ print(
67
+ f"{datetime.now().strftime('%H:%M:%S')}|{gVar.channelid_youtube_ids[output_dir]} 简介获取失败"
68
+ )
69
+
70
+
63
71
  # 获取YouTube播放列表模块
64
72
  def get_youtube_playlist(url, channelid_title):
65
73
  videoids = []
@@ -69,7 +77,15 @@ def get_youtube_playlist(url, channelid_title):
69
77
  0
70
78
  ]["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0][
71
79
  "itemSectionRenderer"
72
- ]["contents"][0]["playlistVideoListRenderer"]["contents"]
80
+ ][
81
+ "contents"
82
+ ][
83
+ 0
84
+ ][
85
+ "playlistVideoListRenderer"
86
+ ][
87
+ "contents"
88
+ ]
73
89
  videoids.extend(
74
90
  content["playlistVideoRenderer"]["videoId"] for content in contents
75
91
  )
@@ -262,12 +278,14 @@ def youtube_xml_items(output_dir):
262
278
  except Exception: # 参数不存在直接更新
263
279
  description = gVar.config["description"]
264
280
  icon = gVar.config["icon"]
281
+ youtube_xml_get_tree = gVar.youtube_xml_get_tree
265
282
  if (
266
283
  output_dir in gVar.channelid_youtube_ids_update
267
- and output_dir in gVar.youtube_xml_get_tree
284
+ and output_dir in youtube_xml_get_tree
285
+ and isinstance(youtube_xml_get_tree[output_dir], dict)
268
286
  ):
269
- description = gVar.youtube_xml_get_tree[output_dir]["description"]
270
- icon = gVar.youtube_xml_get_tree[output_dir]["icon"]
287
+ description = youtube_xml_get_tree[output_dir]["description"]
288
+ icon = youtube_xml_get_tree[output_dir]["icon"]
271
289
  category = gVar.config["category"]
272
290
  if output_dir_value["type"] == "dict":
273
291
  title = output_dir_value["content"]["title"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250221
3
+ Version: 20250301
4
4
  Summary: A podcast server that includes YouTube and BiliBili
5
5
  Home-page: https://github.com/gruel-zxz/podflow
6
6
  Author: gruel_zxz
@@ -1,6 +1,9 @@
1
1
  Podflow/__init__.py,sha256=fRgwxrydKSb9rK_ZsZsb2XhbGEgxxTYKmLB4f88fg0M,6908
2
+ Podflow/download_and_build.py,sha256=LeLjLHnF5N9vdrroDsB-mT4UKY2OwPSL3APUAkhTuwc,671
2
3
  Podflow/ffmpeg_judge.py,sha256=krttVs1PDot_0CDq5rmtUIpchiaiqtAkPYFQRLG6OQM,1275
3
- Podflow/main.py,sha256=nfegfi0Wggw3AlNF4mHHudOgONbtBleSOhpdnYinFU8,8854
4
+ Podflow/main.py,sha256=qSUmVrY5uM1pAViEnPkk9Dx_18NqbZE8W2_fU-Hg2O8,456
5
+ Podflow/main_podcast.py,sha256=IUVEEYpXfyVNSACkDriTPgLsQhjC1xHFZTP5V3zwZZw,8289
6
+ Podflow/main_upload.py,sha256=FzWWRDK5GmvMas-BcTRBq1H5djVgg5-NF8vXMoo7Sko,886
4
7
  Podflow/parse_arguments.py,sha256=qa5228sralPV7s4JdB6UgS1ug66rBqckDvSOwjz2tIc,2171
5
8
  Podflow/basic/__init__.py,sha256=SyFA_F_0-zn1VejCL83IPeZx1k2HCiqNY-0l0bp1N88,44
6
9
  Podflow/basic/file_save.py,sha256=mstWM6njYtCpq2sa28JD_neClvVLcjJOrGlCXBjUnZs,696
@@ -8,7 +11,7 @@ Podflow/basic/folder_build.py,sha256=Bt1ab1shKIluvSRzWqxaFmvG3AVU3kwJNzwSesdNlIU
8
11
  Podflow/basic/get_duration.py,sha256=e9w8XktV-ozdO0v0dlsUMqcyKJZpjcibCpY-wfwZt54,532
9
12
  Podflow/basic/get_file_list.py,sha256=Y6Nqn6lxhPXiIT2eeCkY7fMPrzFClwjv7kXej4CCFCY,2389
10
13
  Podflow/basic/get_html_dict.py,sha256=EZQ0Wlu87SFbUFZuakE7JWgFx53FQKpLbP78L7Gww3E,869
11
- Podflow/basic/http_client.py,sha256=bwhZVauQplM1DIFQ7tDO53bhVEbD_4fyl1QSc0xq9l4,3002
14
+ Podflow/basic/http_client.py,sha256=gvaxg_1lz81tnWCq2HIh-oliShWJCP77JUnYl2v5qng,3003
12
15
  Podflow/basic/list_merge_tidy.py,sha256=o9cEnfEK0bhjf5awls5HyUzg0siPa1e4V97-kqxlpt0,368
13
16
  Podflow/basic/qr_code.py,sha256=3q2omeQTDIt4aDlfPGVpiynfAHW7K4XYx59LBSUiEDk,1704
14
17
  Podflow/basic/split_dict.py,sha256=rxwsGuzBWXJ8nDxcxYI-BqeeccyDTsw011tjkT8rZwI,401
@@ -19,7 +22,7 @@ Podflow/basic/write_log.py,sha256=Sm6qzTSb1x6X2uQnnmVD4dU6BsY1PONxenwsWAdFxso,13
19
22
  Podflow/bilibili/__init__.py,sha256=ekvpCzR4bg6ar73Gq0YWQcPXND4TEqP46pb9iLyke9g,47
20
23
  Podflow/bilibili/build.py,sha256=Oeh6Cqb61F5zQWe8ohDP0QKwykAa4_I2fa-Yl6ZxkCw,7957
21
24
  Podflow/bilibili/get.py,sha256=PXPh_H8B_zP9dhqe6sA_QCeJcdnriBCpbF7mPq46xMw,20337
22
- Podflow/bilibili/login.py,sha256=aYKEj5re-JYXnA0aCOKUF9Cqwwd7FTOuAu_yvcaqdag,10944
25
+ Podflow/bilibili/login.py,sha256=ujczE9rWKFJQ7zIJ7mvlrf2XpXpn6sQwtGtqPsaf3wg,10932
23
26
  Podflow/config/__init__.py,sha256=BroaS1uvdLb00OQeJXc1i7QeuErD-U-2QjynQH5IDdE,45
24
27
  Podflow/config/build_original.py,sha256=tZtsGUi1rG6DH6cxaiaVQPRtIl6U0-FqOdeho7CXl0s,1809
25
28
  Podflow/config/channge_icon.py,sha256=z2tFFiP7aZFiMP1RqXkrovWAxBOI77rTHKhuzzhFfn0,5653
@@ -34,10 +37,10 @@ Podflow/download/delete_part.py,sha256=G9lK8G8tOOmnEBmAHJKPdqPeLhJ61S5m8o6iL5g9S
34
37
  Podflow/download/dl_aideo_video.py,sha256=8Y8agC4bBCpWLDE6zWfCAi0PrT_ik302XkufxgXK1z8,10001
35
38
  Podflow/download/show_progress.py,sha256=7oGzhj_frO2A1o5JeGuHn7bHtma5oPpD_IWcctIO2rs,1595
36
39
  Podflow/download/wait_animation.py,sha256=E2V3cm-10e5Iif40oU722OfzDe7YiLMbDqsjZ6dshBE,1056
37
- Podflow/download/youtube_and_bilibili_download.py,sha256=jt6CWb8ZJ-dyIom7fJ_K1qve9pSzoW0hNwfWnZiju7w,1228
40
+ Podflow/download/youtube_and_bilibili_download.py,sha256=uHb-ZirbDXmnW_gDTmUtus6xqLy8flXwKmSkeslN_dc,1285
38
41
  Podflow/httpfs/__init__.py,sha256=agnAtd2Xe0qfOrElKgoBattAVqUBdj79wU2e2UOpcJM,45
39
42
  Podflow/httpfs/app_bottle.py,sha256=B5rGSxSNFbrjZfmtH7GVYdHCNDZ0T_7Jxrh4_oM8-FU,9333
40
- Podflow/httpfs/port_judge.py,sha256=OSNyE-aWRFB8aPmv2LJugRSQdBkwoxrqv7_rEP7OFzM,565
43
+ Podflow/httpfs/port_judge.py,sha256=KozmaCGlMVh3E0ONekxSUstJWthhwdLK4WoJ84u1c5Q,349
41
44
  Podflow/makeup/__init__.py,sha256=HaBchKbUjRqqXSGCkMfEqLOyx3tlqB2htXvTDs59owI,45
42
45
  Podflow/makeup/del_makeup_format_fail.py,sha256=rUSJD5lXMBM5pCde1amrtF6s93MMHp66-NHCXZy56AI,642
43
46
  Podflow/makeup/make_up_file.py,sha256=6r5Mf_zK9snLS2hByD-HyKyuKWTccqqDJXh39GKhq28,2222
@@ -45,7 +48,7 @@ Podflow/makeup/make_up_file_format_mod.py,sha256=NMG8gzp1ARSyZWZvMta3BMFPH75o1YY
45
48
  Podflow/makeup/make_up_file_mod.py,sha256=CDwgC8BKPf1o1x5U5DF8UR1gtoAEPmhknokkqGLdJWY,1075
46
49
  Podflow/message/__init__.py,sha256=SC42QCYhGwCvzvBnxiBz6L1MN-l57WHbH7PIh-UyPdI,46
47
50
  Podflow/message/backup_zip_save.py,sha256=cU1oLO68INKSlmckYNCWmdZKu51T-HNuVFSHYzA9a-8,1740
48
- Podflow/message/create_main_rss.py,sha256=fGKAvbH6faS72p6g3t1iFlNSWusd-kxJhDJyfTItMkc,1657
51
+ Podflow/message/create_main_rss.py,sha256=R-8nfTp7cg1m9BfK-YGfb186iR_miMwDxszwOLFc-eQ,2946
49
52
  Podflow/message/display_qrcode_and_url.py,sha256=Ren6ly_W-c__FGlRxXrpNSyV16IOu-OwHDNOkzB6La0,1016
50
53
  Podflow/message/fail_message_initialize.py,sha256=J7yjPo7vaL5Ix3lHOK13DE2zZ_xmdFF9xokarajtv6E,6299
51
54
  Podflow/message/format_time.py,sha256=2Z7rPh3rrU68UoDO5fFF_ipV_SEmtotziBmBBl0mHdc,909
@@ -74,13 +77,16 @@ Podflow/remove/remove_file.py,sha256=ACO2iK8RAZKGJ08uABbzGwu5-B0RLklQ-EOOkSuPFiU
74
77
  Podflow/upload/__init__.py,sha256=9uOIL8vr_vP_XYrhfZ4IDZmfSGyCf0_MpLOc-KnbpSY,45
75
78
  Podflow/upload/add_upload.py,sha256=8kxHDLFa225WFze3oTPNtOWbcECF--bQXuALfsRJLoo,1432
76
79
  Podflow/upload/get_upload_original.py,sha256=h2aoutZ-po40N8auRDbKvse14261Y5H7vHgfAN23vV0,4358
80
+ Podflow/upload/linked_client.py,sha256=mprI2ZEGQt9v8hRa6a3QxfFjkOWu-TxAq5dSeeptHrI,2299
81
+ Podflow/upload/linked_server.py,sha256=qbAzkxcy1M_JOCOx0WwZYWpTXYbS2OBcD4geLhEzE08,1041
77
82
  Podflow/upload/update_upload.py,sha256=4B5HyWwfmc_4Z5ZS_Wt2VcL6sgQFvq3JEl0Qubh7TwE,3183
83
+ Podflow/upload/upload_print.py,sha256=KDMPxlWx7jqFVJuEwBFs5U6o3K5AbXBGxcACtXKDqdE,165
78
84
  Podflow/youtube/__init__.py,sha256=-bdMyuw-wxoz2miVkp284amS4Qg0k7VN0JPuGF-cXlM,46
79
- Podflow/youtube/build.py,sha256=9L5KbTyYFSnLAqh7rj7jfEiPOQiBs2WpBwVVxo1eMTI,11192
85
+ Podflow/youtube/build.py,sha256=yy-rPBGs7H4aUjgLNyKz2HE-mjyGTs3b0FL5-KPHPM0,11650
80
86
  Podflow/youtube/get.py,sha256=dFLyiHttygqdJltwC29jD_v8wwoLynE5NUdow_0wERI,16970
81
87
  Podflow/youtube/login.py,sha256=DlS_ZG4g6CKWqS5ojE4UwFJSCSZDsXbeuDVgHtQAa4A,1380
82
- podflow-20250221.dist-info/METADATA,sha256=h4fw0K0QDEtCMX1XjNFmiwrGzA9U6MzvNmQt95rBLo4,13801
83
- podflow-20250221.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
84
- podflow-20250221.dist-info/entry_points.txt,sha256=44nj8jJB7bo1JLNrKQZmwMGEA1OalrALJ0tF_G0yXLY,131
85
- podflow-20250221.dist-info/top_level.txt,sha256=KcvRCiz_DRWWc9i-PgpARvFB0J4CKmpZOZgPqOdG-Lk,8
86
- podflow-20250221.dist-info/RECORD,,
88
+ podflow-20250301.dist-info/METADATA,sha256=B9-vRpJMHUs2TDUY53PZ4PCI8F1Nml46ZjAedU5Jxq8,13801
89
+ podflow-20250301.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
90
+ podflow-20250301.dist-info/entry_points.txt,sha256=44nj8jJB7bo1JLNrKQZmwMGEA1OalrALJ0tF_G0yXLY,131
91
+ podflow-20250301.dist-info/top_level.txt,sha256=KcvRCiz_DRWWc9i-PgpARvFB0J4CKmpZOZgPqOdG-Lk,8
92
+ podflow-20250301.dist-info/RECORD,,