podflow 20250404.9__tar.gz → 20250405__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-20250405}/PKG-INFO +1 -1
  2. {podflow-20250404.9 → podflow-20250405}/podflow/basic/time_print.py +1 -1
  3. {podflow-20250404.9 → podflow-20250405}/podflow/download/dl_aideo_video.py +18 -21
  4. {podflow-20250404.9 → podflow-20250405}/podflow/httpfs/app_bottle.py +16 -1
  5. {podflow-20250404.9 → podflow-20250405}/podflow/main_podcast.py +22 -2
  6. {podflow-20250404.9 → podflow-20250405}/podflow/message/fail_message_initialize.py +2 -0
  7. {podflow-20250404.9 → podflow-20250405}/podflow/templates/index.html +24 -7
  8. {podflow-20250404.9 → podflow-20250405}/podflow.egg-info/PKG-INFO +1 -1
  9. {podflow-20250404.9 → podflow-20250405}/setup.py +1 -1
  10. {podflow-20250404.9 → podflow-20250405}/MANIFEST.in +0 -0
  11. {podflow-20250404.9 → podflow-20250405}/README.md +0 -0
  12. {podflow-20250404.9 → podflow-20250405}/podflow/__init__.py +0 -0
  13. {podflow-20250404.9 → podflow-20250405}/podflow/basic/__init__.py +0 -0
  14. {podflow-20250404.9 → podflow-20250405}/podflow/basic/file_save.py +0 -0
  15. {podflow-20250404.9 → podflow-20250405}/podflow/basic/folder_build.py +0 -0
  16. {podflow-20250404.9 → podflow-20250405}/podflow/basic/get_duration.py +0 -0
  17. {podflow-20250404.9 → podflow-20250405}/podflow/basic/get_file_list.py +0 -0
  18. {podflow-20250404.9 → podflow-20250405}/podflow/basic/get_html_dict.py +0 -0
  19. {podflow-20250404.9 → podflow-20250405}/podflow/basic/http_client.py +0 -0
  20. {podflow-20250404.9 → podflow-20250405}/podflow/basic/list_merge_tidy.py +0 -0
  21. {podflow-20250404.9 → podflow-20250405}/podflow/basic/qr_code.py +0 -0
  22. {podflow-20250404.9 → podflow-20250405}/podflow/basic/split_dict.py +0 -0
  23. {podflow-20250404.9 → podflow-20250405}/podflow/basic/time_format.py +0 -0
  24. {podflow-20250404.9 → podflow-20250405}/podflow/basic/time_stamp.py +0 -0
  25. {podflow-20250404.9 → podflow-20250405}/podflow/basic/vary_replace.py +0 -0
  26. {podflow-20250404.9 → podflow-20250405}/podflow/basic/write_log.py +0 -0
  27. {podflow-20250404.9 → podflow-20250405}/podflow/bilibili/__init__.py +0 -0
  28. {podflow-20250404.9 → podflow-20250405}/podflow/bilibili/build.py +0 -0
  29. {podflow-20250404.9 → podflow-20250405}/podflow/bilibili/get.py +0 -0
  30. {podflow-20250404.9 → podflow-20250405}/podflow/bilibili/login.py +0 -0
  31. {podflow-20250404.9 → podflow-20250405}/podflow/config/__init__.py +0 -0
  32. {podflow-20250404.9 → podflow-20250405}/podflow/config/build_original.py +0 -0
  33. {podflow-20250404.9 → podflow-20250405}/podflow/config/channge_icon.py +0 -0
  34. {podflow-20250404.9 → podflow-20250405}/podflow/config/correct_channelid.py +0 -0
  35. {podflow-20250404.9 → podflow-20250405}/podflow/config/correct_config.py +0 -0
  36. {podflow-20250404.9 → podflow-20250405}/podflow/config/get_channelid.py +0 -0
  37. {podflow-20250404.9 → podflow-20250405}/podflow/config/get_channelid_id.py +0 -0
  38. {podflow-20250404.9 → podflow-20250405}/podflow/config/get_config.py +0 -0
  39. {podflow-20250404.9 → podflow-20250405}/podflow/download/__init__.py +0 -0
  40. {podflow-20250404.9 → podflow-20250405}/podflow/download/convert_bytes.py +0 -0
  41. {podflow-20250404.9 → podflow-20250405}/podflow/download/delete_part.py +0 -0
  42. {podflow-20250404.9 → podflow-20250405}/podflow/download/show_progress.py +0 -0
  43. {podflow-20250404.9 → podflow-20250405}/podflow/download/wait_animation.py +0 -0
  44. {podflow-20250404.9 → podflow-20250405}/podflow/download/youtube_and_bilibili_download.py +0 -0
  45. {podflow-20250404.9 → podflow-20250405}/podflow/download_and_build.py +0 -0
  46. {podflow-20250404.9 → podflow-20250405}/podflow/ffmpeg_judge.py +0 -0
  47. {podflow-20250404.9 → podflow-20250405}/podflow/httpfs/__init__.py +0 -0
  48. {podflow-20250404.9 → podflow-20250405}/podflow/httpfs/browser.py +0 -0
  49. {podflow-20250404.9 → podflow-20250405}/podflow/httpfs/get_channelid.py +0 -0
  50. {podflow-20250404.9 → podflow-20250405}/podflow/httpfs/port_judge.py +0 -0
  51. {podflow-20250404.9 → podflow-20250405}/podflow/httpfs/to_html.py +0 -0
  52. {podflow-20250404.9 → podflow-20250405}/podflow/main.py +0 -0
  53. {podflow-20250404.9 → podflow-20250405}/podflow/main_upload.py +0 -0
  54. {podflow-20250404.9 → podflow-20250405}/podflow/makeup/__init__.py +0 -0
  55. {podflow-20250404.9 → podflow-20250405}/podflow/makeup/del_makeup_format_fail.py +0 -0
  56. {podflow-20250404.9 → podflow-20250405}/podflow/makeup/make_up_file.py +0 -0
  57. {podflow-20250404.9 → podflow-20250405}/podflow/makeup/make_up_file_format_mod.py +0 -0
  58. {podflow-20250404.9 → podflow-20250405}/podflow/makeup/make_up_file_mod.py +0 -0
  59. {podflow-20250404.9 → podflow-20250405}/podflow/message/__init__.py +0 -0
  60. {podflow-20250404.9 → podflow-20250405}/podflow/message/backup_zip_save.py +0 -0
  61. {podflow-20250404.9 → podflow-20250405}/podflow/message/create_main_rss.py +0 -0
  62. {podflow-20250404.9 → podflow-20250405}/podflow/message/display_qrcode_and_url.py +0 -0
  63. {podflow-20250404.9 → podflow-20250405}/podflow/message/format_time.py +0 -0
  64. {podflow-20250404.9 → podflow-20250405}/podflow/message/get_media_name.py +0 -0
  65. {podflow-20250404.9 → podflow-20250405}/podflow/message/get_original_rss.py +0 -0
  66. {podflow-20250404.9 → podflow-20250405}/podflow/message/get_video_format.py +0 -0
  67. {podflow-20250404.9 → podflow-20250405}/podflow/message/get_video_format_multithread.py +0 -0
  68. {podflow-20250404.9 → podflow-20250405}/podflow/message/get_youtube_and_bilibili_video_format.py +0 -0
  69. {podflow-20250404.9 → podflow-20250405}/podflow/message/media_format.py +0 -0
  70. {podflow-20250404.9 → podflow-20250405}/podflow/message/original_rss_fail_print.py +0 -0
  71. {podflow-20250404.9 → podflow-20250405}/podflow/message/rss_create_hash.py +0 -0
  72. {podflow-20250404.9 → podflow-20250405}/podflow/message/save_rss.py +0 -0
  73. {podflow-20250404.9 → podflow-20250405}/podflow/message/title_correction.py +0 -0
  74. {podflow-20250404.9 → podflow-20250405}/podflow/message/update_information_display.py +0 -0
  75. {podflow-20250404.9 → podflow-20250405}/podflow/message/update_youtube_bilibili_rss.py +0 -0
  76. {podflow-20250404.9 → podflow-20250405}/podflow/message/want_retry.py +0 -0
  77. {podflow-20250404.9 → podflow-20250405}/podflow/message/xml_item.py +0 -0
  78. {podflow-20250404.9 → podflow-20250405}/podflow/message/xml_original_item.py +0 -0
  79. {podflow-20250404.9 → podflow-20250405}/podflow/message/xml_rss.py +0 -0
  80. {podflow-20250404.9 → podflow-20250405}/podflow/netscape/__init__.py +0 -0
  81. {podflow-20250404.9 → podflow-20250405}/podflow/netscape/bulid_netscape.py +0 -0
  82. {podflow-20250404.9 → podflow-20250405}/podflow/netscape/get_cookie_dict.py +0 -0
  83. {podflow-20250404.9 → podflow-20250405}/podflow/parse_arguments.py +0 -0
  84. {podflow-20250404.9 → podflow-20250405}/podflow/remove/__init__.py +0 -0
  85. {podflow-20250404.9 → podflow-20250405}/podflow/remove/remove_dir.py +0 -0
  86. {podflow-20250404.9 → podflow-20250405}/podflow/remove/remove_file.py +0 -0
  87. {podflow-20250404.9 → podflow-20250405}/podflow/repair/__init__.py +0 -0
  88. {podflow-20250404.9 → podflow-20250405}/podflow/repair/reverse_log.py +0 -0
  89. {podflow-20250404.9 → podflow-20250405}/podflow/upload/__init__.py +0 -0
  90. {podflow-20250404.9 → podflow-20250405}/podflow/upload/add_upload.py +0 -0
  91. {podflow-20250404.9 → podflow-20250405}/podflow/upload/build_hash.py +0 -0
  92. {podflow-20250404.9 → podflow-20250405}/podflow/upload/get_upload_original.py +0 -0
  93. {podflow-20250404.9 → podflow-20250405}/podflow/upload/linked_client.py +0 -0
  94. {podflow-20250404.9 → podflow-20250405}/podflow/upload/linked_server.py +0 -0
  95. {podflow-20250404.9 → podflow-20250405}/podflow/upload/login.py +0 -0
  96. {podflow-20250404.9 → podflow-20250405}/podflow/upload/time_key.py +0 -0
  97. {podflow-20250404.9 → podflow-20250405}/podflow/upload/update_upload.py +0 -0
  98. {podflow-20250404.9 → podflow-20250405}/podflow/upload/upload_server.py +0 -0
  99. {podflow-20250404.9 → podflow-20250405}/podflow/youtube/__init__.py +0 -0
  100. {podflow-20250404.9 → podflow-20250405}/podflow/youtube/build.py +0 -0
  101. {podflow-20250404.9 → podflow-20250405}/podflow/youtube/get.py +0 -0
  102. {podflow-20250404.9 → podflow-20250405}/podflow/youtube/login.py +0 -0
  103. {podflow-20250404.9 → podflow-20250405}/podflow.egg-info/SOURCES.txt +0 -0
  104. {podflow-20250404.9 → podflow-20250405}/podflow.egg-info/dependency_links.txt +0 -0
  105. {podflow-20250404.9 → podflow-20250405}/podflow.egg-info/entry_points.txt +0 -0
  106. {podflow-20250404.9 → podflow-20250405}/podflow.egg-info/requires.txt +0 -0
  107. {podflow-20250404.9 → podflow-20250405}/podflow.egg-info/top_level.txt +0 -0
  108. {podflow-20250404.9 → podflow-20250405}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250404.9
