podflow 20250307__py3-none-any.whl → 20250315__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.
- Podflow/__init__.py +2 -1
- Podflow/basic/write_log.py +5 -11
- Podflow/httpfs/app_bottle.py +126 -68
- Podflow/httpfs/port_judge.py +7 -0
- Podflow/main.py +6 -13
- Podflow/main_podcast.py +12 -9
- Podflow/main_upload.py +37 -5
- Podflow/repair/__init__.py +2 -0
- Podflow/repair/reverse_log.py +31 -0
- Podflow/upload/linked_client.py +1 -1
- Podflow/upload/linked_server.py +4 -4
- Podflow/upload/login.py +28 -0
- {podflow-20250307.dist-info → podflow-20250315.dist-info}/METADATA +1 -1
- {podflow-20250307.dist-info → podflow-20250315.dist-info}/RECORD +17 -14
- {podflow-20250307.dist-info → podflow-20250315.dist-info}/WHEEL +0 -0
- {podflow-20250307.dist-info → podflow-20250315.dist-info}/entry_points.txt +0 -0
- {podflow-20250307.dist-info → podflow-20250315.dist-info}/top_level.txt +0 -0
Podflow/__init__.py
CHANGED
@@ -114,8 +114,9 @@ class Application_gVar:
|
|
114
114
|
self.overall_rss = "" # 更新后的rss文本
|
115
115
|
self.make_up_file_format = {} # 补全缺失媒体字典
|
116
116
|
self.make_up_file_format_fail = {} # 补全缺失媒体失败字典
|
117
|
-
|
117
|
+
|
118
118
|
self.upload_original = [] # 原始上传信息字典
|
119
|
+
self.upload_data = {} # 上传用户账号密码字典
|
119
120
|
|
120
121
|
self.shortcuts_url = {} # 输出至shortcut的url字典
|
121
122
|
|
Podflow/basic/write_log.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
import re
|
5
5
|
from datetime import datetime
|
6
|
-
from Podflow.basic.file_save import file_save
|
7
6
|
|
8
7
|
|
9
8
|
# 日志模块
|
@@ -19,23 +18,18 @@ def write_log(
|
|
19
18
|
current_time = datetime.now()
|
20
19
|
# 格式化输出, 只保留年月日时分秒
|
21
20
|
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
21
|
# 将新的日志内容添加在原有内容之前
|
29
22
|
log_in = re.sub(r"\033\[[0-9;]+m", "", log)
|
30
23
|
log_in = re.sub(r"\n", "", log_in)
|
31
|
-
only_log = re.sub(r"\033\[[0-9;]+m", "", str(only_log))
|
24
|
+
only_log = re.sub(r"\033\[[0-9;]+m", "", str(only_log)) if only_log else ""
|
32
25
|
new_contents = (
|
33
|
-
f"{formatted_time} {log_in}{only_log}\n
|
26
|
+
f"{formatted_time} {log_in}{only_log}\n"
|
34
27
|
if only_log
|
35
|
-
else f"{formatted_time} {log_in}\n
|
28
|
+
else f"{formatted_time} {log_in}\n"
|
36
29
|
)
|
37
30
|
# 将新的日志内容写入文件
|
38
|
-
|
31
|
+
with open(file_name, "a", encoding="utf-8") as file:
|
32
|
+
file.write(new_contents)
|
39
33
|
if display:
|
40
34
|
formatted_time_mini = current_time.strftime("%H:%M:%S")
|
41
35
|
log_print = f"{formatted_time_mini}|{log}" if time_display else f"{log}"
|
Podflow/httpfs/app_bottle.py
CHANGED
@@ -5,9 +5,11 @@ import os
|
|
5
5
|
import hashlib
|
6
6
|
from datetime import datetime
|
7
7
|
import cherrypy
|
8
|
-
from bottle import Bottle, abort, redirect, request, static_file
|
8
|
+
from bottle import Bottle, abort, redirect, request, static_file, response
|
9
9
|
from Podflow import gVar
|
10
|
+
from Podflow.upload.login import create
|
10
11
|
from Podflow.basic.write_log import write_log
|
12
|
+
from Podflow.upload.time_key import check_time_key
|
11
13
|
|
12
14
|
|
13
15
|
class bottle_app:
|
@@ -15,16 +17,32 @@ class bottle_app:
|
|
15
17
|
def __init__(self):
|
16
18
|
self.app_bottle = Bottle() # 创建 Bottle 应用
|
17
19
|
self.bottle_print = [] # 存储打印日志
|
18
|
-
self.setup_routes()
|
20
|
+
self.setup_routes() # 设置路由
|
21
|
+
self.logname = "httpfs.log" # 默认日志文件名
|
22
|
+
self.http_fs = False
|
19
23
|
|
20
|
-
def setup_routes(self):
|
24
|
+
def setup_routes(self, upload=False):
|
25
|
+
# 设置/favicon.ico路由,回调函数为favicon
|
26
|
+
self.app_bottle.route("/favicon.ico", callback=self.favicon)
|
27
|
+
# 设置根路由,回调函数为home
|
21
28
|
self.app_bottle.route("/", callback=self.home)
|
29
|
+
# 设置/shutdown路由,回调函数为shutdown
|
22
30
|
self.app_bottle.route("/shutdown", callback=self.shutdown)
|
23
|
-
|
24
|
-
|
31
|
+
if upload:
|
32
|
+
self.app_bottle.route("/newuser", callback=self.new_user)
|
33
|
+
self.app_bottle.route("/login", callback=self.login)
|
34
|
+
else:
|
35
|
+
# 设置其他路由,回调函数为serve_static
|
36
|
+
self.app_bottle.route("/<filename:path>", callback=self.serve_static)
|
37
|
+
|
38
|
+
# 设置日志文件名及写入判断
|
39
|
+
def set_logname(self, logname="httpfs.log", http_fs=False):
|
40
|
+
self.logname = logname
|
41
|
+
self.http_fs = http_fs
|
25
42
|
|
26
43
|
# 判断token是否正确的验证模块
|
27
44
|
def token_judgment(self, token, VALID_TOKEN="", filename="", foldername=""):
|
45
|
+
# 判断 token 是否有效
|
28
46
|
if foldername != "channel_audiovisual/":
|
29
47
|
# 对于其他文件夹, 采用常规的 Token 验证
|
30
48
|
return VALID_TOKEN == "" or token == VALID_TOKEN
|
@@ -57,14 +75,14 @@ class bottle_app:
|
|
57
75
|
status = f"{color}{status}\033[0m"
|
58
76
|
now_time = datetime.now().strftime("%H:%M:%S")
|
59
77
|
client_ip = f"\033[34m{client_ip}\033[0m"
|
60
|
-
if
|
78
|
+
if self.http_fs:
|
61
79
|
write_log(
|
62
80
|
f"{client_ip} {filename} {status}",
|
63
81
|
None,
|
64
82
|
False,
|
65
83
|
True,
|
66
84
|
None,
|
67
|
-
|
85
|
+
self.logname,
|
68
86
|
)
|
69
87
|
for suffix in suffixs:
|
70
88
|
filename = filename.replace(suffix, "")
|
@@ -72,35 +90,61 @@ class bottle_app:
|
|
72
90
|
|
73
91
|
# CherryPy 服务器打印模块
|
74
92
|
def cherry_print(self, flag_judgment=True):
|
93
|
+
# 如果flag_judgment为True,则将gVar.server_process_print_flag[0]设置为"keep"
|
75
94
|
if flag_judgment:
|
76
95
|
gVar.server_process_print_flag[0] = "keep"
|
96
|
+
# 如果gVar.server_process_print_flag[0]为"keep"且self.bottle_print不为空,则打印日志
|
77
97
|
if (
|
78
|
-
gVar.server_process_print_flag[0] == "keep"
|
79
|
-
and self.bottle_print
|
98
|
+
gVar.server_process_print_flag[0] == "keep" and self.bottle_print
|
80
99
|
): # 如果设置为保持输出, 则打印日志
|
100
|
+
# 遍历self.bottle_print中的每个元素,并打印
|
81
101
|
for info_print in self.bottle_print:
|
82
102
|
print(info_print)
|
103
|
+
# 清空self.bottle_print
|
83
104
|
self.bottle_print.clear()
|
84
105
|
|
106
|
+
# 输出请求日志的函数
|
107
|
+
def print_out(self, filename, status):
|
108
|
+
client_ip = request.remote_addr
|
109
|
+
client_port = request.environ.get("REMOTE_PORT")
|
110
|
+
if client_port:
|
111
|
+
client_ip = f"{client_ip}:{client_port}"
|
112
|
+
if filename not in [
|
113
|
+
"favicon.ico",
|
114
|
+
"/",
|
115
|
+
"shutdown",
|
116
|
+
"newuser",
|
117
|
+
"login",
|
118
|
+
]:
|
119
|
+
bottle_channelid = (
|
120
|
+
gVar.channelid_youtube_ids_original
|
121
|
+
| gVar.channelid_bilibili_ids_original
|
122
|
+
| {"channel_audiovisual/": "", "channel_rss/": ""}
|
123
|
+
) # 合并多个频道 ID
|
124
|
+
for (
|
125
|
+
bottle_channelid_key,
|
126
|
+
bottle_channelid_value,
|
127
|
+
) in bottle_channelid.items():
|
128
|
+
filename = filename.replace(
|
129
|
+
bottle_channelid_key, bottle_channelid_value
|
130
|
+
) # 替换频道路径
|
131
|
+
if status == 200 and request.headers.get(
|
132
|
+
"Range"
|
133
|
+
): # 如果是部分请求, 则返回 206 状态
|
134
|
+
status = 206
|
135
|
+
self.add_bottle_print(client_ip, filename, status) # 输出日志
|
136
|
+
self.cherry_print(False)
|
137
|
+
|
85
138
|
# 主路由处理根路径请求
|
86
139
|
def home(self):
|
87
140
|
VALID_TOKEN = gVar.config["token"] # 从配置中读取主验证 Token
|
88
|
-
|
89
|
-
# 输出请求日志的函数
|
90
|
-
def print_out(status):
|
91
|
-
client_ip = request.remote_addr # 获取客户端 IP 地址
|
92
|
-
client_port = request.environ.get("REMOTE_PORT") # 获取客户端端口
|
93
|
-
if client_port:
|
94
|
-
client_ip = f"{client_ip}:{client_port}" # 如果有端口信息, 则包括端口
|
95
|
-
self.add_bottle_print(client_ip, "/", status) # 添加日志信息
|
96
|
-
self.cherry_print(False)
|
97
|
-
|
98
141
|
token = request.query.get("token") # 获取请求中的 Token
|
142
|
+
|
99
143
|
if self.token_judgment(token, VALID_TOKEN): # 验证 Token
|
100
|
-
print_out(303) # 如果验证成功, 输出 200 状态
|
144
|
+
self.print_out("/", 303) # 如果验证成功, 输出 200 状态
|
101
145
|
return redirect("https://github.com/gruel-zxz/podflow") # 返回正常响应
|
102
146
|
else:
|
103
|
-
print_out(401) # 如果验证失败, 输出 401 状态
|
147
|
+
self.print_out("/", 401) # 如果验证失败, 输出 401 状态
|
104
148
|
abort(401, "Unauthorized: Invalid Token") # 返回未经授权错误
|
105
149
|
|
106
150
|
# 路由处理关闭服务器的请求
|
@@ -111,34 +155,21 @@ class bottle_app:
|
|
111
155
|
Shutdown_VALID_TOKEN = hashlib.sha256(
|
112
156
|
Shutdown_VALID_TOKEN.encode()
|
113
157
|
).hexdigest() # 用于服务器关闭的验证 Token
|
114
|
-
|
115
|
-
# 输出关闭请求日志的函数
|
116
|
-
def print_out(status):
|
117
|
-
client_ip = request.remote_addr
|
118
|
-
client_port = request.environ.get("REMOTE_PORT")
|
119
|
-
if client_port:
|
120
|
-
client_ip = f"{client_ip}:{client_port}"
|
121
|
-
self.add_bottle_print(client_ip, "shutdown", status)
|
122
|
-
self.cherry_print(False)
|
123
|
-
|
124
158
|
token = request.query.get("token") # 获取请求中的 Token
|
159
|
+
|
125
160
|
if self.token_judgment(
|
126
161
|
token, Shutdown_VALID_TOKEN
|
127
162
|
): # 验证 Token 是否为关闭用的 Token
|
128
|
-
print_out(200) # 如果验证成功, 输出 200 状态
|
163
|
+
self.print_out("shutdown", 200) # 如果验证成功, 输出 200 状态
|
129
164
|
cherrypy.engine.exit() # 使用 CherryPy 提供的停止功能来关闭服务器
|
130
165
|
return "Shutting down..." # 返回关机响应
|
131
166
|
else:
|
132
|
-
print_out(401) # 如果验证失败, 输出 401 状态
|
167
|
+
self.print_out("shutdown", 401) # 如果验证失败, 输出 401 状态
|
133
168
|
abort(401, "Unauthorized: Invalid Token") # 返回未经授权错误
|
134
169
|
|
135
170
|
# 路由处理 favicon 请求
|
136
171
|
def favicon(self):
|
137
|
-
|
138
|
-
if client_port := request.environ.get("REMOTE_PORT"):
|
139
|
-
client_ip = f"{client_ip}:{client_port}"
|
140
|
-
self.add_bottle_print(client_ip, "favicon.ico", 303) # 输出访问 favicon 的日志
|
141
|
-
self.cherry_print(False)
|
172
|
+
self.print_out("favicon.ico", 303) # 输出访问 favicon 的日志
|
142
173
|
return redirect(
|
143
174
|
"https://raw.githubusercontent.com/gruel-zxz/podflow/main/Podflow.png"
|
144
175
|
) # 重定向到图标 URL
|
@@ -152,51 +183,28 @@ class bottle_app:
|
|
152
183
|
bottle_filename.lower(): f"{bottle_filename}.xml", # 文件路径映射, 支持大小写不敏感的文件名
|
153
184
|
f"{bottle_filename.lower()}.xml": f"{bottle_filename}.xml", # 同上, 支持带 .xml 后缀
|
154
185
|
}
|
155
|
-
bottle_channelid = (
|
156
|
-
gVar.channelid_youtube_ids_original
|
157
|
-
| gVar.channelid_bilibili_ids_original
|
158
|
-
| {"channel_audiovisual/": "", "channel_rss/": ""}
|
159
|
-
) # 合并多个频道 ID
|
160
186
|
token = request.query.get("token") # 获取请求中的 Token
|
161
187
|
|
162
|
-
# 输出文件请求日志的函数
|
163
|
-
def print_out(filename, status):
|
164
|
-
client_ip = request.remote_addr
|
165
|
-
client_port = request.environ.get("REMOTE_PORT")
|
166
|
-
if client_port:
|
167
|
-
client_ip = f"{client_ip}:{client_port}"
|
168
|
-
for (
|
169
|
-
bottle_channelid_key,
|
170
|
-
bottle_channelid_value,
|
171
|
-
) in bottle_channelid.items():
|
172
|
-
filename = filename.replace(
|
173
|
-
bottle_channelid_key, bottle_channelid_value
|
174
|
-
) # 替换频道路径
|
175
|
-
if status == 200 and request.headers.get(
|
176
|
-
"Range"
|
177
|
-
): # 如果是部分请求, 则返回 206 状态
|
178
|
-
status = 206
|
179
|
-
self.add_bottle_print(client_ip, filename, status) # 输出日志
|
180
|
-
self.cherry_print(False)
|
181
|
-
|
182
188
|
# 文件是否存在检查的函数
|
183
189
|
def file_exist(token, VALID_TOKEN, filename, foldername=""):
|
190
|
+
# 验证 Token
|
184
191
|
if self.token_judgment(
|
185
192
|
token, VALID_TOKEN, filename, foldername
|
186
193
|
): # 验证 Token
|
194
|
+
# 如果文件存在, 返回文件
|
187
195
|
if os.path.exists(filename): # 如果文件存在, 返回文件
|
188
|
-
print_out(filename, 200)
|
196
|
+
self.print_out(filename, 200)
|
189
197
|
return static_file(filename, root=".")
|
190
198
|
else: # 如果文件不存在, 返回 404 错误
|
191
|
-
print_out(filename, 404)
|
199
|
+
self.print_out(filename, 404)
|
192
200
|
abort(404, "File not found")
|
193
201
|
else: # 如果 Token 验证失败, 返回 401 错误
|
194
|
-
print_out(filename, 401)
|
202
|
+
self.print_out(filename, 401)
|
195
203
|
abort(401, "Unauthorized: Invalid Token")
|
196
204
|
|
197
205
|
# 处理不同的文件路径
|
198
206
|
if filename in ["channel_audiovisual/", "channel_rss/"]:
|
199
|
-
print_out(filename, 404)
|
207
|
+
self.print_out(filename, 404)
|
200
208
|
abort(404, "File not found")
|
201
209
|
elif filename.startswith("channel_audiovisual/"):
|
202
210
|
return file_exist(token, VALID_TOKEN, filename, "channel_audiovisual/")
|
@@ -207,8 +215,58 @@ class bottle_app:
|
|
207
215
|
elif filename.lower() in shared_files:
|
208
216
|
return file_exist(token, VALID_TOKEN, shared_files[filename.lower()])
|
209
217
|
else:
|
210
|
-
print_out(filename, 404) # 如果文件路径未匹配, 返回 404 错误
|
218
|
+
self.print_out(filename, 404) # 如果文件路径未匹配, 返回 404 错误
|
211
219
|
abort(404, "File not found")
|
212
220
|
|
221
|
+
# 路由获取账号密码请求
|
222
|
+
def new_user(self):
|
223
|
+
seed = "We need to generate an account password for uploading non one-time items that need to be saved."
|
224
|
+
token = request.query.get("token") # 获取请求中的 Token
|
225
|
+
response.content_type = 'application/json'
|
226
|
+
|
227
|
+
if check_time_key(token, seed): # 验证 Token
|
228
|
+
username, password = create() # 生成用户名和密码
|
229
|
+
self.print_out("newuser", 200)
|
230
|
+
return {
|
231
|
+
"code":0,
|
232
|
+
"message":"Get New Username And Password Success",
|
233
|
+
"data": {
|
234
|
+
"username": username,
|
235
|
+
"password": password,
|
236
|
+
},
|
237
|
+
}
|
238
|
+
else:
|
239
|
+
self.print_out("newuser", 401)
|
240
|
+
return {
|
241
|
+
"code":-1,
|
242
|
+
"message":"Unauthorized: Invalid Token",
|
243
|
+
}
|
244
|
+
|
245
|
+
# 路由处理登陆请求
|
246
|
+
def login(self):
|
247
|
+
upload_data = gVar.upload_data
|
248
|
+
username = request.query.get("username")
|
249
|
+
password = request.query.get("password")
|
250
|
+
|
251
|
+
if username in upload_data:
|
252
|
+
if upload_data[username] == password:
|
253
|
+
self.print_out("login", 200)
|
254
|
+
return {
|
255
|
+
"code":0,
|
256
|
+
"message":"Login Success",
|
257
|
+
}
|
258
|
+
else:
|
259
|
+
self.print_out("login", 401)
|
260
|
+
return {
|
261
|
+
"code":-3,
|
262
|
+
"message":"Password Error",
|
263
|
+
}
|
264
|
+
else:
|
265
|
+
self.print_out("login", 401)
|
266
|
+
return {
|
267
|
+
"code":-2,
|
268
|
+
"message":"Username Error",
|
269
|
+
}
|
270
|
+
|
213
271
|
|
214
272
|
bottle_app_instance = bottle_app()
|
Podflow/httpfs/port_judge.py
CHANGED
@@ -4,11 +4,18 @@
|
|
4
4
|
import socket
|
5
5
|
|
6
6
|
|
7
|
+
# 定义一个函数,用于判断指定IP地址和端口号是否可用
|
7
8
|
def port_judge(hostip, port):
|
9
|
+
# 尝试创建一个socket对象
|
8
10
|
try:
|
11
|
+
# 创建一个socket对象,指定地址族为IPv4,类型为TCP
|
9
12
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
13
|
+
# 设置socket选项,允许地址重用
|
10
14
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
15
|
+
# 绑定socket对象到指定的IP地址和端口号
|
11
16
|
sock.bind((hostip, port))
|
17
|
+
# 如果绑定成功,返回True
|
12
18
|
return True
|
19
|
+
# 如果绑定失败,捕获OSError异常,并返回False
|
13
20
|
except OSError:
|
14
21
|
return False
|
Podflow/main.py
CHANGED
@@ -1,33 +1,26 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
import signal
|
4
|
-
import sys
|
5
|
-
from datetime import datetime
|
6
3
|
from importlib.metadata import version
|
7
4
|
from Podflow import parse
|
8
5
|
from Podflow.main_upload import main_upload
|
9
6
|
from Podflow.main_podcast import main_podcast
|
10
7
|
from Podflow.basic.time_print import time_print
|
8
|
+
from Podflow.repair.reverse_log import reverse_log
|
11
9
|
from Podflow.parse_arguments import parse_arguments
|
12
10
|
|
13
11
|
|
14
|
-
def signal_handler(sig, frame):
|
15
|
-
time_print("Podflow被中断, 正在退出...")
|
16
|
-
sys.exit(0)
|
17
|
-
|
18
|
-
|
19
12
|
def main():
|
20
|
-
# 注册SIGINT信号处理器(Ctrl+C)
|
21
|
-
signal.signal(signal.SIGINT, signal_handler)
|
22
|
-
|
23
13
|
# 获取传入的参数
|
24
14
|
parse_arguments()
|
25
15
|
# 开始运行
|
26
16
|
if parse.upload:
|
27
|
-
time_print("Podflow
|
17
|
+
time_print("Podflow|接收服务开始运行...")
|
18
|
+
reverse_log("upload")
|
28
19
|
main_upload()
|
29
20
|
else:
|
30
|
-
time_print(f"Podflow|{version('Podflow')}
|
21
|
+
time_print(f"Podflow|{version('Podflow')} 开始运行...")
|
22
|
+
reverse_log("Podflow")
|
23
|
+
reverse_log("httpfs")
|
31
24
|
main_podcast()
|
32
25
|
|
33
26
|
|
Podflow/main_podcast.py
CHANGED
@@ -6,13 +6,13 @@ import json
|
|
6
6
|
import time
|
7
7
|
import urllib
|
8
8
|
import subprocess
|
9
|
-
from datetime import datetime
|
10
9
|
|
11
10
|
import cherrypy
|
12
11
|
|
13
12
|
# 基本功能模块
|
14
13
|
from Podflow import gVar, parse
|
15
14
|
from Podflow.basic.split_dict import split_dict
|
15
|
+
from Podflow.basic.time_print import time_print
|
16
16
|
|
17
17
|
# 网络和 HTTP 模块
|
18
18
|
from Podflow.httpfs.port_judge import port_judge
|
@@ -68,6 +68,13 @@ def main_podcast():
|
|
68
68
|
port = gVar.config["port"]
|
69
69
|
hostip = "0.0.0.0"
|
70
70
|
if port_judge(hostip, port):
|
71
|
+
# 设置路由
|
72
|
+
bottle_app_instance.setup_routes(upload=False)
|
73
|
+
# 设置logname
|
74
|
+
bottle_app_instance.set_logname(
|
75
|
+
logname="httpfs.log",
|
76
|
+
http_fs=gVar.config["httpfs"],
|
77
|
+
)
|
71
78
|
# 启动 CherryPy 服务器
|
72
79
|
cherrypy.tree.graft(
|
73
80
|
bottle_app_instance.app_bottle
|
@@ -85,16 +92,12 @@ def main_podcast():
|
|
85
92
|
}
|
86
93
|
)
|
87
94
|
cherrypy.engine.start() # 启动 CherryPy 服务器
|
88
|
-
|
89
|
-
f"{datetime.now().strftime('%H:%M:%S')}|HTTP服务器启动, 端口: \033[32m{port}\033[0m"
|
90
|
-
)
|
95
|
+
time_print(f"HTTP服务器启动, 端口: \033[32m{port}\033[0m")
|
91
96
|
if parse.httpfs: # HttpFS参数判断, 是否继续运行
|
92
97
|
cherrypy.engine.block() # 阻止程序退出, 保持HTTP服务运行
|
93
98
|
sys.exit(0)
|
94
99
|
else:
|
95
|
-
|
96
|
-
f"{datetime.now().strftime('%H:%M:%S')}|HTTP服务器端口: \033[32m{port}\033[0m, \033[31m被占用\033[0m"
|
97
|
-
)
|
100
|
+
time_print(f"HTTP服务器端口: \033[32m{port}\033[0m, \033[31m被占用\033[0m")
|
98
101
|
if parse.httpfs:
|
99
102
|
sys.exit(0)
|
100
103
|
# 主流程
|
@@ -191,7 +194,7 @@ def main_podcast():
|
|
191
194
|
# 更新并保存上传列表
|
192
195
|
update_upload()
|
193
196
|
else:
|
194
|
-
|
197
|
+
time_print("频道无更新内容")
|
195
198
|
|
196
199
|
# 将需要更新转为否
|
197
200
|
gVar.update_generate_rss = False
|
@@ -215,4 +218,4 @@ def main_podcast():
|
|
215
218
|
time.sleep(parse.time_delay)
|
216
219
|
# 关闭CherryPy服务器
|
217
220
|
cherrypy.engine.exit()
|
218
|
-
|
221
|
+
time_print("Podflow运行结束")
|
Podflow/main_upload.py
CHANGED
@@ -2,24 +2,56 @@
|
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
4
|
import sys
|
5
|
-
import
|
5
|
+
import cherrypy
|
6
|
+
from Podflow.upload.login import get_login
|
6
7
|
from Podflow.basic.time_print import time_print
|
8
|
+
from Podflow.basic.folder_build import folder_build
|
9
|
+
from Podflow.httpfs.app_bottle import bottle_app_instance
|
7
10
|
from Podflow.upload.linked_server import handle_discovery, usable_port
|
8
11
|
|
9
12
|
|
10
13
|
def main_upload():
|
14
|
+
# 构建文件夹channel_audiovisual
|
15
|
+
folder_build("channel_audiovisual")
|
16
|
+
# 构建文件夹channel_data
|
17
|
+
folder_build("channel_data")
|
18
|
+
# 获取账号密码
|
19
|
+
get_login()
|
11
20
|
# 服务发现相关配置
|
12
21
|
broadcast_port = 37001 # 服务发现用端口
|
13
22
|
service_port = 5000 # 实际服务端口
|
23
|
+
hostip = "0.0.0.0"
|
14
24
|
|
15
25
|
broadcast_port = usable_port(broadcast_port, 37010)
|
16
26
|
service_port = usable_port(service_port, 5010)
|
27
|
+
|
17
28
|
if broadcast_port and service_port:
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
# 设置路由
|
30
|
+
bottle_app_instance.setup_routes(upload=True)
|
31
|
+
# 设置logname
|
32
|
+
bottle_app_instance.set_logname(
|
33
|
+
logname="upload.log",
|
34
|
+
http_fs=True,
|
35
|
+
)
|
36
|
+
# 启动 CherryPy 服务器
|
37
|
+
cherrypy.tree.graft(
|
38
|
+
bottle_app_instance.app_bottle
|
39
|
+
) # 将 Bottle 应用嵌入到 CherryPy 中
|
40
|
+
cherrypy.config.update(
|
41
|
+
{
|
42
|
+
"global": {
|
43
|
+
"tools.sessions.on": True, # 启用会话支持
|
44
|
+
"server.socket_host": hostip, # 监听所有 IP 地址
|
45
|
+
"server.socket_port": service_port, # 设置监听端口
|
46
|
+
"log.screen": False, # 禁用屏幕日志输出
|
47
|
+
"log.access_file": "", # 关闭访问日志
|
48
|
+
"log.error_file": "", # 关闭错误日志
|
49
|
+
}
|
50
|
+
}
|
21
51
|
)
|
22
|
-
|
52
|
+
cherrypy.engine.start() # 启动 CherryPy 服务器
|
53
|
+
time_print(f"上传服务已启动|端口: \033[32m{service_port}\033[0m")
|
54
|
+
# 服务发现
|
23
55
|
handle_discovery(broadcast_port, service_port)
|
24
56
|
else:
|
25
57
|
if not broadcast_port:
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Podflow/repair/reverse_log.py
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
import re
|
5
|
+
from Podflow.basic.time_print import time_print
|
6
|
+
|
7
|
+
|
8
|
+
def reverse_log(filename):
|
9
|
+
try:
|
10
|
+
with open(f"{filename}.log", "r", encoding="utf-8") as file:
|
11
|
+
lines = file.readlines()
|
12
|
+
except Exception:
|
13
|
+
return
|
14
|
+
num = 0
|
15
|
+
end_num = len(lines) - 1
|
16
|
+
|
17
|
+
def date_time(num):
|
18
|
+
pattern = r"^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$"
|
19
|
+
date_str = lines[num][:19]
|
20
|
+
return re.match(pattern, date_str)
|
21
|
+
|
22
|
+
while not date_time(num):
|
23
|
+
num += 1
|
24
|
+
while not date_time(end_num):
|
25
|
+
end_num -= 1
|
26
|
+
if end_num > num and lines[num][:19] > lines[end_num][:19]:
|
27
|
+
# 反转行的顺序
|
28
|
+
reversed_lines = lines[::-1]
|
29
|
+
with open(f"{filename}.log", "w", encoding="utf-8") as file:
|
30
|
+
file.writelines(reversed_lines)
|
31
|
+
time_print(f"{filename}.log反转成功")
|
Podflow/upload/linked_client.py
CHANGED
Podflow/upload/linked_server.py
CHANGED
@@ -33,9 +33,9 @@ def handle_discovery(broadcast_port, service_port):
|
|
33
33
|
# 设置套接字选项,允许广播
|
34
34
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
35
35
|
# 绑定套接字到广播端口
|
36
|
-
sock.bind(("", broadcast_port))
|
36
|
+
sock.bind(("0.0.0.0", broadcast_port))
|
37
37
|
# 打印发现服务已启动
|
38
|
-
time_print("
|
38
|
+
time_print(f"发现服务已启动|端口: \033[32m{broadcast_port}\033[0m")
|
39
39
|
# 无限循环,等待接收广播消息
|
40
40
|
while True:
|
41
41
|
# 接收广播消息
|
@@ -45,11 +45,11 @@ def handle_discovery(broadcast_port, service_port):
|
|
45
45
|
# 检查消息是否包含时间关键字
|
46
46
|
if check_time_key(data ,"PODFLOW_DISCOVER_SERVER_REQUEST"):
|
47
47
|
# 打印接收到的发现请求成功
|
48
|
-
time_print(f"来自{addr}的发现请求\033[32m成功\033[0m")
|
48
|
+
time_print(f"来自{addr[0]}的发现请求\033[32m成功\033[0m")
|
49
49
|
# 构造响应消息
|
50
50
|
response = f"PODFLOW_SERVER_INFO|{service_port}".encode()
|
51
51
|
# 发送响应消息
|
52
52
|
sock.sendto(response, addr)
|
53
53
|
else:
|
54
54
|
# 打印接收到的发现请求失败
|
55
|
-
time_print(f"来自{addr}的发现请求\033[31m失败\033[0m")
|
55
|
+
time_print(f"来自{addr[0]}的发现请求\033[31m失败\033[0m")
|
Podflow/upload/login.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Podflow/upload/login.py
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
import os
|
5
|
+
import json
|
6
|
+
import uuid
|
7
|
+
import hashlib
|
8
|
+
from Podflow import gVar
|
9
|
+
from Podflow.basic.file_save import file_save
|
10
|
+
|
11
|
+
|
12
|
+
def get_login():
|
13
|
+
try:
|
14
|
+
with open("channel_data/upload_login.json", "r") as file:
|
15
|
+
upload_data = file.read()
|
16
|
+
gVar.upload_data = json.loads(upload_data)
|
17
|
+
except Exception:
|
18
|
+
file_save(gVar.upload_data, "upload_login.json", "channel_data")
|
19
|
+
|
20
|
+
|
21
|
+
def create():
|
22
|
+
new_username = str(uuid.uuid4())
|
23
|
+
while new_username in gVar.upload_data:
|
24
|
+
new_username = str(uuid.uuid4())
|
25
|
+
new_password = hashlib.sha256(os.urandom(64)).hexdigest()
|
26
|
+
gVar.upload_data[new_username] = new_password
|
27
|
+
file_save(gVar.upload_data, "upload_login.json", "channel_data")
|
28
|
+
return new_username, new_password
|
@@ -1,9 +1,9 @@
|
|
1
|
-
Podflow/__init__.py,sha256
|
1
|
+
Podflow/__init__.py,sha256=-TdRoLAAMAPVNx2S2qSWR2vN-ZRD8wsEjZR4X1-7U1Q,6964
|
2
2
|
Podflow/download_and_build.py,sha256=LeLjLHnF5N9vdrroDsB-mT4UKY2OwPSL3APUAkhTuwc,671
|
3
3
|
Podflow/ffmpeg_judge.py,sha256=krttVs1PDot_0CDq5rmtUIpchiaiqtAkPYFQRLG6OQM,1275
|
4
|
-
Podflow/main.py,sha256=
|
5
|
-
Podflow/main_podcast.py,sha256=
|
6
|
-
Podflow/main_upload.py,sha256=
|
4
|
+
Podflow/main.py,sha256=tevbRqCDPuIrKuViI1Zm2lfdfjushYd7Nai6xZCGF4k,739
|
5
|
+
Podflow/main_podcast.py,sha256=6p-JrDfLVOYic_X_wsVrtjOZKH1SbMRWOEe7KO2vsZQ,8455
|
6
|
+
Podflow/main_upload.py,sha256=9FoIEO7kRwGwyyF-KKKEz5vRO9vE9_ab2VE6xTMroeM,2296
|
7
7
|
Podflow/parse_arguments.py,sha256=H2OXiOIHUuEUKOD0fk3zt_m1VsorGkL0eTcvx4N_C3Y,2441
|
8
8
|
Podflow/basic/__init__.py,sha256=SyFA_F_0-zn1VejCL83IPeZx1k2HCiqNY-0l0bp1N88,44
|
9
9
|
Podflow/basic/file_save.py,sha256=mstWM6njYtCpq2sa28JD_neClvVLcjJOrGlCXBjUnZs,696
|
@@ -19,7 +19,7 @@ Podflow/basic/time_format.py,sha256=Rddq-5wQgo6IrgiMlcMgDA52vQnMvWZvZC8g1Bj3BaQ,
|
|
19
19
|
Podflow/basic/time_print.py,sha256=2M22bcHRNEkaTZ1lvbHEILUYXEQImJJmOzHa9kamDR8,323
|
20
20
|
Podflow/basic/time_stamp.py,sha256=vf0p6FIK2-ZN2p2sotbpf4dewQMy-Vior-aREYfT0Zc,1888
|
21
21
|
Podflow/basic/vary_replace.py,sha256=aecFDuAOvQjTpgtoSW5uDu8Tx_YTs4bCFF9imVFkfVY,211
|
22
|
-
Podflow/basic/write_log.py,sha256=
|
22
|
+
Podflow/basic/write_log.py,sha256=3S7afoJvVHAGRa8SV9y71YL2_P_QvQ4QXn6LQUTTmlM,1169
|
23
23
|
Podflow/bilibili/__init__.py,sha256=ekvpCzR4bg6ar73Gq0YWQcPXND4TEqP46pb9iLyke9g,47
|
24
24
|
Podflow/bilibili/build.py,sha256=Oeh6Cqb61F5zQWe8ohDP0QKwykAa4_I2fa-Yl6ZxkCw,7957
|
25
25
|
Podflow/bilibili/get.py,sha256=PXPh_H8B_zP9dhqe6sA_QCeJcdnriBCpbF7mPq46xMw,20337
|
@@ -40,8 +40,8 @@ Podflow/download/show_progress.py,sha256=7oGzhj_frO2A1o5JeGuHn7bHtma5oPpD_IWcctI
|
|
40
40
|
Podflow/download/wait_animation.py,sha256=E2V3cm-10e5Iif40oU722OfzDe7YiLMbDqsjZ6dshBE,1056
|
41
41
|
Podflow/download/youtube_and_bilibili_download.py,sha256=0uuEO2ybyLlg9uOButjqFvmIkCfq6xiQ7QLV2_u4c_M,1294
|
42
42
|
Podflow/httpfs/__init__.py,sha256=agnAtd2Xe0qfOrElKgoBattAVqUBdj79wU2e2UOpcJM,45
|
43
|
-
Podflow/httpfs/app_bottle.py,sha256=
|
44
|
-
Podflow/httpfs/port_judge.py,sha256=
|
43
|
+
Podflow/httpfs/app_bottle.py,sha256=ZkCRXYrBJFUEatboUEB6Og35JoguWGnnuxImFLflsVA,11424
|
44
|
+
Podflow/httpfs/port_judge.py,sha256=RrBTxD6lXHbkfiDMu69-EUv0kbflfu1HpPfpB5Wz3MU,764
|
45
45
|
Podflow/makeup/__init__.py,sha256=HaBchKbUjRqqXSGCkMfEqLOyx3tlqB2htXvTDs59owI,45
|
46
46
|
Podflow/makeup/del_makeup_format_fail.py,sha256=rUSJD5lXMBM5pCde1amrtF6s93MMHp66-NHCXZy56AI,642
|
47
47
|
Podflow/makeup/make_up_file.py,sha256=6r5Mf_zK9snLS2hByD-HyKyuKWTccqqDJXh39GKhq28,2222
|
@@ -75,19 +75,22 @@ Podflow/netscape/get_cookie_dict.py,sha256=ptT2H8ZwFseK8EmpH0jn-lhzh-KCciOOPwpAJ
|
|
75
75
|
Podflow/remove/__init__.py,sha256=kns-jfTXH8lXh9OQ5E5-llsrAPlET5rl6RYpjoZKav8,45
|
76
76
|
Podflow/remove/remove_dir.py,sha256=tah3LCD0bCcf5dDg3NrHuseaje3-31C5NLNupMg15TU,1099
|
77
77
|
Podflow/remove/remove_file.py,sha256=ACO2iK8RAZKGJ08uABbzGwu5-B0RLklQ-EOOkSuPFiU,982
|
78
|
+
Podflow/repair/__init__.py,sha256=sfrjrUGdBuCri8x51oassbkHJMDqmEp0MMZHodMxYNM,45
|
79
|
+
Podflow/repair/reverse_log.py,sha256=nGVM4DOcnPz-VXJG6aHoKc1E461edYlaYoQYx7i6atQ,957
|
78
80
|
Podflow/upload/__init__.py,sha256=9uOIL8vr_vP_XYrhfZ4IDZmfSGyCf0_MpLOc-KnbpSY,45
|
79
81
|
Podflow/upload/add_upload.py,sha256=8kxHDLFa225WFze3oTPNtOWbcECF--bQXuALfsRJLoo,1432
|
80
82
|
Podflow/upload/get_upload_original.py,sha256=h2aoutZ-po40N8auRDbKvse14261Y5H7vHgfAN23vV0,4358
|
81
|
-
Podflow/upload/linked_client.py,sha256=
|
82
|
-
Podflow/upload/linked_server.py,sha256=
|
83
|
+
Podflow/upload/linked_client.py,sha256=ODaS95VNKcAUpfcnMxZBM4nHlZ6S9jBDFpDEmapzT5I,3242
|
84
|
+
Podflow/upload/linked_server.py,sha256=Tf3ANSryGuqjHsH1EqCrCA_pxo_dDuoYfCBzSgVVTrI,2264
|
85
|
+
Podflow/upload/login.py,sha256=ovQKdtrddsoea_4s-SpnMHTVstH1pWoo0dFx1dpU_sU,798
|
83
86
|
Podflow/upload/time_key.py,sha256=aR6xVe9x7ry8dKSHKoFLSUIfor2CoCuv8N9NRmsnUVo,967
|
84
87
|
Podflow/upload/update_upload.py,sha256=4B5HyWwfmc_4Z5ZS_Wt2VcL6sgQFvq3JEl0Qubh7TwE,3183
|
85
88
|
Podflow/youtube/__init__.py,sha256=-bdMyuw-wxoz2miVkp284amS4Qg0k7VN0JPuGF-cXlM,46
|
86
89
|
Podflow/youtube/build.py,sha256=o6gld4qMph7UKq9pdO2E4dmtOA8brCK4sa_-vKEtYMM,12006
|
87
90
|
Podflow/youtube/get.py,sha256=dFLyiHttygqdJltwC29jD_v8wwoLynE5NUdow_0wERI,16970
|
88
91
|
Podflow/youtube/login.py,sha256=DlS_ZG4g6CKWqS5ojE4UwFJSCSZDsXbeuDVgHtQAa4A,1380
|
89
|
-
podflow-
|
90
|
-
podflow-
|
91
|
-
podflow-
|
92
|
-
podflow-
|
93
|
-
podflow-
|
92
|
+
podflow-20250315.dist-info/METADATA,sha256=sWLPHwYRGI7rcDccvaXhU_KSsgq30adtNg2lgiN_Yjo,14030
|
93
|
+
podflow-20250315.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
94
|
+
podflow-20250315.dist-info/entry_points.txt,sha256=44nj8jJB7bo1JLNrKQZmwMGEA1OalrALJ0tF_G0yXLY,131
|
95
|
+
podflow-20250315.dist-info/top_level.txt,sha256=KcvRCiz_DRWWc9i-PgpARvFB0J4CKmpZOZgPqOdG-Lk,8
|
96
|
+
podflow-20250315.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|