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.
Files changed (106) hide show
  1. {podflow-20250317 → podflow-20250323}/PKG-INFO +1 -1
  2. {podflow-20250317 → podflow-20250323}/Podflow/__init__.py +1 -0
  3. {podflow-20250317 → podflow-20250323}/Podflow/basic/file_save.py +2 -0
  4. {podflow-20250317 → podflow-20250323}/Podflow/download/dl_aideo_video.py +4 -1
  5. {podflow-20250317 → podflow-20250323}/Podflow/httpfs/app_bottle.py +139 -19
  6. podflow-20250323/Podflow/httpfs/browser.py +9 -0
  7. podflow-20250323/Podflow/httpfs/get_channelid.py +81 -0
  8. podflow-20250323/Podflow/httpfs/html.py +145 -0
  9. {podflow-20250317 → podflow-20250323}/Podflow/main_podcast.py +13 -2
  10. {podflow-20250317 → podflow-20250323}/Podflow/parse_arguments.py +6 -0
  11. podflow-20250323/Podflow/upload/build_hash.py +16 -0
  12. {podflow-20250317 → podflow-20250323}/Podflow/upload/linked_client.py +1 -4
  13. {podflow-20250317 → podflow-20250323}/Podflow/upload/linked_server.py +3 -2
  14. podflow-20250323/Podflow/upload/login.py +119 -0
  15. {podflow-20250317 → podflow-20250323}/podflow.egg-info/PKG-INFO +1 -1
  16. {podflow-20250317 → podflow-20250323}/podflow.egg-info/SOURCES.txt +4 -0
  17. {podflow-20250317 → podflow-20250323}/podflow.egg-info/requires.txt +1 -1
  18. {podflow-20250317 → podflow-20250323}/setup.py +2 -2
  19. podflow-20250317/Podflow/upload/login.py +0 -28
  20. {podflow-20250317 → podflow-20250323}/Podflow/basic/__init__.py +0 -0
  21. {podflow-20250317 → podflow-20250323}/Podflow/basic/folder_build.py +0 -0
  22. {podflow-20250317 → podflow-20250323}/Podflow/basic/get_duration.py +0 -0
  23. {podflow-20250317 → podflow-20250323}/Podflow/basic/get_file_list.py +0 -0
  24. {podflow-20250317 → podflow-20250323}/Podflow/basic/get_html_dict.py +0 -0
  25. {podflow-20250317 → podflow-20250323}/Podflow/basic/http_client.py +0 -0
  26. {podflow-20250317 → podflow-20250323}/Podflow/basic/list_merge_tidy.py +0 -0
  27. {podflow-20250317 → podflow-20250323}/Podflow/basic/qr_code.py +0 -0
  28. {podflow-20250317 → podflow-20250323}/Podflow/basic/split_dict.py +0 -0
  29. {podflow-20250317 → podflow-20250323}/Podflow/basic/time_format.py +0 -0
  30. {podflow-20250317 → podflow-20250323}/Podflow/basic/time_print.py +0 -0
  31. {podflow-20250317 → podflow-20250323}/Podflow/basic/time_stamp.py +0 -0
  32. {podflow-20250317 → podflow-20250323}/Podflow/basic/vary_replace.py +0 -0
  33. {podflow-20250317 → podflow-20250323}/Podflow/basic/write_log.py +0 -0
  34. {podflow-20250317 → podflow-20250323}/Podflow/bilibili/__init__.py +0 -0
  35. {podflow-20250317 → podflow-20250323}/Podflow/bilibili/build.py +0 -0
  36. {podflow-20250317 → podflow-20250323}/Podflow/bilibili/get.py +0 -0
  37. {podflow-20250317 → podflow-20250323}/Podflow/bilibili/login.py +0 -0
  38. {podflow-20250317 → podflow-20250323}/Podflow/config/__init__.py +0 -0
  39. {podflow-20250317 → podflow-20250323}/Podflow/config/build_original.py +0 -0
  40. {podflow-20250317 → podflow-20250323}/Podflow/config/channge_icon.py +0 -0
  41. {podflow-20250317 → podflow-20250323}/Podflow/config/correct_channelid.py +0 -0
  42. {podflow-20250317 → podflow-20250323}/Podflow/config/correct_config.py +0 -0
  43. {podflow-20250317 → podflow-20250323}/Podflow/config/get_channelid.py +0 -0
  44. {podflow-20250317 → podflow-20250323}/Podflow/config/get_channelid_id.py +0 -0
  45. {podflow-20250317 → podflow-20250323}/Podflow/config/get_config.py +0 -0
  46. {podflow-20250317 → podflow-20250323}/Podflow/download/__init__.py +0 -0
  47. {podflow-20250317 → podflow-20250323}/Podflow/download/convert_bytes.py +0 -0
  48. {podflow-20250317 → podflow-20250323}/Podflow/download/delete_part.py +0 -0
  49. {podflow-20250317 → podflow-20250323}/Podflow/download/show_progress.py +0 -0
  50. {podflow-20250317 → podflow-20250323}/Podflow/download/wait_animation.py +0 -0
  51. {podflow-20250317 → podflow-20250323}/Podflow/download/youtube_and_bilibili_download.py +0 -0
  52. {podflow-20250317 → podflow-20250323}/Podflow/download_and_build.py +0 -0
  53. {podflow-20250317 → podflow-20250323}/Podflow/ffmpeg_judge.py +0 -0
  54. {podflow-20250317 → podflow-20250323}/Podflow/httpfs/__init__.py +0 -0
  55. {podflow-20250317 → podflow-20250323}/Podflow/httpfs/port_judge.py +0 -0
  56. {podflow-20250317 → podflow-20250323}/Podflow/main.py +0 -0
  57. {podflow-20250317 → podflow-20250323}/Podflow/main_upload.py +0 -0
  58. {podflow-20250317 → podflow-20250323}/Podflow/makeup/__init__.py +0 -0
  59. {podflow-20250317 → podflow-20250323}/Podflow/makeup/del_makeup_format_fail.py +0 -0
  60. {podflow-20250317 → podflow-20250323}/Podflow/makeup/make_up_file.py +0 -0
  61. {podflow-20250317 → podflow-20250323}/Podflow/makeup/make_up_file_format_mod.py +0 -0
  62. {podflow-20250317 → podflow-20250323}/Podflow/makeup/make_up_file_mod.py +0 -0
  63. {podflow-20250317 → podflow-20250323}/Podflow/message/__init__.py +0 -0
  64. {podflow-20250317 → podflow-20250323}/Podflow/message/backup_zip_save.py +0 -0
  65. {podflow-20250317 → podflow-20250323}/Podflow/message/create_main_rss.py +0 -0
  66. {podflow-20250317 → podflow-20250323}/Podflow/message/display_qrcode_and_url.py +0 -0
  67. {podflow-20250317 → podflow-20250323}/Podflow/message/fail_message_initialize.py +0 -0
  68. {podflow-20250317 → podflow-20250323}/Podflow/message/format_time.py +0 -0
  69. {podflow-20250317 → podflow-20250323}/Podflow/message/get_media_name.py +0 -0
  70. {podflow-20250317 → podflow-20250323}/Podflow/message/get_original_rss.py +0 -0
  71. {podflow-20250317 → podflow-20250323}/Podflow/message/get_video_format.py +0 -0
  72. {podflow-20250317 → podflow-20250323}/Podflow/message/get_video_format_multithread.py +0 -0
  73. {podflow-20250317 → podflow-20250323}/Podflow/message/get_youtube_and_bilibili_video_format.py +0 -0
  74. {podflow-20250317 → podflow-20250323}/Podflow/message/media_format.py +0 -0
  75. {podflow-20250317 → podflow-20250323}/Podflow/message/original_rss_fail_print.py +0 -0
  76. {podflow-20250317 → podflow-20250323}/Podflow/message/rss_create_hash.py +0 -0
  77. {podflow-20250317 → podflow-20250323}/Podflow/message/save_rss.py +0 -0
  78. {podflow-20250317 → podflow-20250323}/Podflow/message/title_correction.py +0 -0
  79. {podflow-20250317 → podflow-20250323}/Podflow/message/update_information_display.py +0 -0
  80. {podflow-20250317 → podflow-20250323}/Podflow/message/update_youtube_bilibili_rss.py +0 -0
  81. {podflow-20250317 → podflow-20250323}/Podflow/message/want_retry.py +0 -0
  82. {podflow-20250317 → podflow-20250323}/Podflow/message/xml_item.py +0 -0
  83. {podflow-20250317 → podflow-20250323}/Podflow/message/xml_original_item.py +0 -0
  84. {podflow-20250317 → podflow-20250323}/Podflow/message/xml_rss.py +0 -0
  85. {podflow-20250317 → podflow-20250323}/Podflow/netscape/__init__.py +0 -0
  86. {podflow-20250317 → podflow-20250323}/Podflow/netscape/bulid_netscape.py +0 -0
  87. {podflow-20250317 → podflow-20250323}/Podflow/netscape/get_cookie_dict.py +0 -0
  88. {podflow-20250317 → podflow-20250323}/Podflow/remove/__init__.py +0 -0
  89. {podflow-20250317 → podflow-20250323}/Podflow/remove/remove_dir.py +0 -0
  90. {podflow-20250317 → podflow-20250323}/Podflow/remove/remove_file.py +0 -0
  91. {podflow-20250317 → podflow-20250323}/Podflow/repair/__init__.py +0 -0
  92. {podflow-20250317 → podflow-20250323}/Podflow/repair/reverse_log.py +0 -0
  93. {podflow-20250317 → podflow-20250323}/Podflow/upload/__init__.py +0 -0
  94. {podflow-20250317 → podflow-20250323}/Podflow/upload/add_upload.py +0 -0
  95. {podflow-20250317 → podflow-20250323}/Podflow/upload/get_upload_original.py +0 -0
  96. {podflow-20250317 → podflow-20250323}/Podflow/upload/time_key.py +0 -0
  97. {podflow-20250317 → podflow-20250323}/Podflow/upload/update_upload.py +0 -0
  98. {podflow-20250317 → podflow-20250323}/Podflow/youtube/__init__.py +0 -0
  99. {podflow-20250317 → podflow-20250323}/Podflow/youtube/build.py +0 -0
  100. {podflow-20250317 → podflow-20250323}/Podflow/youtube/get.py +0 -0
  101. {podflow-20250317 → podflow-20250323}/Podflow/youtube/login.py +0 -0
  102. {podflow-20250317 → podflow-20250323}/README.md +0 -0
  103. {podflow-20250317 → podflow-20250323}/podflow.egg-info/dependency_links.txt +0 -0
  104. {podflow-20250317 → podflow-20250323}/podflow.egg-info/entry_points.txt +0 -0
  105. {podflow-20250317 → podflow-20250323}/podflow.egg-info/top_level.txt +0 -0
  106. {podflow-20250317 → podflow-20250323}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250317
