podflow 20250404.9__tar.gz → 20250406__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 (108) hide show
  1. {podflow-20250404.9 → podflow-20250406}/PKG-INFO +1 -1
  2. {podflow-20250404.9 → podflow-20250406}/podflow/basic/time_print.py +1 -1
  3. {podflow-20250404.9 → podflow-20250406}/podflow/download/dl_aideo_video.py +18 -21
  4. {podflow-20250404.9 → podflow-20250406}/podflow/httpfs/app_bottle.py +16 -1
  5. {podflow-20250404.9 → podflow-20250406}/podflow/main_podcast.py +22 -2
  6. {podflow-20250404.9 → podflow-20250406}/podflow/message/fail_message_initialize.py +2 -0
  7. {podflow-20250404.9 → podflow-20250406}/podflow/templates/index.html +75 -83
  8. {podflow-20250404.9 → podflow-20250406}/podflow.egg-info/PKG-INFO +1 -1
  9. {podflow-20250404.9 → podflow-20250406}/setup.py +1 -1
  10. {podflow-20250404.9 → podflow-20250406}/MANIFEST.in +0 -0
  11. {podflow-20250404.9 → podflow-20250406}/README.md +0 -0
  12. {podflow-20250404.9 → podflow-20250406}/podflow/__init__.py +0 -0
  13. {podflow-20250404.9 → podflow-20250406}/podflow/basic/__init__.py +0 -0
  14. {podflow-20250404.9 → podflow-20250406}/podflow/basic/file_save.py +0 -0
  15. {podflow-20250404.9 → podflow-20250406}/podflow/basic/folder_build.py +0 -0
  16. {podflow-20250404.9 → podflow-20250406}/podflow/basic/get_duration.py +0 -0
  17. {podflow-20250404.9 → podflow-20250406}/podflow/basic/get_file_list.py +0 -0
  18. {podflow-20250404.9 → podflow-20250406}/podflow/basic/get_html_dict.py +0 -0
  19. {podflow-20250404.9 → podflow-20250406}/podflow/basic/http_client.py +0 -0
  20. {podflow-20250404.9 → podflow-20250406}/podflow/basic/list_merge_tidy.py +0 -0
  21. {podflow-20250404.9 → podflow-20250406}/podflow/basic/qr_code.py +0 -0
  22. {podflow-20250404.9 → podflow-20250406}/podflow/basic/split_dict.py +0 -0
  23. {podflow-20250404.9 → podflow-20250406}/podflow/basic/time_format.py +0 -0
  24. {podflow-20250404.9 → podflow-20250406}/podflow/basic/time_stamp.py +0 -0
  25. {podflow-20250404.9 → podflow-20250406}/podflow/basic/vary_replace.py +0 -0
  26. {podflow-20250404.9 → podflow-20250406}/podflow/basic/write_log.py +0 -0
  27. {podflow-20250404.9 → podflow-20250406}/podflow/bilibili/__init__.py +0 -0
  28. {podflow-20250404.9 → podflow-20250406}/podflow/bilibili/build.py +0 -0
  29. {podflow-20250404.9 → podflow-20250406}/podflow/bilibili/get.py +0 -0
  30. {podflow-20250404.9 → podflow-20250406}/podflow/bilibili/login.py +0 -0
  31. {podflow-20250404.9 → podflow-20250406}/podflow/config/__init__.py +0 -0
  32. {podflow-20250404.9 → podflow-20250406}/podflow/config/build_original.py +0 -0
  33. {podflow-20250404.9 → podflow-20250406}/podflow/config/channge_icon.py +0 -0
  34. {podflow-20250404.9 → podflow-20250406}/podflow/config/correct_channelid.py +0 -0
  35. {podflow-20250404.9 → podflow-20250406}/podflow/config/correct_config.py +0 -0
  36. {podflow-20250404.9 → podflow-20250406}/podflow/config/get_channelid.py +0 -0
  37. {podflow-20250404.9 → podflow-20250406}/podflow/config/get_channelid_id.py +0 -0
  38. {podflow-20250404.9 → podflow-20250406}/podflow/config/get_config.py +0 -0
  39. {podflow-20250404.9 → podflow-20250406}/podflow/download/__init__.py +0 -0
  40. {podflow-20250404.9 → podflow-20250406}/podflow/download/convert_bytes.py +0 -0
  41. {podflow-20250404.9 → podflow-20250406}/podflow/download/delete_part.py +0 -0
  42. {podflow-20250404.9 → podflow-20250406}/podflow/download/show_progress.py +0 -0
  43. {podflow-20250404.9 → podflow-20250406}/podflow/download/wait_animation.py +0 -0
  44. {podflow-20250404.9 → podflow-20250406}/podflow/download/youtube_and_bilibili_download.py +0 -0
  45. {podflow-20250404.9 → podflow-20250406}/podflow/download_and_build.py +0 -0
  46. {podflow-20250404.9 → podflow-20250406}/podflow/ffmpeg_judge.py +0 -0
  47. {podflow-20250404.9 → podflow-20250406}/podflow/httpfs/__init__.py +0 -0
  48. {podflow-20250404.9 → podflow-20250406}/podflow/httpfs/browser.py +0 -0
  49. {podflow-20250404.9 → podflow-20250406}/podflow/httpfs/get_channelid.py +0 -0
  50. {podflow-20250404.9 → podflow-20250406}/podflow/httpfs/port_judge.py +0 -0
  51. {podflow-20250404.9 → podflow-20250406}/podflow/httpfs/to_html.py +0 -0
  52. {podflow-20250404.9 → podflow-20250406}/podflow/main.py +0 -0
  53. {podflow-20250404.9 → podflow-20250406}/podflow/main_upload.py +0 -0
  54. {podflow-20250404.9 → podflow-20250406}/podflow/makeup/__init__.py +0 -0
  55. {podflow-20250404.9 → podflow-20250406}/podflow/makeup/del_makeup_format_fail.py +0 -0
  56. {podflow-20250404.9 → podflow-20250406}/podflow/makeup/make_up_file.py +0 -0
  57. {podflow-20250404.9 → podflow-20250406}/podflow/makeup/make_up_file_format_mod.py +0 -0
  58. {podflow-20250404.9 → podflow-20250406}/podflow/makeup/make_up_file_mod.py +0 -0
  59. {podflow-20250404.9 → podflow-20250406}/podflow/message/__init__.py +0 -0
  60. {podflow-20250404.9 → podflow-20250406}/podflow/message/backup_zip_save.py +0 -0
  61. {podflow-20250404.9 → podflow-20250406}/podflow/message/create_main_rss.py +0 -0
  62. {podflow-20250404.9 → podflow-20250406}/podflow/message/display_qrcode_and_url.py +0 -0
  63. {podflow-20250404.9 → podflow-20250406}/podflow/message/format_time.py +0 -0
  64. {podflow-20250404.9 → podflow-20250406}/podflow/message/get_media_name.py +0 -0
  65. {podflow-20250404.9 → podflow-20250406}/podflow/message/get_original_rss.py +0 -0
  66. {podflow-20250404.9 → podflow-20250406}/podflow/message/get_video_format.py +0 -0
  67. {podflow-20250404.9 → podflow-20250406}/podflow/message/get_video_format_multithread.py +0 -0
  68. {podflow-20250404.9 → podflow-20250406}/podflow/message/get_youtube_and_bilibili_video_format.py +0 -0
  69. {podflow-20250404.9 → podflow-20250406}/podflow/message/media_format.py +0 -0
  70. {podflow-20250404.9 → podflow-20250406}/podflow/message/original_rss_fail_print.py +0 -0
  71. {podflow-20250404.9 → podflow-20250406}/podflow/message/rss_create_hash.py +0 -0
  72. {podflow-20250404.9 → podflow-20250406}/podflow/message/save_rss.py +0 -0
  73. {podflow-20250404.9 → podflow-20250406}/podflow/message/title_correction.py +0 -0
  74. {podflow-20250404.9 → podflow-20250406}/podflow/message/update_information_display.py +0 -0
  75. {podflow-20250404.9 → podflow-20250406}/podflow/message/update_youtube_bilibili_rss.py +0 -0
  76. {podflow-20250404.9 → podflow-20250406}/podflow/message/want_retry.py +0 -0
  77. {podflow-20250404.9 → podflow-20250406}/podflow/message/xml_item.py +0 -0
  78. {podflow-20250404.9 → podflow-20250406}/podflow/message/xml_original_item.py +0 -0
  79. {podflow-20250404.9 → podflow-20250406}/podflow/message/xml_rss.py +0 -0
  80. {podflow-20250404.9 → podflow-20250406}/podflow/netscape/__init__.py +0 -0
  81. {podflow-20250404.9 → podflow-20250406}/podflow/netscape/bulid_netscape.py +0 -0
  82. {podflow-20250404.9 → podflow-20250406}/podflow/netscape/get_cookie_dict.py +0 -0
  83. {podflow-20250404.9 → podflow-20250406}/podflow/parse_arguments.py +0 -0
  84. {podflow-20250404.9 → podflow-20250406}/podflow/remove/__init__.py +0 -0
  85. {podflow-20250404.9 → podflow-20250406}/podflow/remove/remove_dir.py +0 -0
  86. {podflow-20250404.9 → podflow-20250406}/podflow/remove/remove_file.py +0 -0
  87. {podflow-20250404.9 → podflow-20250406}/podflow/repair/__init__.py +0 -0
  88. {podflow-20250404.9 → podflow-20250406}/podflow/repair/reverse_log.py +0 -0
  89. {podflow-20250404.9 → podflow-20250406}/podflow/upload/__init__.py +0 -0
  90. {podflow-20250404.9 → podflow-20250406}/podflow/upload/add_upload.py +0 -0
  91. {podflow-20250404.9 → podflow-20250406}/podflow/upload/build_hash.py +0 -0
  92. {podflow-20250404.9 → podflow-20250406}/podflow/upload/get_upload_original.py +0 -0
  93. {podflow-20250404.9 → podflow-20250406}/podflow/upload/linked_client.py +0 -0
  94. {podflow-20250404.9 → podflow-20250406}/podflow/upload/linked_server.py +0 -0
  95. {podflow-20250404.9 → podflow-20250406}/podflow/upload/login.py +0 -0
  96. {podflow-20250404.9 → podflow-20250406}/podflow/upload/time_key.py +0 -0
  97. {podflow-20250404.9 → podflow-20250406}/podflow/upload/update_upload.py +0 -0
  98. {podflow-20250404.9 → podflow-20250406}/podflow/upload/upload_server.py +0 -0
  99. {podflow-20250404.9 → podflow-20250406}/podflow/youtube/__init__.py +0 -0
  100. {podflow-20250404.9 → podflow-20250406}/podflow/youtube/build.py +0 -0
  101. {podflow-20250404.9 → podflow-20250406}/podflow/youtube/get.py +0 -0
  102. {podflow-20250404.9 → podflow-20250406}/podflow/youtube/login.py +0 -0
  103. {podflow-20250404.9 → podflow-20250406}/podflow.egg-info/SOURCES.txt +0 -0
  104. {podflow-20250404.9 → podflow-20250406}/podflow.egg-info/dependency_links.txt +0 -0
  105. {podflow-20250404.9 → podflow-20250406}/podflow.egg-info/entry_points.txt +0 -0
  106. {podflow-20250404.9 → podflow-20250406}/podflow.egg-info/requires.txt +0 -0
  107. {podflow-20250404.9 → podflow-20250406}/podflow.egg-info/top_level.txt +0 -0
  108. {podflow-20250404.9 → podflow-20250406}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250404.9
