podflow 20250317__tar.gz → 20250323__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {podflow-20250317 → podflow-20250323}/PKG-INFO +1 -1
- {podflow-20250317 → podflow-20250323}/Podflow/__init__.py +1 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/file_save.py +2 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/dl_aideo_video.py +4 -1
- {podflow-20250317 → podflow-20250323}/Podflow/httpfs/app_bottle.py +139 -19
- podflow-20250323/Podflow/httpfs/browser.py +9 -0
- podflow-20250323/Podflow/httpfs/get_channelid.py +81 -0
- podflow-20250323/Podflow/httpfs/html.py +145 -0
- {podflow-20250317 → podflow-20250323}/Podflow/main_podcast.py +13 -2
- {podflow-20250317 → podflow-20250323}/Podflow/parse_arguments.py +6 -0
- podflow-20250323/Podflow/upload/build_hash.py +16 -0
- {podflow-20250317 → podflow-20250323}/Podflow/upload/linked_client.py +1 -4
- {podflow-20250317 → podflow-20250323}/Podflow/upload/linked_server.py +3 -2
- podflow-20250323/Podflow/upload/login.py +119 -0
- {podflow-20250317 → podflow-20250323}/podflow.egg-info/PKG-INFO +1 -1
- {podflow-20250317 → podflow-20250323}/podflow.egg-info/SOURCES.txt +4 -0
- {podflow-20250317 → podflow-20250323}/podflow.egg-info/requires.txt +1 -1
- {podflow-20250317 → podflow-20250323}/setup.py +2 -2
- podflow-20250317/Podflow/upload/login.py +0 -28
- {podflow-20250317 → podflow-20250323}/Podflow/basic/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/folder_build.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/get_duration.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/get_file_list.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/get_html_dict.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/http_client.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/list_merge_tidy.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/qr_code.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/split_dict.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/time_format.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/time_print.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/time_stamp.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/vary_replace.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/basic/write_log.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/bilibili/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/bilibili/build.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/bilibili/get.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/bilibili/login.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/build_original.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/channge_icon.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/correct_channelid.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/correct_config.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/get_channelid.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/get_channelid_id.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/config/get_config.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/convert_bytes.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/delete_part.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/show_progress.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/wait_animation.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download/youtube_and_bilibili_download.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/download_and_build.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/ffmpeg_judge.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/httpfs/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/httpfs/port_judge.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/main.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/main_upload.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/makeup/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/makeup/del_makeup_format_fail.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/makeup/make_up_file.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/makeup/make_up_file_format_mod.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/makeup/make_up_file_mod.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/backup_zip_save.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/create_main_rss.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/display_qrcode_and_url.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/fail_message_initialize.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/format_time.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/get_media_name.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/get_original_rss.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/get_video_format.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/get_video_format_multithread.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/get_youtube_and_bilibili_video_format.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/media_format.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/original_rss_fail_print.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/rss_create_hash.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/save_rss.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/title_correction.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/update_information_display.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/update_youtube_bilibili_rss.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/want_retry.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/xml_item.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/xml_original_item.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/message/xml_rss.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/netscape/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/netscape/bulid_netscape.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/netscape/get_cookie_dict.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/remove/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/remove/remove_dir.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/remove/remove_file.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/repair/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/repair/reverse_log.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/upload/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/upload/add_upload.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/upload/get_upload_original.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/upload/time_key.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/upload/update_upload.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/youtube/__init__.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/youtube/build.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/youtube/get.py +0 -0
- {podflow-20250317 → podflow-20250323}/Podflow/youtube/login.py +0 -0
- {podflow-20250317 → podflow-20250323}/README.md +0 -0
- {podflow-20250317 → podflow-20250323}/podflow.egg-info/dependency_links.txt +0 -0
- {podflow-20250317 → podflow-20250323}/podflow.egg-info/entry_points.txt +0 -0
- {podflow-20250317 → podflow-20250323}/podflow.egg-info/top_level.txt +0 -0
- {podflow-20250317 → podflow-20250323}/setup.cfg +0 -0
@@ -15,7 +15,9 @@ def file_save(content, file_name, folder=None):
|
|
15
15
|
file_path = os.path.join(os.getcwd(), file_name)
|
16
16
|
# 保存文件
|
17
17
|
with open(file_path, "w", encoding="utf-8") as file:
|
18
|
+
# 如果文件名中包含"."且文件类型为json,则将内容以json格式保存
|
18
19
|
if "." in file_name and file_name.split(".")[-1] == "json":
|
19
20
|
json.dump(content, file, ensure_ascii=False, indent=4)
|
20
21
|
else:
|
22
|
+
# 否则将内容以文本格式保存
|
21
23
|
file.write(content)
|
@@ -123,7 +123,10 @@ def dl_full_video(
|
|
123
123
|
duration_video = get_duration(
|
124
124
|
f"channel_audiovisual/{output_dir}/{video_url}{sesuffix}.{output_format}"
|
125
125
|
) # 获取已下载视频的实际时长
|
126
|
-
if
|
126
|
+
if (
|
127
|
+
duration_video is not None
|
128
|
+
and abs(id_duration - duration_video) <= 1
|
129
|
+
): # 检查实际时长与预计时长是否一致
|
127
130
|
return None, None
|
128
131
|
if duration_video:
|
129
132
|
fail_info = f"不完整({id_duration}|{duration_video}"
|
@@ -5,11 +5,15 @@ 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, response
|
8
|
+
from bottle import Bottle, abort, redirect, request, static_file, response, template
|
9
9
|
from Podflow import gVar
|
10
10
|
from Podflow.upload.login import create
|
11
|
+
from Podflow.httpfs.html import html_index
|
12
|
+
from Podflow.basic.file_save import file_save
|
11
13
|
from Podflow.basic.write_log import write_log
|
14
|
+
from Podflow.upload.build_hash import build_hash
|
12
15
|
from Podflow.upload.time_key import check_time_key
|
16
|
+
from Podflow.httpfs.get_channelid import get_channelid
|
13
17
|
|
14
18
|
|
15
19
|
class bottle_app:
|
@@ -31,8 +35,10 @@ class bottle_app:
|
|
31
35
|
if upload:
|
32
36
|
self.app_bottle.route("/newuser", callback=self.new_user)
|
33
37
|
self.app_bottle.route("/login", callback=self.login)
|
38
|
+
self.app_bottle.route("/upload", method="POST", callback=self.upload)
|
34
39
|
else:
|
35
40
|
# 设置其他路由,回调函数为serve_static
|
41
|
+
self.app_bottle.route("/index", method=["GET", "POST"], callback=self.index)
|
36
42
|
self.app_bottle.route("/<filename:path>", callback=self.serve_static)
|
37
43
|
|
38
44
|
# 设置日志文件名及写入判断
|
@@ -106,8 +112,7 @@ class bottle_app:
|
|
106
112
|
# 输出请求日志的函数
|
107
113
|
def print_out(self, filename, status):
|
108
114
|
client_ip = request.remote_addr
|
109
|
-
client_port
|
110
|
-
if client_port:
|
115
|
+
if client_port := request.environ.get("REMOTE_PORT"):
|
111
116
|
client_ip = f"{client_ip}:{client_port}"
|
112
117
|
if filename not in [
|
113
118
|
"favicon.ico",
|
@@ -120,7 +125,7 @@ class bottle_app:
|
|
120
125
|
gVar.channelid_youtube_ids_original
|
121
126
|
| gVar.channelid_bilibili_ids_original
|
122
127
|
| {"channel_audiovisual/": "", "channel_rss/": ""}
|
123
|
-
) # 合并多个频道 ID
|
128
|
+
) # 合并多个频道 ID
|
124
129
|
for (
|
125
130
|
bottle_channelid_key,
|
126
131
|
bottle_channelid_value,
|
@@ -139,7 +144,7 @@ class bottle_app:
|
|
139
144
|
def home(self):
|
140
145
|
VALID_TOKEN = gVar.config["token"] # 从配置中读取主验证 Token
|
141
146
|
token = request.query.get("token") # 获取请求中的 Token
|
142
|
-
|
147
|
+
|
143
148
|
if self.token_judgment(token, VALID_TOKEN): # 验证 Token
|
144
149
|
self.print_out("/", 303) # 如果验证成功, 输出 200 状态
|
145
150
|
return redirect("https://github.com/gruel-zxz/podflow") # 返回正常响应
|
@@ -156,7 +161,7 @@ class bottle_app:
|
|
156
161
|
Shutdown_VALID_TOKEN.encode()
|
157
162
|
).hexdigest() # 用于服务器关闭的验证 Token
|
158
163
|
token = request.query.get("token") # 获取请求中的 Token
|
159
|
-
|
164
|
+
|
160
165
|
if self.token_judgment(
|
161
166
|
token, Shutdown_VALID_TOKEN
|
162
167
|
): # 验证 Token 是否为关闭用的 Token
|
@@ -220,16 +225,17 @@ class bottle_app:
|
|
220
225
|
|
221
226
|
# 路由获取账号密码请求
|
222
227
|
def new_user(self):
|
228
|
+
# 生成一个用于上传非一次性项目的账户密码,该密码需要保存
|
223
229
|
seed = "We need to generate an account password for uploading non one-time items that need to be saved."
|
224
230
|
token = request.query.get("token") # 获取请求中的 Token
|
225
|
-
response.content_type =
|
226
|
-
|
231
|
+
response.content_type = "application/json"
|
232
|
+
|
227
233
|
if check_time_key(token, seed): # 验证 Token
|
228
234
|
username, password = create() # 生成用户名和密码
|
229
235
|
self.print_out("newuser", 200)
|
230
236
|
return {
|
231
|
-
"code":0,
|
232
|
-
"message":"Get New Username And Password Success",
|
237
|
+
"code": 0,
|
238
|
+
"message": "Get New Username And Password Success",
|
233
239
|
"data": {
|
234
240
|
"username": username,
|
235
241
|
"password": password,
|
@@ -238,35 +244,149 @@ class bottle_app:
|
|
238
244
|
else:
|
239
245
|
self.print_out("newuser", 401)
|
240
246
|
return {
|
241
|
-
"code"
|
242
|
-
"message":"Unauthorized: Invalid Token",
|
247
|
+
"code": -1,
|
248
|
+
"message": "Unauthorized: Invalid Token",
|
243
249
|
}
|
244
250
|
|
251
|
+
# 获取Channel-ID请求
|
252
|
+
def index(self):
|
253
|
+
if request.method == "POST":
|
254
|
+
user_input = request.forms.get("inputOutput") # 获取用户输入
|
255
|
+
processed_input = get_channelid(user_input)
|
256
|
+
self.print_out("channelid", 200)
|
257
|
+
return template(html_index, processed_input=processed_input) # 直接回显到输入框
|
258
|
+
else:
|
259
|
+
self.print_out("index", 200)
|
260
|
+
return template(html_index, processed_input="") # 默认输入框为空
|
261
|
+
|
245
262
|
# 路由处理登陆请求
|
246
263
|
def login(self):
|
264
|
+
# 获取上传的数据
|
247
265
|
upload_data = gVar.upload_data
|
266
|
+
# 获取用户名
|
248
267
|
username = request.query.get("username")
|
268
|
+
# 获取密码
|
249
269
|
password = request.query.get("password")
|
250
|
-
|
270
|
+
# 判断用户名是否在上传的数据中
|
251
271
|
if username in upload_data:
|
272
|
+
# 判断密码是否正确
|
252
273
|
if upload_data[username] == password:
|
274
|
+
# 打印登录成功
|
253
275
|
self.print_out("login", 200)
|
276
|
+
# 返回登录成功的信息
|
254
277
|
return {
|
255
|
-
"code":0,
|
256
|
-
"message":"Login Success",
|
278
|
+
"code": 0,
|
279
|
+
"message": "Login Success",
|
257
280
|
}
|
258
281
|
else:
|
282
|
+
# 打印密码错误
|
259
283
|
self.print_out("login", 401)
|
284
|
+
# 返回密码错误的信息
|
260
285
|
return {
|
261
|
-
"code"
|
262
|
-
"message":"Password Error",
|
286
|
+
"code": -3,
|
287
|
+
"message": "Password Error",
|
263
288
|
}
|
264
289
|
else:
|
290
|
+
# 打印用户名错误
|
291
|
+
self.print_out("login", 401)
|
292
|
+
# 返回用户名错误的信息
|
293
|
+
return {
|
294
|
+
"code": -2,
|
295
|
+
"message": "Username Error",
|
296
|
+
}
|
297
|
+
|
298
|
+
# 文件上传处理请求
|
299
|
+
def upload(self):
|
300
|
+
# 获取上传数据配置(存储用户名和密码)
|
301
|
+
upload_data = gVar.upload_data
|
302
|
+
# 从请求参数中获取用户名,默认为空字符串
|
303
|
+
username = request.query.get("username", "")
|
304
|
+
# 从请求参数中获取密码,默认为空字符串
|
305
|
+
password = request.query.get("password", "")
|
306
|
+
upload_hash = request.query.get("hash", "")
|
307
|
+
channelid = request.query.get("channel_id", "")
|
308
|
+
|
309
|
+
# 验证用户是否存在
|
310
|
+
if username not in upload_data:
|
311
|
+
self.print_out("login", 401)
|
312
|
+
return {
|
313
|
+
"code": -2,
|
314
|
+
"message": "Username Error",
|
315
|
+
}
|
316
|
+
# 验证密码是否正确
|
317
|
+
if upload_data[username] != password:
|
265
318
|
self.print_out("login", 401)
|
266
319
|
return {
|
267
|
-
"code"
|
268
|
-
"message":"
|
320
|
+
"code": -3,
|
321
|
+
"message": "Password Error",
|
322
|
+
}
|
323
|
+
# 从请求中获取上传的文件对象
|
324
|
+
upload_file = request.files.get("file")
|
325
|
+
# 检查是否有文件被上传
|
326
|
+
if not upload_file:
|
327
|
+
# 打印错误信息并返回错误码
|
328
|
+
self.print_out("upload", 404)
|
329
|
+
return {
|
330
|
+
"code": -4,
|
331
|
+
"message": "No File Provided",
|
332
|
+
}
|
333
|
+
# 判断文件是否完整
|
334
|
+
if upload_hash != build_hash(upload_file):
|
335
|
+
self.print_out("upload", 401)
|
336
|
+
return {
|
337
|
+
"code": -5,
|
338
|
+
"message": "Incomplete File",
|
339
|
+
}
|
340
|
+
if not channelid:
|
341
|
+
# 打印错误信息并返回错误码
|
342
|
+
self.print_out("upload", 404)
|
343
|
+
return {
|
344
|
+
"code": -6,
|
345
|
+
"message": "ChannelId Does Not Exist",
|
269
346
|
}
|
347
|
+
# 获取上传文件的原始文件名
|
348
|
+
filename = upload_file.filename
|
349
|
+
name = filename.split(".")[0]
|
350
|
+
suffix = filename.split(".")[1]
|
351
|
+
if suffix in ["mp4", "m4a"]:
|
352
|
+
self.print_out("upload", 404)
|
353
|
+
return {
|
354
|
+
"code": -6,
|
355
|
+
"message": "File Format Error",
|
356
|
+
}
|
357
|
+
address = f"/channel_audiovisual/{channelid}"
|
358
|
+
if os.path.exists(address):
|
359
|
+
file_list = os.listdir(address)
|
360
|
+
else:
|
361
|
+
file_list = []
|
362
|
+
num = 0
|
363
|
+
while True:
|
364
|
+
if num != 0:
|
365
|
+
filename = f"{name}.{num}.{suffix}"
|
366
|
+
if filename in file_list:
|
367
|
+
with open(f"{address}/{filename}", "rb") as original_file:
|
368
|
+
if upload_hash == build_hash(original_file):
|
369
|
+
self.print_out("upload", 200)
|
370
|
+
return {
|
371
|
+
"code": 1,
|
372
|
+
"message": "The Same File Exists",
|
373
|
+
"data": {
|
374
|
+
"filename": filename,
|
375
|
+
},
|
376
|
+
}
|
377
|
+
else:
|
378
|
+
num += 1
|
379
|
+
else:
|
380
|
+
file_save(upload_file, filename, address)
|
381
|
+
# 打印成功信息并返回成功码
|
382
|
+
self.print_out("upload", 200)
|
383
|
+
return {
|
384
|
+
"code": 0,
|
385
|
+
"message": "Upload Success",
|
386
|
+
"data": {
|
387
|
+
"filename": filename,
|
388
|
+
},
|
389
|
+
}
|
270
390
|
|
271
391
|
|
272
392
|
bottle_app_instance = bottle_app()
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# Podflow/httpfs/get_channelid.py
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
import re
|
5
|
+
from Podflow.basic.http_client import http_client
|
6
|
+
|
7
|
+
|
8
|
+
def get_response(
|
9
|
+
text,
|
10
|
+
name,
|
11
|
+
re_ucid,
|
12
|
+
mod_ucid,
|
13
|
+
re_title,
|
14
|
+
):
|
15
|
+
if response := http_client(
|
16
|
+
url=text,
|
17
|
+
name=name,
|
18
|
+
headers_possess=True
|
19
|
+
):
|
20
|
+
if mod_ucid == "text":
|
21
|
+
ucid = re.search(re_ucid, response.text)
|
22
|
+
elif mod_ucid == "url":
|
23
|
+
ucid = re.search(re_ucid, response.url)
|
24
|
+
else:
|
25
|
+
ucid = ""
|
26
|
+
title = re.search(re_title, response.text)
|
27
|
+
if ucid and title :
|
28
|
+
ucid = ucid.group(0)
|
29
|
+
title = title.group(0)
|
30
|
+
return f'''"{title}": "{ucid}"
|
31
|
+
|
32
|
+
"{title}": {{
|
33
|
+
"id": "{ucid}",
|
34
|
+
"quality": "1080",
|
35
|
+
"media": "mp4",
|
36
|
+
"InmainRSS": false,
|
37
|
+
"update_size": 2
|
38
|
+
}}'''
|
39
|
+
else:
|
40
|
+
return "Network Error"
|
41
|
+
else:
|
42
|
+
return "Network Error"
|
43
|
+
|
44
|
+
|
45
|
+
def get_channelid(text):
|
46
|
+
if text in [None, ""]:
|
47
|
+
return ""
|
48
|
+
url_pattern = re.compile(r'https?://[^\s<>"]+|www\.[^\s<>"]+')
|
49
|
+
text_match = url_pattern.search(text) # 使用 search() 而不是 match()
|
50
|
+
if text_match:
|
51
|
+
text = text_match.group(0)
|
52
|
+
if "youtube" in text:
|
53
|
+
return get_response(
|
54
|
+
text,
|
55
|
+
"youtube",
|
56
|
+
r"(?<=https://www.youtube.com/channel/)UC.{22}",
|
57
|
+
"text",
|
58
|
+
r'(?<=<link itemprop="name" content=").+?(?=")',
|
59
|
+
)
|
60
|
+
elif "bilibili" in text or "b23.tv" in text:
|
61
|
+
return get_response(
|
62
|
+
text,
|
63
|
+
"bilibili",
|
64
|
+
r'(?<=https://space\.bilibili\.com/)([0-9]+)',
|
65
|
+
"url",
|
66
|
+
r'(?<=<title>).+(?=的个人空间-)',
|
67
|
+
)
|
68
|
+
else:
|
69
|
+
text_match = re.search(r'(?<=UID:)([0-9]+)', text, re.IGNORECASE)
|
70
|
+
if text_match:
|
71
|
+
text = f"https://space.bilibili.com/{text_match.group(0)}"
|
72
|
+
return get_response(
|
73
|
+
text,
|
74
|
+
"bilibili",
|
75
|
+
r'(?<=https://space\.bilibili\.com/)([0-9]+)',
|
76
|
+
"url",
|
77
|
+
r'(?<=<title>).+(?=的个人空间-)',
|
78
|
+
)
|
79
|
+
else:
|
80
|
+
return "Network Error"
|
81
|
+
return "Network Error"
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# Podflow/httpfs/html.py
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
|
5
|
+
html_index = '''<!DOCTYPE html>
|
6
|
+
<html lang="zh">
|
7
|
+
<head>
|
8
|
+
<meta charset="UTF-8">
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
10
|
+
<title>输入输出示例</title>
|
11
|
+
<style>
|
12
|
+
/* 基本样式 */
|
13
|
+
body {
|
14
|
+
font-family: Arial, sans-serif;
|
15
|
+
text-align: center;
|
16
|
+
padding: 20px;
|
17
|
+
background-color: #f8f9fa;
|
18
|
+
}
|
19
|
+
|
20
|
+
h2 {
|
21
|
+
color: #333;
|
22
|
+
}
|
23
|
+
|
24
|
+
/* 响应式输入框 */
|
25
|
+
textarea {
|
26
|
+
width: 90%;
|
27
|
+
max-width: 600px;
|
28
|
+
height: 250px; /* 增加默认高度 */
|
29
|
+
font-size: 16px;
|
30
|
+
padding: 10px;
|
31
|
+
border: 1px solid #ccc;
|
32
|
+
border-radius: 8px;
|
33
|
+
resize: vertical; /* 允许手动调整高度 */
|
34
|
+
overflow-y: auto; /* 始终显示垂直滚动条(如需自动出现滚动条,可用 `overflow-y: auto;`) */
|
35
|
+
}
|
36
|
+
|
37
|
+
/* 按钮样式 */
|
38
|
+
.button-container {
|
39
|
+
margin-top: 10px;
|
40
|
+
}
|
41
|
+
|
42
|
+
button {
|
43
|
+
background-color: #007bff;
|
44
|
+
color: white;
|
45
|
+
border: none;
|
46
|
+
padding: 12px 18px;
|
47
|
+
font-size: 16px;
|
48
|
+
border-radius: 6px;
|
49
|
+
cursor: pointer;
|
50
|
+
transition: 0.3s;
|
51
|
+
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.2);
|
52
|
+
margin: 5px;
|
53
|
+
}
|
54
|
+
|
55
|
+
button:hover {
|
56
|
+
background-color: #0056b3;
|
57
|
+
}
|
58
|
+
|
59
|
+
/* 手机端优化 */
|
60
|
+
@media (max-width: 600px) {
|
61
|
+
textarea {
|
62
|
+
font-size: 18px;
|
63
|
+
height: 180px;
|
64
|
+
}
|
65
|
+
|
66
|
+
button {
|
67
|
+
width: 90%;
|
68
|
+
font-size: 18px;
|
69
|
+
padding: 14px;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
/* 提示信息 */
|
74
|
+
.hint {
|
75
|
+
font-size: 14px;
|
76
|
+
color: #666;
|
77
|
+
margin-top: 10px;
|
78
|
+
}
|
79
|
+
</style>
|
80
|
+
</head>
|
81
|
+
<body>
|
82
|
+
<h2>获取Channel-ID</h2>
|
83
|
+
<form id="inputForm" method="post">
|
84
|
+
<label for="inputOutput">请输入:</label><br>
|
85
|
+
<textarea name="inputOutput" id="inputOutput">{{ processed_input }}</textarea><br>
|
86
|
+
<div class="button-container">
|
87
|
+
<button type="button" onclick="pasteFromClipboard()">📋 粘贴</button>
|
88
|
+
<button type="submit">✅ 提交</button>
|
89
|
+
//<button type="button" onclick="copyText()">📄 拷贝</button>
|
90
|
+
<button type="button" onclick="clearInput()">🗑️ 清空</button>
|
91
|
+
</div>
|
92
|
+
<p class="hint">📌 如果粘贴按钮无效,请长按输入框手动粘贴。</p>
|
93
|
+
</form>
|
94
|
+
<script>
|
95
|
+
function pasteFromClipboard() {
|
96
|
+
let inputOutput = document.getElementById('inputOutput');
|
97
|
+
|
98
|
+
if (navigator.clipboard && navigator.clipboard.readText) {
|
99
|
+
navigator.clipboard.readText().then(text => {
|
100
|
+
inputOutput.value = text;
|
101
|
+
inputOutput.focus(); // 聚焦输入框
|
102
|
+
//alert("✅ 已成功粘贴!");
|
103
|
+
}).catch(err => {
|
104
|
+
console.warn("❌ 剪贴板读取失败:", err);
|
105
|
+
alert("❌ 无法读取剪贴板,请手动粘贴!");
|
106
|
+
});
|
107
|
+
} else {
|
108
|
+
// 兼容旧版浏览器
|
109
|
+
try {
|
110
|
+
inputOutput.focus();
|
111
|
+
document.execCommand('paste'); // 仅部分浏览器支持
|
112
|
+
//alert("✅ 尝试使用旧版粘贴方法!");
|
113
|
+
} catch (err) {
|
114
|
+
console.warn("❌ execCommand 粘贴失败:", err);
|
115
|
+
alert("❌ 您的浏览器不支持自动粘贴,请手动操作!");
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
function copyText() {
|
120
|
+
let inputOutput = document.getElementById('inputOutput');
|
121
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
122
|
+
navigator.clipboard.writeText(inputOutput.value).then(() => {
|
123
|
+
//alert("✅ 已拷贝到剪贴板!");
|
124
|
+
}).catch(err => {
|
125
|
+
console.warn("拷贝失败:", err);
|
126
|
+
alert("❌ 无法拷贝,请手动选择文本后按 Ctrl+C 复制!");
|
127
|
+
});
|
128
|
+
} else {
|
129
|
+
// 兼容旧版浏览器
|
130
|
+
try {
|
131
|
+
inputOutput.select();
|
132
|
+
document.execCommand('copy');
|
133
|
+
//alert("✅ 已拷贝到剪贴板!");
|
134
|
+
} catch (err) {
|
135
|
+
console.warn("execCommand 复制失败:", err);
|
136
|
+
alert("❌ 您的浏览器不支持拷贝,请手动操作!");
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
function clearInput() {
|
141
|
+
document.getElementById('inputOutput').value = '';
|
142
|
+
}
|
143
|
+
</script>
|
144
|
+
</body>
|
145
|
+
</html>'''
|
@@ -15,6 +15,7 @@ from Podflow.basic.split_dict import split_dict
|
|
15
15
|
from Podflow.basic.time_print import time_print
|
16
16
|
|
17
17
|
# 网络和 HTTP 模块
|
18
|
+
from Podflow.httpfs.browser import open_url
|
18
19
|
from Podflow.httpfs.port_judge import port_judge
|
19
20
|
from Podflow.httpfs.app_bottle import bottle_app_instance
|
20
21
|
|
@@ -53,6 +54,7 @@ from Podflow.remove.remove_dir import remove_dir
|
|
53
54
|
from Podflow.youtube.build import print_fail_youtube
|
54
55
|
|
55
56
|
# 长期媒体进行上传模块
|
57
|
+
from Podflow.upload.login import login_upload
|
56
58
|
from Podflow.upload.add_upload import add_upload
|
57
59
|
from Podflow.upload.update_upload import update_upload
|
58
60
|
from Podflow.upload.linked_client import connect_upload_server
|
@@ -93,6 +95,8 @@ def main_podcast():
|
|
93
95
|
)
|
94
96
|
cherrypy.engine.start() # 启动 CherryPy 服务器
|
95
97
|
time_print(f"HTTP服务器启动, 端口: \033[32m{port}\033[0m")
|
98
|
+
if parse.index:
|
99
|
+
open_url(f"{gVar.config['address']}/index")
|
96
100
|
if parse.httpfs: # HttpFS参数判断, 是否继续运行
|
97
101
|
cherrypy.engine.block() # 阻止程序退出, 保持HTTP服务运行
|
98
102
|
sys.exit(0)
|
@@ -116,10 +120,17 @@ def main_podcast():
|
|
116
120
|
gVar.xmls_original, gVar.hash_rss_original, gVar.xmls_original_fail = (
|
117
121
|
get_original_rss()
|
118
122
|
)
|
123
|
+
# 连接上传服务器
|
124
|
+
upload_url = connect_upload_server()
|
125
|
+
# 登陆上传服务器
|
126
|
+
if upload_url:
|
127
|
+
upload_json = login_upload(upload_url)
|
128
|
+
if upload_json:
|
129
|
+
gVar.config["upload"] = False
|
130
|
+
else:
|
131
|
+
gVar.config["upload"] = False
|
119
132
|
# 初始化原始上传信息
|
120
133
|
get_upload_original()
|
121
|
-
# 连接上传服务器
|
122
|
-
connect_upload_server()
|
123
134
|
# 更新Youtube和哔哩哔哩频道xml
|
124
135
|
update_youtube_bilibili_rss()
|
125
136
|
# 判断是否有更新内容
|
@@ -58,6 +58,11 @@ def parse_arguments():
|
|
58
58
|
metavar="URL",
|
59
59
|
help="Only shortcuts can be work",
|
60
60
|
)
|
61
|
+
parser.add_argument(
|
62
|
+
"--index",
|
63
|
+
action="store_true",
|
64
|
+
help="Only shortcuts can be work",
|
65
|
+
)
|
61
66
|
parser.add_argument(
|
62
67
|
"--httpfs",
|
63
68
|
action="store_true",
|
@@ -77,6 +82,7 @@ def parse_arguments():
|
|
77
82
|
parse.file = args.file
|
78
83
|
parse.httpfs = args.httpfs
|
79
84
|
parse.upload = args.upload
|
85
|
+
parse.index = args.index
|
80
86
|
# 检查并处理参数的状态
|
81
87
|
if args.times is not None:
|
82
88
|
parse.update_num = int(args.times[0])
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Podflow/upload/build_hash.py
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
import hashlib
|
5
|
+
|
6
|
+
|
7
|
+
# 定义一个函数,用于计算文件的哈希值
|
8
|
+
def build_hash(file):
|
9
|
+
# 获取hashlib模块中的sha256函数
|
10
|
+
hash_func = getattr(hashlib, "sha256")()
|
11
|
+
# 循环读取文件内容,每次读取4096字节
|
12
|
+
for chunk in iter(lambda: file.read(4096), b''):
|
13
|
+
# 更新哈希值
|
14
|
+
hash_func.update(chunk)
|
15
|
+
# 返回哈希值的十六进制表示
|
16
|
+
return hash_func.hexdigest()
|
@@ -2,6 +2,7 @@
|
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
4
|
import socket
|
5
|
+
from Podflow.basic.write_log import write_log
|
5
6
|
from Podflow.basic.time_print import time_print
|
6
7
|
from Podflow.httpfs.port_judge import port_judge
|
7
8
|
from Podflow.upload.time_key import check_time_key
|
@@ -45,11 +46,11 @@ def handle_discovery(broadcast_port, service_port):
|
|
45
46
|
# 检查消息是否包含时间关键字
|
46
47
|
if check_time_key(data ,"PODFLOW_DISCOVER_SERVER_REQUEST"):
|
47
48
|
# 打印接收到的发现请求成功
|
48
|
-
|
49
|
+
write_log(f"来自{addr[0]}的发现请求\033[32m成功\033[0m")
|
49
50
|
# 构造响应消息
|
50
51
|
response = f"PODFLOW_SERVER_INFO|{service_port}".encode()
|
51
52
|
# 发送响应消息
|
52
53
|
sock.sendto(response, addr)
|
53
54
|
else:
|
54
55
|
# 打印接收到的发现请求失败
|
55
|
-
|
56
|
+
write_log(f"来自{addr[0]}的发现请求\033[31m失败\033[0m")
|
@@ -0,0 +1,119 @@
|
|
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.upload.time_key import time_key
|
10
|
+
from Podflow.basic.file_save import file_save
|
11
|
+
from Podflow.basic.write_log import write_log
|
12
|
+
from Podflow.basic.time_print import time_print
|
13
|
+
from Podflow.basic.http_client import http_client
|
14
|
+
|
15
|
+
|
16
|
+
def get_login():
|
17
|
+
try:
|
18
|
+
with open("channel_data/upload_login.json", "r") as file:
|
19
|
+
upload_data = file.read()
|
20
|
+
gVar.upload_data = json.loads(upload_data)
|
21
|
+
except Exception:
|
22
|
+
file_save(gVar.upload_data, "upload_login.json", "channel_data")
|
23
|
+
|
24
|
+
|
25
|
+
def create():
|
26
|
+
new_username = str(uuid.uuid4())
|
27
|
+
while new_username in gVar.upload_data:
|
28
|
+
new_username = str(uuid.uuid4())
|
29
|
+
new_password = hashlib.sha256(os.urandom(64)).hexdigest()
|
30
|
+
gVar.upload_data[new_username] = new_password
|
31
|
+
file_save(gVar.upload_data, "upload_login.json", "channel_data")
|
32
|
+
return new_username, new_password
|
33
|
+
|
34
|
+
|
35
|
+
def get_account(url):
|
36
|
+
url = f"{url}/newuser"
|
37
|
+
token = time_key(
|
38
|
+
"We need to generate an account password for uploading non one-time items that need to be saved."
|
39
|
+
)
|
40
|
+
data = {"token": token}
|
41
|
+
if response := http_client(
|
42
|
+
url=url,
|
43
|
+
name="获取上传服务账号密码",
|
44
|
+
data=data,
|
45
|
+
):
|
46
|
+
return response.json()
|
47
|
+
|
48
|
+
|
49
|
+
def login(url, username, password):
|
50
|
+
url = f"{url}/login"
|
51
|
+
data = {
|
52
|
+
"username": username,
|
53
|
+
"password": password,
|
54
|
+
}
|
55
|
+
if response := http_client(
|
56
|
+
url=url,
|
57
|
+
name="登陆上传服务",
|
58
|
+
data=data,
|
59
|
+
):
|
60
|
+
return response.json()
|
61
|
+
|
62
|
+
|
63
|
+
def login_upload(url):
|
64
|
+
try:
|
65
|
+
# 尝试打开并读取 JSON 文件
|
66
|
+
with open("channel_data/upload_data.json", "r") as file:
|
67
|
+
upload_json = file.read()
|
68
|
+
upload_json = json.loads(
|
69
|
+
upload_json
|
70
|
+
)
|
71
|
+
except Exception:
|
72
|
+
upload_json = {}
|
73
|
+
if "username" not in upload_json:
|
74
|
+
write_log("上传服务账号密码不存在")
|
75
|
+
time_print("获取上传服务账号密码...")
|
76
|
+
account_data = get_account(url)
|
77
|
+
if "code" in account_data:
|
78
|
+
if account_data["code"] == 0:
|
79
|
+
write_log("账号密码获取\033[32m成功\033[0m")
|
80
|
+
username = account_data["data"]["username"]
|
81
|
+
password = account_data["data"]["password"]
|
82
|
+
upload_json = {
|
83
|
+
"username": username,
|
84
|
+
"password": password,
|
85
|
+
}
|
86
|
+
file_save(upload_json, "upload_data.json", "channel_data")
|
87
|
+
return upload_json
|
88
|
+
elif account_data["code"] == -1:
|
89
|
+
write_log("账号密码获取\033[31m失败\033[0m: 认证失败")
|
90
|
+
return
|
91
|
+
else:
|
92
|
+
write_log("账号密码获取\033[31m失败\033[0m")
|
93
|
+
return
|
94
|
+
else:
|
95
|
+
write_log("账号密码获取\033[31m失败\033[0m: 无法连接")
|
96
|
+
return
|
97
|
+
else:
|
98
|
+
username = upload_json["username"]
|
99
|
+
password = upload_json.get("password", "")
|
100
|
+
login_data = login(url, username, password)
|
101
|
+
if "code" in login_data:
|
102
|
+
if login_data["code"] == 0:
|
103
|
+
time_print("登陆上传服务\033[32m成功\033[0m")
|
104
|
+
return upload_json
|
105
|
+
elif login_data["code"] == -1:
|
106
|
+
write_log("登陆上传服务\033[31m失败\033[0m: 认证失败")
|
107
|
+
return
|
108
|
+
elif login_data["code"] == -2:
|
109
|
+
write_log("登陆上传服务\033[31m失败\033[0m: 账号错误")
|
110
|
+
return
|
111
|
+
elif login_data["code"] == -3:
|
112
|
+
write_log("登陆上传服务\033[31m失败\033[0m: 密码错误")
|
113
|
+
return
|
114
|
+
else:
|
115
|
+
write_log("登陆上传服务\033[31m失败\033[0m")
|
116
|
+
return
|
117
|
+
else:
|
118
|
+
write_log("登陆上传服务\033[31m失败\033[0m: 无法连接")
|
119
|
+
return
|
@@ -43,6 +43,9 @@ Podflow/download/wait_animation.py
|
|
43
43
|
Podflow/download/youtube_and_bilibili_download.py
|
44
44
|
Podflow/httpfs/__init__.py
|
45
45
|
Podflow/httpfs/app_bottle.py
|
46
|
+
Podflow/httpfs/browser.py
|
47
|
+
Podflow/httpfs/get_channelid.py
|
48
|
+
Podflow/httpfs/html.py
|
46
49
|
Podflow/httpfs/port_judge.py
|
47
50
|
Podflow/makeup/__init__.py
|
48
51
|
Podflow/makeup/del_makeup_format_fail.py
|
@@ -81,6 +84,7 @@ Podflow/repair/__init__.py
|
|
81
84
|
Podflow/repair/reverse_log.py
|
82
85
|
Podflow/upload/__init__.py
|
83
86
|
Podflow/upload/add_upload.py
|
87
|
+
Podflow/upload/build_hash.py
|
84
88
|
Podflow/upload/get_upload_original.py
|
85
89
|
Podflow/upload/linked_client.py
|
86
90
|
Podflow/upload/linked_server.py
|
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
|
|
5
5
|
|
6
6
|
setup(
|
7
7
|
name="podflow",
|
8
|
-
version="
|
8
|
+
version="20250323",
|
9
9
|
author="gruel_zxz",
|
10
10
|
author_email="zhuxizhouzxz@gmail.com",
|
11
11
|
description="A podcast server that includes YouTube and BiliBili",
|
@@ -30,7 +30,7 @@ setup(
|
|
30
30
|
install_requires=[
|
31
31
|
"astral>=3.2",
|
32
32
|
"bottle>=0.13.2",
|
33
|
-
"yt-dlp>=2025.
|
33
|
+
"yt-dlp>=2025.3.21",
|
34
34
|
"chardet>=5.2.0",
|
35
35
|
"cherrypy>=18.10.0",
|
36
36
|
"pyqrcode>=1.2.1",
|
@@ -1,28 +0,0 @@
|
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{podflow-20250317 → podflow-20250323}/Podflow/message/get_youtube_and_bilibili_video_format.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|