3
+ Version: 20250323
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
@@ -132,6 +132,7 @@ class Application_parse:
132
132
  self.period = 1
133
133
  self.file = ""
134
134
  self.httpfs = False
135
+ self.index = False
135
136
 
136
137
 
137
138
  # 创建 Application 类的实例
@@ -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 abs(id_duration - duration_video) <= 1: # 检查实际时长与预计时长是否一致
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 = request.environ.get("REMOTE_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 = 'application/json'
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":-1,
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":-3,
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":-2,
268
- "message":"Username Error",
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,9 @@
1
+ # Podflow/httpfs/browser.py
2
+ # coding: utf-8
3
+
4
+ import os
5
+
6
+
7
+ def open_url(url):
8
+ browser = os.getenv("BROWSER", "internalbrowser")
9
+ os.system(f"{browser} {url} &") # 在后台运行,避免阻塞
@@ -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()
@@ -86,7 +86,4 @@ def connect_upload_server():
86
86
  # 选择第一个找到的服务器
87
87
  server_ip, server_port = servers[0]
88
88
  time_print(f"正在连接到{server_ip}:{server_port}...", True)
89
- return {
90
- "ip": server_ip,
91
- "port": server_port
92
- }
89
+ return f"http://{server_ip}:{server_port}"
@@ -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
- time_print(f"来自{addr[0]}的发现请求\033[32m成功\033[0m")
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
- time_print(f"来自{addr[0]}的发现请求\033[31m失败\033[0m")
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250317
3
+ Version: 20250323
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  astral>=3.2
2
2
  bottle>=0.13.2
3
- yt-dlp>=2025.2.19
3
+ yt-dlp>=2025.3.21
4
4
  chardet>=5.2.0
5
5
  cherrypy>=18.10.0
6
6
  pyqrcode>=1.2.1
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
5
5
 
6
6
  setup(
7
7
  name="podflow",
8
- version="20250317",
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.2.19",
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