3
+ Version: 20250406
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
@@ -35,5 +35,5 @@ def time_print(text, Top=False, NoEnter=False, Time=True, Url=""):
35
35
  gVar.index_message["enter"] = True
36
36
  if Url:
37
37
  gVar.index_message["podflow"].append(
38
- f'<a href="{Url}"><span class="ansi-url">{Url}</span></a>'
38
+ f'<a href="{Url}" target="_blank"><span class="ansi-url">{Url}</span></a>'
39
39
  )
@@ -2,6 +2,7 @@
2
2
  # coding: utf-8
3
3
 
4
4
  import os
5
+ import re
5
6
  import ffmpeg
6
7
  import yt_dlp
7
8
  from podflow import gVar
@@ -68,22 +69,22 @@ def download_video(
68
69
  ydl.download([f"{video_website}"]) # 下载指定视频链接的视频
69
70
  return None, None
70
71
  except Exception as download_video_error:
71
- fail_info = fail_message_initialize(download_video_error, video_url).replace("\n","")
72
+ fail_info = fail_message_initialize(download_video_error, video_url).replace(
73
+ "\n", ""
74
+ )
72
75
  remove_info = ""
73
- if fail_info in [
74
- "",
75
- "\033[31m请求拒绝\033[0m",
76
- "\033[31m数据不完整\033[0m",
77
- "\033[31m传输中断\033[0m",
78
- "\033[31m请求超时\033[0m",
79
- "\033[31m响应超时\033[0m",
80
- ] and "www.youtube.com" in video_website:
76
+ if (
77
+ fail_info == ""
78
+ or re.search(r"请求拒绝|数据不完整|传输中断|请求超时|响应超时", fail_info)
79
+ ) and "www.youtube.com" in video_website:
80
+ if fail_info != "":
81
+ remove_info = "|"
81
82
  if os.path.isfile(outtmpl):
82
83
  os.remove(outtmpl)
83
- remove_info = "|已删除失败文件"
84
+ remove_info += "已删除失败文件"
84
85
  elif os.path.isfile(outtmpl + ".part"):
85
86
  os.remove(outtmpl + ".part")
86
- remove_info = "|已删除部分失败文件"
87
+ remove_info += "已删除部分失败文件"
87
88
  write_log(
88
89
  f"{video_write_log} \033[31m下载失败\033[0m",
89
90
  None,
@@ -124,15 +125,12 @@ def dl_full_video(
124
125
  f"channel_audiovisual/{output_dir}/{video_url}{sesuffix}.{output_format}"
125
126
  ) # 获取已下载视频的实际时长
126
127
  if (
127
- duration_video is not None
128
- and abs(id_duration - duration_video) <= 1
128
+ duration_video is not None and abs(id_duration - duration_video) <= 1
129
129
  ): # 检查实际时长与预计时长是否一致
130
130
  return None, None
131
131
  if duration_video:
132
132
  fail_info = f"不完整({id_duration}|{duration_video}"
133
- write_log(
134
- f"{video_write_log} \033[31m下载失败\033[0m\n错误信息: {fail_info})"
135
- )
133
+ write_log(f"{video_write_log} \033[31m下载失败\033[0m\n错误信息: {fail_info})")
136
134
  os.remove(
137
135
  f"channel_audiovisual/{output_dir}/{video_url}{sesuffix}.{output_format}"
138
136
  ) # 删除不完整的视频
@@ -176,7 +174,9 @@ def dl_retry_video(
176
174
  cookies = "channel_data/yt_dlp_youtube.txt"
177
175
  video_id_count += 1
178
176
  if cookies:
179
- write_log(f"{video_write_log} 第\033[34m{video_id_count}\033[0m次重新下载 🍪")
177
+ write_log(
178
+ f"{video_write_log} 第\033[34m{video_id_count}\033[0m次重新下载 🍪"
179
+ )
180
180
  else:
181
181
  write_log(f"{video_write_log} 第\033[34m{video_id_count}\033[0m次重新下载")
182
182
  video_id_failed, _ = dl_full_video(
@@ -298,10 +298,7 @@ def dl_aideo_video(
298
298
  audio, video, output_file, vcodec="copy", acodec="copy"
299
299
  )
300
300
  ffmpeg.run(stream, quiet=True)
301
- time_print(
302
- " \033[32m合成成功\033[0m",
303
- Time=False
304
- )
301
+ time_print(" \033[32m合成成功\033[0m", Time=False)
305
302
  # 删除临时文件
306
303
  os.remove(f"channel_audiovisual/{output_dir}/{video_url}.part.mp4")
307
304
  os.remove(f"channel_audiovisual/{output_dir}/{video_url}.part.m4a")
@@ -3,6 +3,7 @@
3
3
 
4
4
  import os
5
5
  import hashlib
6
+ import mimetypes
6
7
  import pkg_resources
7
8
  from datetime import datetime
8
9
  import cherrypy
@@ -203,7 +204,21 @@ class bottle_app:
203
204
  # 如果文件存在, 返回文件
204
205
  if os.path.exists(filename): # 如果文件存在, 返回文件
205
206
  self.print_out(filename, 200)
206
- return static_file(filename, root=".")
207
+ # 设置正确的 Content-Type 头部
208
+ content_type, _ = mimetypes.guess_type(filename)
209
+ # 如果无法自动猜测出正确的 Content-Type,手动指定
210
+ if not content_type:
211
+ if filename.endswith(".xml"):
212
+ content_type = "application/xml"
213
+ elif filename.endswith(".m4a"):
214
+ content_type = "audio/mp4"
215
+ elif filename.endswith(".mp4"):
216
+ content_type = "video/mp4"
217
+ else:
218
+ content_type = "application/octet-stream" # 默认文件类型
219
+
220
+ # 返回静态文件并附加正确的 Content-Type
221
+ return static_file(filename, root=".", mimetype=content_type)
207
222
  else: # 如果文件不存在, 返回 404 错误
208
223
  self.print_out(filename, 404)
209
224
  abort(404, "File not found")
@@ -210,7 +210,27 @@ def main_podcast():
210
210
  update_upload()
211
211
  else:
212
212
  time_print("频道无更新内容")
213
-
213
+ # 清空变量内数据
214
+ gVar.channelid_youtube_ids_update.clear() # 需更新的YouTube频道字典
215
+ gVar.youtube_content_ytid_update.clear() # 需下载YouTube视频字典
216
+ gVar.youtube_content_ytid_backward_update.clear() # 向后更新需下载YouTube视频字典
217
+ gVar.channelid_youtube_rss.clear() # YouTube频道最新Rss Response字典
218
+ gVar.channelid_bilibili_ids_update.clear() # 需更新的哔哩哔哩频道字典
219
+ gVar.bilibili_content_bvid_update.clear() # 需下载哔哩哔哩视频字典
220
+ gVar.channelid_bilibili_rss.clear() # 哔哩哔哩频道最新Rss Response字典
221
+ gVar.bilibili_content_bvid_backward_update.clear() # 向后更新需下载哔哩哔哩视频字典
222
+ gVar.video_id_failed.clear() # YouTube&哔哩哔哩视频下载失败列表
223
+ gVar.video_id_update_format.clear() # YouTube&哔哩哔哩视频下载的详细信息字典
224
+ gVar.hash_rss_original = "" # 原始rss哈希值文本
225
+ gVar.xmls_original.clear() # 原始xml信息字典
226
+ gVar.xmls_original_fail.clear() # 未获取原始xml频道列表
227
+ gVar.youtube_xml_get_tree.clear() # YouTube频道简介和图标字典
228
+ gVar.all_youtube_content_ytid.clear() # 所有YouTube视频id字典
229
+ gVar.all_bilibili_content_bvid.clear() # 所有哔哩哔哩视频id字典
230
+ gVar.all_items.clear() # 更新后所有item明细列表
231
+ gVar.overall_rss = "" # 更新后的rss文本
232
+ gVar.make_up_file_format.clear() # 补全缺失媒体字典
233
+ gVar.make_up_file_format_fail.clear() # 补全缺失媒体失败字典
214
234
  # 将需要更新转为否
215
235
  gVar.update_generate_rss = False
216
236
  if parse.update_num != -1:
@@ -232,5 +252,5 @@ def main_podcast():
232
252
  # 延时
233
253
  time.sleep(parse.time_delay)
234
254
  # 关闭CherryPy服务器
235
- cherrypy.engine.exit()
236
255
  time_print("Podflow运行结束")
256
+ cherrypy.engine.exit()
@@ -169,4 +169,6 @@ def fail_message_initialize(message_error, video_url):
169
169
  elif mode == "regexp" and re.search(fail_info, fail_message):
170
170
  fail_message = re.sub(rf"{fail_info}", field, fail_message)
171
171
  break
172
+ if fail_message[0] == "\n":
173
+ fail_message = fail_message[1:]
172
174
  return fail_message
@@ -151,6 +151,9 @@
151
151
  overflow-x: auto; /* 当内容超出宽度时显示水平滚动条(可选) */
152
152
  overflow-y: auto;
153
153
  font-family: 'Courier New', Courier, monospace; /* 添加字体 */
154
+ word-break: break-word;
155
+ overflow-wrap: break-word;
156
+ white-space: pre-wrap; /* 保留换行并允许自动换行 */
154
157
  }
155
158
  textarea {
156
159
  height: 350px;
@@ -222,6 +225,11 @@
222
225
  nav {
223
226
  position: fixed;
224
227
  }
228
+ /* 确保页面宽度不会超出视口 */
229
+ body, html {
230
+ width: 100%;
231
+ overflow-x: hidden;
232
+ }
225
233
  }
226
234
  .message {
227
235
  padding: 0px;
@@ -276,7 +284,7 @@
276
284
  </section>
277
285
  </main>
278
286
  <script>
279
- (function() {
287
+ (function() {
280
288
  // 生成单个二维码的函数
281
289
  function generateQRCodeForNode(container) {
282
290
  const rootStyles = getComputedStyle(document.documentElement);
@@ -297,36 +305,10 @@
297
305
  container.textContent = 'URL 未提供';
298
306
  }
299
307
  }
300
-
301
- // 为所有存在的二维码容器生成二维码
302
- function generateQRCodes() {
303
- const containers = document.querySelectorAll('.qrcode-container');
304
- containers.forEach(generateQRCodeForNode);
305
- }
306
-
307
- // 利用 MutationObserver 动态监听 container 内新增的二维码容器
308
- function observeQRCodes(container) {
309
- const observer = new MutationObserver((mutationsList) => {
310
- mutationsList.forEach(mutation => {
311
- mutation.addedNodes.forEach(node => {
312
- if (node.nodeType === Node.ELEMENT_NODE) {
313
- if (node.classList.contains('qrcode-container')) {
314
- generateQRCodeForNode(node);
315
- } else {
316
- const childContainers = node.querySelectorAll('.qrcode-container');
317
- childContainers.forEach(generateQRCodeForNode);
318
- }
319
- }
320
- });
321
- });
322
- });
323
- observer.observe(container, { childList: true, subtree: true });
324
- }
325
-
308
+
326
309
  // 缓存常用节点
327
310
  const menu = document.getElementById('menu');
328
311
  const toggleMenuBtn = document.getElementById('toggleMenu');
329
- const mainArea = document.getElementById('main');
330
312
  const pages = {
331
313
  pageChannel: document.getElementById('pageChannel'),
332
314
  pageMessage: document.getElementById('pageMessage')
@@ -338,12 +320,9 @@
338
320
  const pasteBtn = document.getElementById('pasteBtn');
339
321
  const copyBtn = document.getElementById('copyBtn');
340
322
  const clearBtn = document.getElementById('clearBtn');
341
-
323
+
342
324
  let pollingTimer = null;
343
-
344
- // 监听 messageArea 内动态新增的二维码容器
345
- observeQRCodes(messageArea);
346
-
325
+
347
326
  // 菜单切换函数
348
327
  function toggleMenu() {
349
328
  menu.classList.toggle('hidden');
@@ -355,7 +334,7 @@
355
334
  toggleMenuBtn.textContent = '❮';
356
335
  }
357
336
  }
358
-
337
+
359
338
  // 根据页面标识显示对应面板
360
339
  function showPage(pageId) {
361
340
  Object.values(pages).forEach(page => page.style.display = 'none');
@@ -366,29 +345,26 @@
366
345
  toggleMenu();
367
346
  }
368
347
  pageId === 'pageMessage' ? startMessagePolling() : stopMessagePolling();
369
- if (pageId === 'pageMessage') {
370
- generateQRCodes(); // 页面切换时生成已有二维码
371
- }
372
348
  }
373
349
  }
374
-
350
+
375
351
  // 初始化默认页面
376
352
  showPage('pageMessage');
377
-
353
+
378
354
  let lastMessage = { podflow: [], http: [] };
379
355
  let userScrolled = false;
380
-
356
+
381
357
  // 监听滚动事件,检测用户是否手动滚动
382
358
  function onUserScroll(event) {
383
359
  const element = event.target;
384
- // 判断是否接近底部
360
+ // 判断是否接近底部,增加一定的容差值
385
361
  const nearBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 10;
386
362
  userScrolled = !nearBottom;
387
363
  }
388
-
364
+
389
365
  messageArea.addEventListener('scroll', onUserScroll);
390
366
  messageHttp.addEventListener('scroll', onUserScroll);
391
-
367
+
392
368
  // 轮询消息更新,更新 messageArea 与 messageHttp
393
369
  function getMessages() {
394
370
  fetch('message')
@@ -403,7 +379,7 @@
403
379
  })
404
380
  .catch(error => console.error('获取消息失败:', error));
405
381
  }
406
-
382
+
407
383
  function createMessageElement(message) {
408
384
  const p = document.createElement('p');
409
385
  p.innerHTML = message;
@@ -411,16 +387,34 @@
411
387
  return p;
412
388
  }
413
389
 
390
+ function processQRCodeContainers(p) {
391
+ const qrContainers = p.querySelectorAll('.qrcode-container');
392
+ qrContainers.forEach(container => {
393
+ // 判断当前容器是否有 data-url 属性,并且值不为空
394
+ if (container.dataset.url) {
395
+ generateQRCodeForNode(container);
396
+ } else {
397
+ // 如果没有 data-url 或值为空,可以执行其他操作,例如输出提示信息
398
+ console.log('容器中未提供 URL,跳过二维码生成:', container);
399
+ container.textContent = '未提供二维码 URL'; // 可选:在容器中显示提示
400
+ }
401
+ });
402
+ }
403
+
404
+ // 修改后的 appendMessages 函数:先生成消息节点内的二维码,再将节点追加或替换到消息容器中
414
405
  function appendMessages(container, newMessages, oldMessages) {
415
406
  // 判断当前是否在底部
416
- const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10;
417
-
418
- // 当两数组长度相等且有内容时,只比较最后一项
407
+ const wasAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10;
408
+ // 较为简单的情况:两数组长度相同且有内容,只比较最后一项
419
409
  if (newMessages.length === oldMessages.length && newMessages.length > 0) {
420
410
  const lastNewMessage = newMessages[newMessages.length - 1];
421
411
  const lastOldMessage = oldMessages[oldMessages.length - 1];
422
412
  if (lastNewMessage !== lastOldMessage) {
413
+ // 先创建消息节点
423
414
  const p = createMessageElement(lastNewMessage);
415
+ // 在插入前先处理二维码生成
416
+ processQRCodeContainers(p);
417
+ // 替换或追加到容器中
424
418
  const lastChild = container.lastElementChild;
425
419
  if (lastChild) {
426
420
  container.replaceChild(p, lastChild);
@@ -429,11 +423,13 @@
429
423
  }
430
424
  }
431
425
  } else {
432
- // 如果 newMessages 与 oldMessages 数量不一致
433
- // 先替换容器中最后一项为 newMessages 中对应位置的消息(若 oldMessages 存在数据)
426
+ // newMessages 与 oldMessages 数量不一致时
427
+ // 如果 oldMessages 存在数据,先替换容器中最后一项对应的消息
434
428
  if (oldMessages.length > 0) {
435
429
  const replaceIndex = oldMessages.length - 1;
436
430
  const p = createMessageElement(newMessages[replaceIndex]);
431
+ // 先生成二维码
432
+ processQRCodeContainers(p);
437
433
  const lastChild = container.lastElementChild;
438
434
  if (lastChild) {
439
435
  container.replaceChild(p, lastChild);
@@ -443,30 +439,33 @@
443
439
  }
444
440
  // 再追加从 oldMessages.length 开始的后续消息
445
441
  newMessages.slice(oldMessages.length).forEach(msg => {
446
- container.appendChild(createMessageElement(msg));
442
+ const p = createMessageElement(msg);
443
+ // 先生成二维码
444
+ processQRCodeContainers(p);
445
+ // 插入容器中
446
+ container.appendChild(p);
447
447
  });
448
448
  }
449
-
450
- // 如果用户没有主动滚动,则自动滚动到底部
451
- if (!userScrolled) {
449
+ // 如果之前在底部且用户没有主动滚动,则自动滚动到底部
450
+ if (wasAtBottom && !userScrolled) {
452
451
  container.scrollTop = container.scrollHeight;
453
452
  }
454
453
  }
455
-
454
+
456
455
  function startMessagePolling() {
457
456
  getMessages();
458
- pollingTimer = setInterval(getMessages, 250);
457
+ pollingTimer = setInterval(getMessages, 500);
459
458
  }
460
-
459
+
461
460
  function stopMessagePolling() {
462
461
  if (pollingTimer !== null) {
463
462
  clearInterval(pollingTimer);
464
463
  pollingTimer = null;
465
464
  }
466
465
  }
467
-
466
+
468
467
  startMessagePolling();
469
-
468
+
470
469
  // 表单异步提交(获取 Channel-ID)
471
470
  inputForm && inputForm.addEventListener('submit', function(event) {
472
471
  event.preventDefault();
@@ -476,19 +475,19 @@
476
475
  headers: { 'Content-Type': 'application/json' },
477
476
  body: JSON.stringify({ content })
478
477
  })
479
- .then(response => {
480
- if (!response.ok) {
481
- throw new Error('网络响应异常');
482
- }
483
- return response.json();
484
- })
485
- .then(data => inputOutput.value = data.response)
486
- .catch(error => {
487
- console.error('请求失败:', error);
488
- alert('请求失败,请稍后重试!');
489
- });
478
+ .then(response => {
479
+ if (!response.ok) {
480
+ throw new Error('网络响应异常');
481
+ }
482
+ return response.json();
483
+ })
484
+ .then(data => inputOutput.value = data.response)
485
+ .catch(error => {
486
+ console.error('请求失败:', error);
487
+ alert('请求失败,请稍后重试!');
488
+ });
490
489
  });
491
-
490
+
492
491
  // 粘贴功能
493
492
  pasteBtn.addEventListener('click', function() {
494
493
  if (navigator.clipboard && navigator.clipboard.readText) {
@@ -509,7 +508,7 @@
509
508
  }
510
509
  }
511
510
  });
512
-
511
+
513
512
  // 复制功能
514
513
  copyBtn.addEventListener('click', function() {
515
514
  if (navigator.clipboard && navigator.clipboard.writeText) {
@@ -527,12 +526,12 @@
527
526
  }
528
527
  }
529
528
  });
530
-
529
+
531
530
  // 清空输入框
532
531
  clearBtn.addEventListener('click', function() {
533
532
  inputOutput.value = '';
534
533
  });
535
-
534
+
536
535
  // 菜单项点击事件委托
537
536
  menu.addEventListener('click', function(event) {
538
537
  const target = event.target;
@@ -540,24 +539,17 @@
540
539
  showPage(target.dataset.page);
541
540
  }
542
541
  });
