podflow 20250706__py3-none-any.whl → 20250803__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
podflow/__init__.py CHANGED
@@ -34,7 +34,7 @@ default_config = {
34
34
  "QRcode": False, # 是否显示子博客地址二维码(仅在DisplayRSSaddress为True时有效)
35
35
  "BackwardUpdate": False, # 是否向后更新
36
36
  "BackwardUpdate_size": 3, # 向后更新数量(仅在BackwardUpdate为True时有效)
37
- "want_retry_count": 8, # 媒体获取失败后多少次后重试(小于等于该数量时将一直重试)
37
+ "want_retry_count": 25, # 媒体获取失败后多少次后重试(小于等于该数量时将一直重试)
38
38
  "title_change": [ # 标题文本修改(默认为无, 可多个条件,以列表形式存在)
39
39
  { # match和url参数至少有一个, 如都有将同时生效
40
40
  "mode": "add-left", # 修改模式(add-left: 开头添加, add-right: 结尾添加, replace: 内容替换)
@@ -146,6 +146,7 @@ class Application_parse:
146
146
  self.file = ""
147
147
  self.httpfs = False
148
148
  self.index = False
149
+ self.save = []
149
150
 
150
151
 
151
152
  # 创建 Application 类的实例
@@ -62,19 +62,19 @@ def correct_channelid(channelid, website):
62
62
  # 复制字典channelid, 遍历复制后的字典进行操作以避免在循环中删除元素导致的迭代错误
63
63
  channelid_copy = channelid.copy()
64
64
  # 对channelid的错误进行更正
65
- for channelid_key, channeli_value in channelid_copy.items():
65
+ for channelid_key, channelid_value in channelid_copy.items():
66
66
  # 判断是否为字典
67
- if not isinstance(channeli_value, dict):
68
- channeli_value = {"id": channeli_value}
69
- channelid[channelid_key] = channeli_value
67
+ if not isinstance(channelid_value, dict):
68
+ channelid_value = {"id": channelid_value}
69
+ channelid[channelid_key] = channelid_value
70
70
  # 判断id是否正确
71
71
  if (
72
- "id" not in channeli_value
72
+ "id" not in channelid_value
73
73
  or (
74
74
  website == "youtube"
75
- and not re.search(r"^UC.{22}", channeli_value["id"])
75
+ and not re.search(r"^UC.{22}", channelid_value["id"])
76
76
  )
77
- or (website == "bilibili" and not channeli_value["id"].isdigit())
77
+ or (website == "bilibili" and not channelid_value["id"].isdigit())
78
78
  ):
79
79
  # 删除错误的
80
80
  del channelid[channelid_key]
@@ -82,9 +82,9 @@ def correct_channelid(channelid, website):
82
82
  else:
83
83
  # 对update_size进行纠正
84
84
  if (
85
- "update_size" not in channeli_value
86
- or not isinstance(channeli_value["update_size"], int)
87
- or channeli_value["update_size"] <= 0
85
+ "update_size" not in channelid_value
86
+ or not isinstance(channelid_value["update_size"], int)
87
+ or channelid_value["update_size"] <= 0
88
88
  ):
89
89
  channelid[channelid_key]["update_size"] = default_config[
90
90
  f"channelid_{website}"
@@ -92,13 +92,13 @@ def correct_channelid(channelid, website):
92
92
  # 对id进行纠正
93
93
  if website == "youtube":
94
94
  channelid[channelid_key]["id"] = re.search(
95
- r"UC.{22}", channeli_value["id"]
95
+ r"UC.{22}", channelid_value["id"]
96
96
  ).group()
97
97
  # 对last_size进行纠正
98
98
  if (
99
- "last_size" not in channeli_value
100
- or not isinstance(channeli_value["last_size"], int)
101
- or channeli_value["last_size"] <= 0
99
+ "last_size" not in channelid_value
100
+ or not isinstance(channelid_value["last_size"], int)
101
+ or channelid_value["last_size"] <= 0
102
102
  ):
103
103
  channelid[channelid_key]["last_size"] = default_config[
104
104
  f"channelid_{website}"
@@ -108,77 +108,77 @@ def correct_channelid(channelid, website):
108
108
  channelid[channelid_key]["update_size"],
109
109
  )
110
110
  # 对title进行纠正
111
- if "title" not in channeli_value:
111
+ if "title" not in channelid_value:
112
112
  channelid[channelid_key]["title"] = channelid_key
113
113
  # 对quality进行纠正
114
114
  if (
115
115
  (
116
- "quality" not in channeli_value
117
- or channeli_value["quality"] not in dpi
116
+ "quality" not in channelid_value
117
+ or channelid_value["quality"] not in dpi
118
118
  )
119
- and "media" in channeli_value
120
- and channeli_value["media"] == "mp4"
119
+ and "media" in channelid_value
120
+ and channelid_value["media"] == "mp4"
121
121
  ):
122
122
  channelid[channelid_key]["quality"] = default_config[
123
123
  f"channelid_{website}"
124
124
  ][channelid_name]["quality"]
125
125
  # 对media进行纠正
126
126
  if (
127
- "media" in channeli_value
128
- and channeli_value["media"] not in media
129
- and channeli_value["media"] in video_media
127
+ "media" in channelid_value
128
+ and channelid_value["media"] not in media
129
+ and channelid_value["media"] in video_media
130
130
  ):
131
131
  channelid[channelid_key]["media"] = "mp4"
132
132
  elif (
133
- "media" in channeli_value
134
- and channeli_value["media"] not in media
135
- or "media" not in channeli_value
133
+ "media" in channelid_value
134
+ and channelid_value["media"] not in media
135
+ or "media" not in channelid_value
136
136
  ):
137
137
  channelid[channelid_key]["media"] = "m4a"
138
138
  # 对DisplayRSSaddress进行纠正
139
- if "DisplayRSSaddress" not in channeli_value or not isinstance(
140
- channeli_value["DisplayRSSaddress"], bool
139
+ if "DisplayRSSaddress" not in channelid_value or not isinstance(
140
+ channelid_value["DisplayRSSaddress"], bool
141
141
  ):
142
142
  channelid[channelid_key]["DisplayRSSaddress"] = False
143
143
  # 对InmainRSS进行纠正
144
- if "InmainRSS" in channeli_value and isinstance(
145
- channeli_value["InmainRSS"], bool
144
+ if "InmainRSS" in channelid_value and isinstance(
145
+ channelid_value["InmainRSS"], bool
146
146
  ):
147
- if channeli_value["InmainRSS"] is False:
147
+ if channelid_value["InmainRSS"] is False:
148
148
  channelid[channelid_key]["DisplayRSSaddress"] = True
149
149
  else:
150
150
  channelid[channelid_key]["InmainRSS"] = True
151
151
  # 对QRcode进行纠正
152
- if "QRcode" not in channeli_value or not isinstance(
153
- channeli_value["QRcode"], bool
152
+ if "QRcode" not in channelid_value or not isinstance(
153
+ channelid_value["QRcode"], bool
154
154
  ):
155
155
  channelid[channelid_key]["QRcode"] = False
156
156
  # 对BackwardUpdate进行纠正
157
- if "BackwardUpdate" not in channeli_value or not isinstance(
158
- channeli_value["BackwardUpdate"], bool
157
+ if "BackwardUpdate" not in channelid_value or not isinstance(
158
+ channelid_value["BackwardUpdate"], bool
159
159
  ):
160
160
  channelid[channelid_key]["BackwardUpdate"] = False
161
161
  # 对BackwardUpdate_size进行纠正
162
162
  if channelid[channelid_key]["BackwardUpdate"] and (
163
- "BackwardUpdate_size" not in channeli_value
164
- or not isinstance(channeli_value["BackwardUpdate_size"], int)
165
- or channeli_value["BackwardUpdate_size"] <= 0
163
+ "BackwardUpdate_size" not in channelid_value
164
+ or not isinstance(channelid_value["BackwardUpdate_size"], int)
165
+ or channelid_value["BackwardUpdate_size"] <= 0
166
166
  ):
167
167
  channelid[channelid_key]["BackwardUpdate_size"] = default_config[
168
168
  f"channelid_{website}"
169
169
  ][channelid_name]["BackwardUpdate_size"]
170
170
  # 对want_retry_count进行纠正
171
171
  if (
172
- "want_retry_count" not in channeli_value
173
- or not isinstance(channeli_value["want_retry_count"], int)
174
- or channeli_value["want_retry_count"] <= 0
172
+ "want_retry_count" not in channelid_value
173
+ or not isinstance(channelid_value["want_retry_count"], int)
174
+ or channelid_value["want_retry_count"] <= 0
175
175
  ):
176
176
  channelid[channelid_key]["want_retry_count"] = default_config[
177
177
  f"channelid_{website}"
178
178
  ][channelid_name]["want_retry_count"]
179
179
  # 对title_change进行纠正
180
- if "title_change" in channeli_value:
181
- title_changes = channeli_value["title_change"]
180
+ if "title_change" in channelid_value:
181
+ title_changes = channelid_value["title_change"]
182
182
  uphold_title_changes = []
183
183
  if isinstance(title_changes, list):
184
184
  for title_change in title_changes:
@@ -208,23 +208,23 @@ def correct_channelid(channelid, website):
208
208
  else:
209
209
  del channelid[channelid_key]["title_change"]
210
210
  if website == "bilibili" and (
211
- "AllPartGet" not in channeli_value
212
- or not isinstance(channeli_value["AllPartGet"], bool)
211
+ "AllPartGet" not in channelid_value
212
+ or not isinstance(channelid_value["AllPartGet"], bool)
213
213
  ):
214
214
  channelid[channelid_key]["AllPartGet"] = (
215
215
  channelid[channelid_key]["update_size"] > 5
216
216
  )
217
217
  if website == "youtube" and (
218
- "NoShorts" not in channeli_value
219
- or not isinstance(channeli_value["NoShorts"], bool)
218
+ "NoShorts" not in channelid_value
219
+ or not isinstance(channelid_value["NoShorts"], bool)
220
220
  ):
221
221
  channelid[channelid_key]["NoShorts"] = False
222
222
  if (
223
223
  channelid[channelid_key]["InmainRSS"] is False
224
- and f"{config['address']}/channel_rss/{channeli_value['id']}.xml"
224
+ and f"{config['address']}/channel_rss/{channelid_value['id']}.xml"
225
225
  not in parse.shortcuts_url_original
226
226
  ):
227
227
  gVar.shortcuts_url[channelid_key] = (
228
- f"{config['address']}/channel_rss/{channeli_value['id']}.xml"
228
+ f"{config['address']}/channel_rss/{channelid_value['id']}.xml"
229
229
  )
230
230
  return channelid
@@ -21,6 +21,7 @@ from podflow.upload.time_key import check_time_key
21
21
  from podflow.basic.folder_build import folder_build
22
22
  from podflow.httpfs.get_channelid import get_channelid
23
23
  from podflow.basic.random_sequence import random_sequence
24
+ from podflow.upload.find_media_index import find_media_index
24
25
  from podflow.upload.store_users_info import store_users_info
25
26
 
26
27
 
@@ -47,6 +48,7 @@ class bottle_app:
47
48
  self.app_bottle.route("/login", callback=self.login)
48
49
  self.app_bottle.route("/upload", method="POST", callback=self.upload)
49
50
  self.app_bottle.route("/flush", method="POST", callback=self.clear_cache)
51
+ self.app_bottle.route("/remove", method="POST", callback=self.remove)
50
52
  else:
51
53
  self.app_bottle.route("/index", callback=self.index)
52
54
  self.app_bottle.route("/getid", method="POST", callback=self.getid)
@@ -278,7 +280,7 @@ class bottle_app:
278
280
  self.print_out("newuser", 401)
279
281
  return {
280
282
  "code": -1,
281
- "message": "Unauthorized: Invalid Token",
283
+ "message": "Unauthorized: Invalid Token", # 未经授权: 无效的 Token
282
284
  }
283
285
 
284
286
  # 路由处理登陆请求
@@ -357,6 +359,12 @@ class bottle_app:
357
359
  "code": -6,
358
360
  "message": "ChannelId Does Not Exist", # 频道ID不存在
359
361
  }
362
+ if not filename:
363
+ self.print_out("upload", 404)
364
+ return {
365
+ "code": -14,
366
+ "message": "Filename Not Provided", # 未提供文件名
367
+ }
360
368
  address = f"channel_audiovisual/{channelid}"
361
369
  file_list = os.listdir(address) if os.path.exists(address) else []
362
370
  # 安全地分割文件名和后缀
@@ -431,7 +439,7 @@ class bottle_app:
431
439
  original_file.seek(0)
432
440
  if upload_hash == build_hash(original_file):
433
441
  self.print_out("upload same", 200)
434
- store_users_info(username,filename)
442
+ store_users_info(username, filename, channelid)
435
443
  close_file()
436
444
  return {
437
445
  "code": 1,
@@ -452,7 +460,7 @@ class bottle_app:
452
460
  ) # 传递文件对象
453
461
  # 打印成功信息并返回成功码
454
462
  self.print_out("upload", 200)
455
- store_users_info(username,filename)
463
+ store_users_info(username, filename, channelid)
456
464
  close_file()
457
465
  return {
458
466
  "code": 0,
@@ -465,7 +473,7 @@ class bottle_app:
465
473
  # 捕获所有其他可能的异常
466
474
  self.print_out("upload", 500)
467
475
  return {
468
- "code": -9,
476
+ "code": -10,
469
477
  "message": f"Server Error: {str(e)}", # 将异常信息返回给客户端
470
478
  }
471
479
  finally:
@@ -495,7 +503,7 @@ class bottle_app:
495
503
  original_file.seek(0)
496
504
  if upload_hash == build_hash(original_file):
497
505
  self.print_out("upload same", 200)
498
- store_users_info(username,filename)
506
+ store_users_info(username, filename, channelid)
499
507
  return {
500
508
  "code": 1,
501
509
  "message": "The Same File Exists", # 相同文件已存在
@@ -557,10 +565,155 @@ class bottle_app:
557
565
  else:
558
566
  self.print_out("flush", 404)
559
567
  return {
560
- "code": -10,
561
- "message": "Cache Does Not Exist", # 频道ID不存在
568
+ "code": -12,
569
+ "message": "Cache Does Not Exist", # 缓存不存在
562
570
  }
563
571
 
572
+ # 路由处理删除请求
573
+ def remove(self):
574
+ # 获取已上传数据
575
+ upload_message = gVar.upload_message
576
+ # 获取上传数据配置(存储用户名和密码)
577
+ upload_data = gVar.upload_data
578
+ # 从请求参数中获取用户名,默认为空字符串
579
+ username = request.query.get("username", "")
580
+ # 从请求参数中获取密码,默认为空字符串
581
+ password = request.query.get("password", "")
582
+ if username not in upload_data:
583
+ self.print_out("login", 401)
584
+ return {
585
+ "code": -2,
586
+ "message": "Username Error", # 用户名错误
587
+ "error": None,
588
+ }
589
+ # 验证密码是否正确
590
+ if upload_data[username] != password:
591
+ self.print_out("login", 401)
592
+ return {
593
+ "code": -3,
594
+ "message": "Password Error", # 密码错误
595
+ "error": None,
596
+ }
597
+ mode = request.query.get("mode", "")
598
+ if mode not in ["file", "folder"]:
599
+ self.print_out("remove", 404)
600
+ return {
601
+ "code": -13,
602
+ "message": "Invalid Mode", # 无效的模式
603
+ "error": None,
604
+ }
605
+ channelid = request.query.get("channel_id", "")
606
+ if not channelid:
607
+ # 打印错误信息并返回错误码
608
+ self.print_out("remove", 404)
609
+ return {
610
+ "code": -6,
611
+ "message": "ChannelId Does Not Exist", # 频道ID不存在
612
+ "error": None,
613
+ }
614
+ if mode == "file":
615
+ filename = request.query.get("filename", "")
616
+ if not filename:
617
+ self.print_out("remove", 404)
618
+ return {
619
+ "code": -14,
620
+ "message": "Filename Not Provided", # 未提供文件名
621
+ "error": None,
622
+ }
623
+ index = find_media_index(upload_message, filename, "mediaid")
624
+ if index == -1:
625
+ self.print_out("remove", 404)
626
+ return{
627
+ "code": -15,
628
+ "message": "File Not In Data", # 文件不在数据中
629
+ "error": None,
630
+ }
631
+ if upload_message[index]["channelid"] != channelid:
632
+ self.print_out("remove", 404)
633
+ return {
634
+ "code": -16,
635
+ "message": "ChannelId Mismatch", # 频道ID不匹配
636
+ "error": None,
637
+ }
638
+ userlist = upload_message[index]["users"]
639
+ if username not in userlist:
640
+ self.print_out("remove", 404)
641
+ return {
642
+ "code": -17,
643
+ "message": "User Not In List", # 用户不在列表中
644
+ "error": None,
645
+ }
646
+ try:
647
+ os.remove(f"channel_audiovisual/{channelid}/{filename}")
648
+ except FileNotFoundError:
649
+ self.print_out("remove", 404)
650
+ return {
651
+ "code": -18,
652
+ "message": "File Not Found", # 文件未找到
653
+ "error": None,
654
+ }
655
+ except Exception as e:
656
+ self.print_out("remove", 500)
657
+ return {
658
+ "code": -19,
659
+ "message": f"Error Removing File: {str(e)}", # 删除文件错误
660
+ "error": str(e),
661
+ }
662
+ if len(userlist) == 1:
663
+ # 如果用户列表中只有当前用户, 则删除该条记录
664
+ del upload_message[index]
665
+ else:
666
+ # 如果用户列表中有多个用户, 则移除当前用户
667
+ upload_message[index]["users"].remove(username)
668
+ self.print_out("remove", 200)
669
+ return {
670
+ "code": 4,
671
+ "message": "File Removed Successfully", # 文件删除成功
672
+ "error": None,
673
+ }
674
+ else:
675
+ remove_num = 0
676
+ for item in upload_message:
677
+ userlist = item["users"]
678
+ if item["channelid"] == channelid and username in userlist:
679
+ try:
680
+ os.remove(f"channel_audiovisual/{channelid}/{item['mediaid']}")
681
+ remove_num += 1
682
+ except FileNotFoundError:
683
+ self.print_out("remove", 404)
684
+ return {
685
+ "code": -18,
686
+ "message": "File Not Found", # 文件未找到
687
+ "error": None,
688
+ }
689
+ except Exception as e:
690
+ self.print_out("remove", 500)
691
+ return {
692
+ "code": -19,
693
+ "message": f"Error Removing File: {str(e)}", # 删除文件错误
694
+ "error": str(e),
695
+ }
696
+ if len(userlist) == 1:
697
+ # 如果用户列表中只有当前用户, 则删除该条记录
698
+ del upload_message[index]
699
+ else:
700
+ # 如果用户列表中有多个用户, 则移除当前用户
701
+ upload_message[index]["users"].remove(username)
702
+ if remove_num == 0:
703
+ self.print_out("remove", 404)
704
+ return {
705
+ "code": -20,
706
+ "message": "No Files Found", # 未找到用户的文件
707
+ "error": None,
708
+ }
709
+ else:
710
+ self.print_out("remove", 200)
711
+ return {
712
+ "code": 5,
713
+ "message": "Folder Removed Successfully", # 文件夹删除成功
714
+ "error": None,
715
+ }
716
+
564
717
  # 路由处理模板文件请求
565
718
  def serve_template_file(self, filepath):
566
719
  template_dir = pkg_resources.resource_filename("podflow", "templates")
podflow/main_podcast.py CHANGED
@@ -12,6 +12,7 @@ import cherrypy
12
12
 
13
13
  # 基本功能模块
14
14
  from podflow import gVar, parse
15
+ from podflow.basic.file_save import file_save
15
16
  from podflow.basic.split_dict import split_dict
16
17
  from podflow.basic.time_print import time_print
17
18
 
@@ -211,9 +212,9 @@ def main_podcast():
211
212
  progress_update(0.83, num=0.0049)
212
213
  if gVar.config["remove_media"]:
213
214
  # 删除不在rss中的媒体文件
214
- remove_file()
215
+ remove_file(upload_url)
215
216
  # 删除已抛弃的媒体文件夹
216
- remove_dir()
217
+ remove_dir(upload_url)
217
218
  progress_update(0.84)
218
219
  # 补全缺失媒体文件到字典
219
220
  make_up_file()
@@ -257,6 +258,12 @@ def main_podcast():
257
258
  if upload_url:
258
259
  thread_upload.join()
259
260
  time_print("频道无更新内容")
261
+ # 保存需要的变量
262
+ if parse.save:
263
+ for save_data in parse.save:
264
+ file_data = getattr(gVar, save_data, None)
265
+ if file_data:
266
+ file_save(file_data, f"{save_data}.json")
260
267
  # 清空变量内数据
261
268
  gVar.channelid_youtube_ids_update.clear() # 需更新的YouTube频道字典
262
269
  gVar.youtube_content_ytid_update.clear() # 需下载YouTube视频字典
@@ -170,15 +170,20 @@ error_reason = [
170
170
  "text",
171
171
  ],
172
172
  [
173
- r"Got error: HTTPSConnectionPool\(host='.+\.mcdn\.bilivideo\.cn', port=8082\): Read timed out\. \(read timeout=20\.0\)",
173
+ r"Got error: HTTPSConnectionPool\(host='.+\.mcdn\.bilivideo\.cn', port=[0-9]{4}\): Read timed out\. \(read timeout=20\.0\)",
174
174
  "\033[31m响应超时\033[0m",
175
175
  "regexp",
176
176
  ],
177
177
  [
178
- r"Got error: \<urllib3\.connection\.HTTPSConnection object at .{18}\>: Failed to establish a new connection: \[WinError 10061\] 由于目标计算机积极拒绝,无法连接。"
178
+ r"Got error: \<urllib3\.connection\.HTTPSConnection object at .{18}\>: Failed to establish a new connection: \[WinError 10061\] 由于目标计算机积极拒绝,无法连接。",
179
179
  "\033[31m链接拒绝\033[0m",
180
180
  "regexp",
181
181
  ],
182
+ [
183
+ r"YouTube said: The playlist does not exist.",
184
+ "\033[31m播放列表不存在\033[0m",
185
+ "text",
186
+ ],
182
187
  ]
183
188
 
184
189
 
@@ -194,6 +199,7 @@ def fail_message_initialize(message_error, video_url):
194
199
  .replace("[youtube] ", "")
195
200
  .replace("[download] ", "")
196
201
  .replace("[BiliBili] ", "")
202
+ .replace("[youtube:tab] ", "")
197
203
  )
198
204
  if video_url[:2] == "BV":
199
205
  fail_message = fail_message.replace(f"{video_url[2:]}: ", "")
@@ -1,9 +1,24 @@
1
- # podflow/message/want_retry.py
1
+ # podflow/message/update_information_display.py
2
2
  # coding: utf-8
3
3
 
4
4
  import re
5
5
  import os
6
+ from podflow import gVar
6
7
  from podflow.basic.write_log import write_log
8
+ from podflow.message.want_retry import want_retry
9
+
10
+
11
+ def skip_display(name, channelid_key, channelid_value, id_update):
12
+ if name == "YouTube":
13
+ failed_count = gVar.channelid_youtube[channelid_value]["want_retry_count"]
14
+ elif name == "BiliBili":
15
+ failed_count = gVar.channelid_bilibili[channelid_value]["want_retry_count"]
16
+ else:
17
+ failed_count = 0
18
+ for video_id in id_update[channelid_key]:
19
+ if want_retry(video_id, failed_count):
20
+ return False
21
+ return True
7
22
 
8
23
 
9
24
  # 输出需要更新的信息模块
@@ -40,11 +55,23 @@ def update_information_display(
40
55
  channelid_key in content_id_update
41
56
  and channelid_key in content_id_backward_update
42
57
  ):
43
- print_channelid_ids_update += f"\033[34m{channelid_value}\033[0m"
58
+ if (
59
+ skip_display(name, channelid_key, channelid_value, content_id_update)
60
+ and skip_display(name, channelid_key, channelid_value, content_id_backward_update)
61
+ ):
62
+ print_channelid_ids_update += f"\033[97m{channelid_value}\033[0m"
63
+ else:
64
+ print_channelid_ids_update += f"\033[34m{channelid_value}\033[0m"
44
65
  elif channelid_key in content_id_update:
45
- print_channelid_ids_update += f"\033[32m{channelid_value}\033[0m"
66
+ if skip_display(name, channelid_key, channelid_value, content_id_update):
67
+ print_channelid_ids_update += f"\033[97m{channelid_value}\033[0m"
68
+ else:
69
+ print_channelid_ids_update += f"\033[32m{channelid_value}\033[0m"
46
70
  elif channelid_key in content_id_backward_update:
47
- print_channelid_ids_update += f"\033[36m{channelid_value}\033[0m"
71
+ if skip_display(name, channelid_key, channelid_value, content_id_backward_update):
72
+ print_channelid_ids_update += f"\033[97m{channelid_value}\033[0m"
73
+ else:
74
+ print_channelid_ids_update += f"\033[36m{channelid_value}\033[0m"
48
75
  else:
49
76
  print_channelid_ids_update += f"\033[33m{channelid_value}\033[0m"
50
77
  # 如果含有特殊字符将使用此输出
@@ -73,6 +73,13 @@ def parse_arguments():
73
73
  action="store_true",
74
74
  help="Only upload server function, solely for LAN backup (applicable to iOS)",
75
75
  )
76
+ parser.add_argument(
77
+ "--save",
78
+ nargs="*",
79
+ type=str,
80
+ metavar="Variable",
81
+ help="Used during testing",
82
+ )
76
83
  parser.add_argument("--file", nargs="?", help=argparse.SUPPRESS) # 仅运行在ipynb中
77
84
  # 解析参数
78
85
  args = parser.parse_args()
@@ -83,6 +90,7 @@ def parse_arguments():
83
90
  parse.httpfs = args.httpfs
84
91
  parse.upload = args.upload
85
92
  parse.index = args.index
93
+ parse.save = args.save
86
94
  # 检查并处理参数的状态
87
95
  if args.times is not None:
88
96
  parse.update_num = int(args.times[0])
@@ -6,10 +6,64 @@ import re
6
6
  import shutil
7
7
  from podflow import gVar
8
8
  from podflow.basic.write_log import write_log
9
+ from podflow.basic.http_client import http_client
10
+
11
+
12
+ def judge_upload(upload_url, name):
13
+ if upload_url:
14
+ result = {
15
+ -2: "用户名错误",
16
+ -3: "密码错误",
17
+ -13: "删除模式错误",
18
+ -6: "频道ID不存在",
19
+ -14: "未提供文件名",
20
+ -15: "文件名不匹配",
21
+ -16: "频道ID不匹配",
22
+ -17: "用户名不匹配",
23
+ -18: "文件不存在",
24
+ -19: "删除文件错误",
25
+ -20: "未找到用户文件",
26
+ }
27
+ username = gVar.upload_json["username"]
28
+ password = gVar.upload_json["password"]
29
+ data = {
30
+ "username": username,
31
+ "password": password,
32
+ "mode": "folder",
33
+ "channelid": name,
34
+ }
35
+ response, err = http_client(
36
+ url=f"{upload_url}/remove",
37
+ name="",
38
+ max_retries=3,
39
+ data=data,
40
+ mode="post",
41
+ mistake=True,
42
+ )
43
+ if response:
44
+ response = response.json()
45
+ code = response.get("code")
46
+ error = response.get("error", "")
47
+ if code == 5:
48
+ return True
49
+ else:
50
+ message = result.get(code, "未知错误")
51
+ if error:
52
+ message += f":\n{error}"
53
+ bottle_text = f"\033[31m远程删除文件夹失败\033[0m: {message}"
54
+ write_log(bottle_text)
55
+ return False
56
+ else:
57
+ bottle_text = f"\033[31m远程删除文件夹失败\033[0m: 网络连接失败{err}"
58
+ write_log(bottle_text)
59
+ elif gVar.config["upload"]:
60
+ return False
61
+ else:
62
+ return True
9
63
 
10
64
 
11
65
  # 删除已抛弃的媒体文件夹模块
12
- def remove_dir():
66
+ def remove_dir(upload_url):
13
67
  def remove_path(name):
14
68
  directory_path = f"channel_audiovisual/{name}"
15
69
  # 检查目录是否存在
@@ -25,9 +79,15 @@ def remove_dir():
25
79
  ]
26
80
  folder_names_youtube = [name for name in folder_names if re.match(r"UC.{22}", name)]
27
81
  for name in folder_names_youtube:
28
- if name not in gVar.channelid_youtube_ids_original:
82
+ if (
83
+ name not in gVar.channelid_youtube_ids_original
84
+ and judge_upload(upload_url, name)
85
+ ):
29
86
  remove_path(name)
30
87
  folder_names_bilibili = [name for name in folder_names if re.match(r"[0-9]+", name)]
31
88
  for name in folder_names_bilibili:
32
- if name not in gVar.channelid_bilibili_ids_original:
89
+ if (
90
+ name not in gVar.channelid_bilibili_ids_original
91
+ and judge_upload(upload_url, name)
92
+ ):
33
93
  remove_path(name)
@@ -4,21 +4,77 @@
4
4
  import os
5
5
  from podflow import gVar
6
6
  from podflow.basic.write_log import write_log
7
+ from podflow.basic.http_client import http_client
8
+
9
+
10
+ def judge_upload(upload_url, output_dir, file_name):
11
+ if upload_url:
12
+ result = {
13
+ -2: "用户名错误",
14
+ -3: "密码错误",
15
+ -13: "删除模式错误",
16
+ -6: "频道ID不存在",
17
+ -14: "未提供文件名",
18
+ -15: "文件名不匹配",
19
+ -16: "频道ID不匹配",
20
+ -17: "用户名不匹配",
21
+ -18: "文件不存在",
22
+ -19: "删除文件错误",
23
+ -20: "未找到用户文件",
24
+ }
25
+ username = gVar.upload_json["username"]
26
+ password = gVar.upload_json["password"]
27
+ data = {
28
+ "username": username,
29
+ "password": password,
30
+ "mode": "file",
31
+ "channelid": output_dir,
32
+ "filename": file_name,
33
+ }
34
+ response, err = http_client(
35
+ url=f"{upload_url}/remove",
36
+ name="",
37
+ max_retries=3,
38
+ data=data,
39
+ mode="post",
40
+ mistake=True,
41
+ )
42
+ if response:
43
+ response = response.json()
44
+ code = response.get("code")
45
+ error = response.get("error", "")
46
+ if code == 4:
47
+ return True
48
+ else:
49
+ message = result.get(code, "未知错误")
50
+ if error:
51
+ message += f":\n{error}"
52
+ bottle_text = f"\033[31m远程删除文件失败\033[0m: {message}"
53
+ write_log(bottle_text)
54
+ return False
55
+ else:
56
+ bottle_text = f"\033[31m远程删除文件失败\033[0m: 网络连接失败{err}"
57
+ write_log(bottle_text)
58
+ elif gVar.config["upload"]:
59
+ return False
60
+ else:
61
+ return True
7
62
 
8
63
 
9
64
  # 删除多余媒体文件模块
10
- def remove_file():
65
+ def remove_file(upload_url):
11
66
  channelid_youtube_ids = gVar.channelid_youtube_ids
12
-
13
67
  for output_dir, name in channelid_youtube_ids.items():
14
68
  for file_name in os.listdir(f"channel_audiovisual/{output_dir}"):
15
69
  if file_name not in gVar.all_youtube_content_ytid[output_dir]:
16
- os.remove(f"channel_audiovisual/{output_dir}/{file_name}")
17
- write_log(f"{name}|{file_name}抛弃文件已删除")
70
+ if judge_upload(upload_url, output_dir, file_name):
71
+ os.remove(f"channel_audiovisual/{output_dir}/{file_name}")
72
+ write_log(f"{name}|{file_name}抛弃文件已删除")
18
73
 
19
74
  channelid_bilibili_ids = gVar.channelid_bilibili_ids
20
75
  for output_dir, name in channelid_bilibili_ids.items():
21
76
  for file_name in os.listdir(f"channel_audiovisual/{output_dir}"):
22
77
  if file_name not in gVar.all_bilibili_content_bvid[output_dir]:
23
- os.remove(f"channel_audiovisual/{output_dir}/{file_name}")
24
- write_log(f"{name}|{file_name}抛弃文件已删除")
78
+ if judge_upload(upload_url, output_dir, file_name):
79
+ os.remove(f"channel_audiovisual/{output_dir}/{file_name}")
80
+ write_log(f"{name}|{file_name}抛弃文件已删除")
@@ -3,8 +3,8 @@
3
3
 
4
4
 
5
5
  # 查找位置模块
6
- def find_media_index(upload_original, target_media_id):
6
+ def find_media_index(upload_original, target_media_id, key_name="media_id"):
7
7
  for index, item in enumerate(upload_original):
8
- if item.get("media_id") == target_media_id:
8
+ if item.get(key_name) == target_media_id:
9
9
  return index # 返回找到的索引
10
10
  return -1
@@ -6,13 +6,14 @@ from podflow.basic.file_save import file_save
6
6
  from podflow.upload.find_media_index import find_media_index
7
7
 
8
8
 
9
- def store_users_info(username,filename):
9
+ def store_users_info(username, filename, channelid):
10
10
  index = find_media_index(gVar.upload_message, filename)
11
11
  if index == -1:
12
12
  gVar.upload_message.append(
13
13
  {
14
- "media_id": filename,
15
- "users": [username]
14
+ "mediaid": filename,
15
+ "users": [username],
16
+ "channelid": channelid,
16
17
  }
17
18
  )
18
19
  elif username not in gVar.upload_message[index]["users"]:
podflow/youtube/check.py CHANGED
@@ -5,6 +5,7 @@ import os
5
5
  import yt_dlp
6
6
  from podflow.basic.write_log import write_log
7
7
  from podflow.basic.time_print import time_print
8
+ from podflow.message.fail_message_initialize import fail_message_initialize
8
9
 
9
10
 
10
11
  # yt-dlp校验cookie模块
@@ -43,6 +44,7 @@ def yt_dlp_check(file, url):
43
44
  return False
44
45
  except yt_dlp.utils.DownloadError as e:
45
46
  error_message = str(e).lower()
47
+ e = fail_message_initialize(e, "WL")
46
48
  if any(
47
49
  keyword in error_message
48
50
  for keyword in ["login required", "sign in", "private", "forbidden"]
@@ -52,6 +54,7 @@ def yt_dlp_check(file, url):
52
54
  time_print(f"cookie无效或网络异常\n{e}")
53
55
  return False
54
56
  except Exception as e:
57
+ e = fail_message_initialize(e, "WL")
55
58
  time_print(f"cookie发生未知错误\n{e}")
56
59
  return False
57
60
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250706
3
+ Version: 20250803
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
@@ -14,7 +14,7 @@ Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  Requires-Dist: astral>=3.2
16
16
  Requires-Dist: bottle>=0.13.2
17
- Requires-Dist: yt-dlp>=2025.6.30
17
+ Requires-Dist: yt-dlp>=2025.7.21
18
18
  Requires-Dist: chardet>=5.2.0
19
19
  Requires-Dist: cherrypy>=18.10.0
20
20
  Requires-Dist: pyqrcode>=1.2.1
@@ -1,10 +1,10 @@
1
- podflow/__init__.py,sha256=hpC5LyzhH6gYwn2DRHUBjQY4eHADFA5oIDztzhES-Ew,7695
1
+ podflow/__init__.py,sha256=AYe5nm0FXsiKyny-6De6cGbNh2wHcsN5lEHK-TU6QzI,7719
2
2
  podflow/download_and_build.py,sha256=x7S7N26B1G9Yn2yr7YthDJgKUwEKBLtHBLlaqpofLas,746
3
3
  podflow/ffmpeg_judge.py,sha256=wM49pPXOFwFAA_8TKHal5fV6ka9sAA87yGQMDOssvXo,1340
4
4
  podflow/main.py,sha256=7zWdpw80jqPaYu1Un1nPqaZoiAb7Dqg8zaF-cUioU4c,755
5
- podflow/main_podcast.py,sha256=gH2ERZjZpPqaUnTajwGxnjMyV7dlrXyM7WJFoFXH3i4,12990
5
+ podflow/main_podcast.py,sha256=C99Jgt_AnOW_8fBmbpZUwHg1AR3T8eDRV9uxzrnYnCM,13303
6
6
  podflow/main_upload.py,sha256=VOihPtjr37lxRNEr2cJp70DB6W5q7lHvpanApFKA9qE,2595
7
- podflow/parse_arguments.py,sha256=h3a7EaRZS04kNMFYbxTW9Ch29KgZ7dyS-yqEEt_etQI,2592
7
+ podflow/parse_arguments.py,sha256=8t5_r0sfGFB5cSDE4qENlFSNUdMBeQRXIWJz6a9o9d8,2769
8
8
  podflow/basic/__init__.py,sha256=CAfI6mVQtz7KKbAiTIZ9_IbvaTXeAqxR1U7ov9GDoDo,44
9
9
  podflow/basic/file_save.py,sha256=6vu4EkbsN4df5-ci6sJOgIOUEhh-WaRBOyMJ8rpouXo,1233
10
10
  podflow/basic/folder_build.py,sha256=5oHyfiDcSp2YITXQWIPwriBF9XuU3qs7wZQOWJHYJ1s,546
@@ -28,7 +28,7 @@ podflow/bilibili/login.py,sha256=oHnuOMvlKZUnJ1OBHuZpTEnG04pboWsueOx871lQWjc,114
28
28
  podflow/config/__init__.py,sha256=MzgAlkSdV-5MFC11r0QSf-GPxmsLbrMc5ROIrJafjag,45
29
29
  podflow/config/build_original.py,sha256=pG9gCkBBIWwU8QxZA06Br6JRfI4XUSIEiN8eqo1-v7k,1809
30
30
  podflow/config/channge_icon.py,sha256=W-EU_5_wg94_yioNofk8OTyRD2X7SY8HEjvjFmSEyh0,5653
31
- podflow/config/correct_channelid.py,sha256=4Z6B3cR5wQq5Vce4LPGVDqqUGc-fW54B46w7f_XEKYc,9252
31
+ podflow/config/correct_channelid.py,sha256=RQMzeABdW1qYDIz-q1VMPCLTR2D82qlTay0GsRxHsZA,9301
32
32
  podflow/config/correct_config.py,sha256=slC3DYiapyZ8JO6xjDvNWQ5DBOZRvO7FvAQm-2J5H08,4121
33
33
  podflow/config/get_channelid.py,sha256=WUtvUZ1Q_L9n94ZGGaOqu-9QC2HlYMx5oU4gOCZ4_Ew,626
34
34
  podflow/config/get_channelid_id.py,sha256=VjExb_AXPHtQ5v8SaZiehaiLNkNmfMoPcv5HyUar6GU,571
@@ -41,7 +41,7 @@ podflow/download/show_progress.py,sha256=y46chchUC9eZCg4ZdNMFnx_bXJQV_IUq15jVzZt
41
41
  podflow/download/wait_animation.py,sha256=AUTvszXF89QA7XYjocFIauPKV7Qj8cFqry44teClaLQ,1314
42
42
  podflow/download/youtube_and_bilibili_download.py,sha256=VCEhz6pGXFWXusdbGWqkCzi4f4VsKQVn6sZz1pfGsns,1335
43
43
  podflow/httpfs/__init__.py,sha256=BxEXkufjcx-a0F7sDVXo65hmyANqCCbZUd6EH9i8T2c,45
44
- podflow/httpfs/app_bottle.py,sha256=RunI3OVcYelgTk8lkYRJwwF8O2RxHOurih7Xn1RBPxo,28885
44
+ podflow/httpfs/app_bottle.py,sha256=7xVaTmlSn4fyTr2CT3azqR5H6TVg-p0Lj-e5YBksjM4,35288
45
45
  podflow/httpfs/browser.py,sha256=BJ4Xkfiki_tDr0Sc9RqAcEfIVpkAZ3RFOwo0aMHlY3U,197
46
46
  podflow/httpfs/download_bar.py,sha256=0n3HATEO3pdsIpx-E_IZG9OlXa6u-9SeBCoZVgUutyc,965
47
47
  podflow/httpfs/get_channelid.py,sha256=gcwy4IVHBWNQz7qPCpjwiAklGFLRGzvM33-UZz7oFvo,2296
@@ -57,7 +57,7 @@ podflow/message/__init__.py,sha256=pZkcrrtkdtxgMriEHBZ0_rptKaQrQeMPJvPSaoI1Awo,4
57
57
  podflow/message/backup_zip_save.py,sha256=c81jnx8IxHjTcO7G0OUAppawpBIPxa9wgkj9AQhqeJc,1864
58
58
  podflow/message/create_main_rss.py,sha256=kW2QvJhxl2ZcqGV-M-OztlOaQ27ODuQxADeP8poymBQ,3118
59
59
  podflow/message/display_qrcode_and_url.py,sha256=VqmRkDYYG03VihfW4SAU49HJVmfqWbLTgMxqCaREeCo,1037
60
- podflow/message/fail_message_initialize.py,sha256=0Itu-7bJfdPI3D508TN4Tr5MgDA_Z4ZxmPyubXrkNEQ,7864
60
+ podflow/message/fail_message_initialize.py,sha256=J8jhKa1PVHKJ6TUUNmakBjD2dd6nMHs8sVkiBf9oXmM,8040
61
61
  podflow/message/format_time.py,sha256=gveNh4FGeS3ytwDyYB-h12d1_Km6XoX7WSPcFmDfCBk,909
62
62
  podflow/message/get_media_name.py,sha256=5ULPQOQCZ2-lxdkILwlBP-ItzdFEgvEAKxeLtplACbQ,861
63
63
  podflow/message/get_original_rss.py,sha256=Bzy-Fs1vZEjwvQq6D6xp-2IUidliSyaL1P4WtkLJaRg,2450
@@ -70,7 +70,7 @@ podflow/message/original_rss_fail_print.py,sha256=7HM5Gwi3GqBIg2dtTTDlN_FRgZZjYv
70
70
  podflow/message/rss_create_hash.py,sha256=M5OS9KcQ4mIxLes9ij4oNji-4VKgi56bg0Shv5nCIQ4,638
71
71
  podflow/message/save_rss.py,sha256=x-yRwT7bAUt2k-R9DWa5uToqpcOdaXkPW_4VH5Gbeo4,3193
72
72
  podflow/message/title_correction.py,sha256=Zieulj2wQY_o4r3u5ZRsDQP5y8KuZHrL_l8tnM96k6g,915
73
- podflow/message/update_information_display.py,sha256=Zn-Xhps4PKf7NbgQrT-qTwhP096RV-48OEncK_vuUe0,3061
73
+ podflow/message/update_information_display.py,sha256=IfNdr8KQlgoRpN7NSouHx-NXa9OxdXO0Pbck8xoqgh8,4370
74
74
  podflow/message/update_youtube_bilibili_rss.py,sha256=igt41NkGuikPzi0gqo-Hc29hMil65kHV19J4W-oGQfU,5962
75
75
  podflow/message/want_retry.py,sha256=3MtlAG4BZ2oznn0X5zYzAl2S0XzZkXhnN_LHVPcWZjA,699
76
76
  podflow/message/xml_item.py,sha256=jCB93aOoIDK6EaAFrZg5gd6mBMv7fP9uX-Z5eiTSyxg,3127
@@ -79,8 +79,8 @@ podflow/message/xml_rss.py,sha256=ogCteSUXyJJXLhOE7-ZBcRdWYzrRr2Qykjt3oppRpC4,16
79
79
  podflow/netscape/__init__.py,sha256=SUw_BtbV3moA324UdxRECkPLv1xHkjio8r_5JTkVfxI,47
80
80
  podflow/netscape/bulid_netscape.py,sha256=wmUPlDGF8G456GGyajU_6Ak5WJzsqsq4bZgPjCSTGhI,2279
81
81
  podflow/remove/__init__.py,sha256=x1pMfpIyE6xUrmIOkdl43mbvKLwndGo5pIoOBXhJsP4,45
82
- podflow/remove/remove_dir.py,sha256=zqgf47UgxiGiLipb1FeoWKzrSHSxcHaEuu0261bqR1s,1105
83
- podflow/remove/remove_file.py,sha256=ZusvZsBQX_yRdLuboD7bZKiOX4z4Rxg66zZK9KDWHwE,1006
82
+ podflow/remove/remove_dir.py,sha256=X-nxZq0cRkC3nHDfHLIGx0PDdzIGaM57GHy_LgA-8X0,3001
83
+ podflow/remove/remove_file.py,sha256=VgbO76525qmPkU0A0OgSTTtgLW2sVr8_bAfF2jckD8U,2961
84
84
  podflow/remove/remove_flush.py,sha256=HWCe5SjNJ3VXaXbgFtqGdTXeJ1R2vv5qllNilB-G0_g,1851
85
85
  podflow/repair/__init__.py,sha256=Gpc1i6xiSLodKjjmzH66c_Y1z0HQ9E9CS3p95FRnVFM,45
86
86
  podflow/repair/reverse_log.py,sha256=Wc_vAH0WB-z1fNdWx7FYaVH4caRPtot7tDwDwFhmpz4,1106
@@ -93,23 +93,23 @@ podflow/templates/js/qrcode.min.js,sha256=xUHvBjJ4hahBW8qN9gceFBibSFUzbe9PNttUve
93
93
  podflow/upload/__init__.py,sha256=AtOSXDrE5EjUe3z-iBd1NTDaH8n_X9qA5WXdBLkONjA,45
94
94
  podflow/upload/add_upload.py,sha256=LloIucGJSXARhxiWerAbJVW-FpTkTUxvYCAKPbl93Ew,1536
95
95
  podflow/upload/build_hash.py,sha256=9opa3xLd7nJbGGX5xa3uuKPS6dxlbkAb87ZdEiUxmxI,473
96
- podflow/upload/find_media_index.py,sha256=HAtKkp0e-mjqjyLv1jgxXcNlNYvz-RD_kWvGkFdLygY,299
96
+ podflow/upload/find_media_index.py,sha256=kJwk_iNOPt__7lyZiGBhsTM5-cK7Am7iKDUIjsXqa2Q,318
97
97
  podflow/upload/get_upload_original.py,sha256=TEDnRutumm2FZNIesPJIlExHyKWpfB3ZAHb3sZt7V6A,4312
98
98
  podflow/upload/linked_client.py,sha256=NdSi5uh0TbZUhOHbA_mkHo4aIz1XNKoSXHhT4rMRUpc,5288
99
99
  podflow/upload/linked_server.py,sha256=h-qSx13fP8_Ny2IKW3wCNPwqRqW6-Iz1pqxD9ga9-dM,2308
100
100
  podflow/upload/login.py,sha256=WwQoPCr1oSGZh6wEsEuUH5E8-PEYS_k36b-Qfe00rjY,4282
101
- podflow/upload/store_users_info.py,sha256=nnAUgPUStYR48ma1B8mtY9SFOr3rOGvnlCuKSNdee3M,679
101
+ podflow/upload/store_users_info.py,sha256=VO8eSUXU8steKW0QpUWJlSHRTvfHghSLZA_qIc67AlY,731
102
102
  podflow/upload/time_key.py,sha256=6jZ3cxUjzj_umYDwH27R0YNZlLXxfhNp-CqV_K22wlo,967
103
103
  podflow/upload/update_upload.py,sha256=tolV9WMRFg9KqdGSSC37REBy4N_f-d3GvCihciMlOlg,3456
104
104
  podflow/upload/upload_files.py,sha256=vI0sSjCxUILlu0K9doMLJpmR7KrqhMRsCJmcWrCKlA0,5564
105
105
  podflow/upload/upload_server.py,sha256=BFq3QrWE7U97LbC4EQiDhQXbLapEc4R00eRDBH12E6A,565
106
106
  podflow/youtube/__init__.py,sha256=pgXod8gq0IijZxIkPSwgAOcb9JI5rd1mqMomoR7bcJ4,46
107
107
  podflow/youtube/build.py,sha256=j6SVq3HFFGlNNqRrHfnBIThdzsH88PFmwLnejosif1U,12311
108
- podflow/youtube/check.py,sha256=VUu2h7o4p2yZ7sFLtHRmxojURbgwqQUgYVNPeyhZ0wc,2009
108
+ podflow/youtube/check.py,sha256=UTk5GFkhc_3l_3GfrJDZuAyJnG9Esc_v01x7EHm-aKM,2175
109
109
  podflow/youtube/get.py,sha256=oO32GjTFvUgP5AfFX5AlIuXU2UT6QtOUOXWLFzi8XtI,17157
110
110
  podflow/youtube/login.py,sha256=pDJNgCJdLOKV02yGot_5JJ-962JACbWZ-GlISmDfgIk,2742
111
- podflow-20250706.dist-info/METADATA,sha256=euTyT4mYua63OHU-BSgOkAn-XFTNASWnVrN2dIIOmNo,14195
112
- podflow-20250706.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
113
- podflow-20250706.dist-info/entry_points.txt,sha256=mn7hD_c_dmpKe3XU0KNekheBvD01LhlJ9htY-Df0j2A,131
114
- podflow-20250706.dist-info/top_level.txt,sha256=fUujhhz-RrMI8aGvi-3Ey5y7FQnpOOgoFw9OWM3yLCU,8
115
- podflow-20250706.dist-info/RECORD,,
111
+ podflow-20250803.dist-info/METADATA,sha256=JL-W9JhBRZyX1hhCTr13uiP7yXxRjxBKZc3_hHGv_Eg,14195
112
+ podflow-20250803.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
113
+ podflow-20250803.dist-info/entry_points.txt,sha256=mn7hD_c_dmpKe3XU0KNekheBvD01LhlJ9htY-Df0j2A,131
114
+ podflow-20250803.dist-info/top_level.txt,sha256=fUujhhz-RrMI8aGvi-3Ey5y7FQnpOOgoFw9OWM3yLCU,8
115
+ podflow-20250803.dist-info/RECORD,,