3
+ Version: 20250405
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;
@@ -312,9 +320,17 @@
312
320
  if (node.nodeType === Node.ELEMENT_NODE) {
313
321
  if (node.classList.contains('qrcode-container')) {
314
322
  generateQRCodeForNode(node);
323
+ // 在生成二维码后,检查是否需要滚动
324
+ if (!userScrolled && container.scrollHeight - container.scrollTop <= container.clientHeight + 10) {
325
+ container.scrollTop = container.scrollHeight;
326
+ }
315
327
  } else {
316
328
  const childContainers = node.querySelectorAll('.qrcode-container');
317
329
  childContainers.forEach(generateQRCodeForNode);
330
+ // 在生成二维码后,检查是否需要滚动
331
+ if (!userScrolled && container.scrollHeight - container.scrollTop <= container.clientHeight + 10) {
332
+ container.scrollTop = container.scrollHeight;
333
+ }
318
334
  }
319
335
  }
320
336
  });
@@ -343,6 +359,7 @@
343
359
 
344
360
  // 监听 messageArea 内动态新增的二维码容器
345
361
  observeQRCodes(messageArea);
362
+ observeQRCodes(messageHttp); // Also observe for messageHttp
346
363
 
347
364
  // 菜单切换函数
348
365
  function toggleMenu() {
@@ -381,8 +398,8 @@
381
398
  // 监听滚动事件,检测用户是否手动滚动
382
399
  function onUserScroll(event) {
383
400
  const element = event.target;
384
- // 判断是否接近底部
385
- const nearBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 10;
401
+ // 判断是否接近底部,增加一定的容差值
402
+ const nearBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 20;
386
403
  userScrolled = !nearBottom;
387
404
  }
388
405
 
@@ -413,8 +430,8 @@
413
430
 
414
431
  function appendMessages(container, newMessages, oldMessages) {
415
432
  // 判断当前是否在底部
416
- const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10;
417
-
433
+ const wasAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10;
434
+
418
435
  // 当两数组长度相等且有内容时,只比较最后一项
419
436
  if (newMessages.length === oldMessages.length && newMessages.length > 0) {
420
437
  const lastNewMessage = newMessages[newMessages.length - 1];
@@ -446,9 +463,9 @@
446
463
  container.appendChild(createMessageElement(msg));
447
464
  });
448
465
  }
449
-
450
- // 如果用户没有主动滚动,则自动滚动到底部
451
- if (!userScrolled) {
466
+
467
+ // 如果之前在底部且用户没有主动滚动,则自动滚动到底部
468
+ if (wasAtBottom && !userScrolled) {
452
469
  container.scrollTop = container.scrollHeight;
453
470
  }
454
471
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250404.9
3
+ Version: 20250405
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="20250405",
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