543
-
542
+
544
543
  // 菜单切换按钮事件绑定
545
544
  toggleMenuBtn.addEventListener('click', toggleMenu);
546
-
545
+
547
546
  // 针对手机端,初始化时隐藏菜单
548
547
  if (window.innerWidth <= 600) {
549
548
  menu.classList.add('hidden');
550
549
  toggleMenuBtn.style.left = '0px';
551
550
  toggleMenuBtn.textContent = '❯';
552
551
  }
553
-
554
- // 在页面加载完成后首次生成二维码(如果当前显示的是 Channel 页面)
555
- window.addEventListener('load', () => {
556
- if (pages.pageChannel.style.display === 'block') {
557
- generateQRCodes();
558
- }
559
- });
560
552
  })();
561
- </script>
553
+ </script>
562
554
  </body>
563
555
  </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250404.9
3
+ Version: 20250406
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
@@ -5,7 +5,7 @@ from setuptools import setup, find_packages
5
5
 
6
6
  setup(
7
7
  name="podflow",
8
- version="20250404.9",
8
+ version="20250406",
9
9
  author="gruel_zxz",
10
10
  author_email="zhuxizhouzxz@gmail.com",
11
11
  description="A podcast server that includes YouTube and BiliBili",
File without changes
File without changes
File without changes
File without changes