podflow 20250328__py3-none-any.whl → 20250330.1__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
@@ -119,6 +119,12 @@ class Application_gVar:
119
119
  self.upload_data = {} # 上传用户账号密码字典
120
120
 
121
121
  self.shortcuts_url = {} # 输出至shortcut的url字典
122
+
123
+ self.index_message = { # 图形界面显示信息字典
124
+ "podflow": [], # 主窗口信息列表
125
+ "http": [], # httpfs窗口信息列表
126
+ "enter": True, # 是否换行
127
+ }
122
128
 
123
129
 
124
130
  # 参数变量
@@ -2,14 +2,26 @@
2
2
  # coding: utf-8
3
3
 
4
4
  from datetime import datetime
5
+ from podflow import gVar
6
+ from podflow.httpfs.ansi_to_html import ansi_to_html
5
7
 
6
8
 
7
- def time_print(text, Top=False, Enter=False, Time=True):
9
+ def time_print(text, Top=False, NoEnter=False, Time=True):
8
10
  if Time:
9
11
  text = f"{datetime.now().strftime('%H:%M:%S')}|{text}"
10
12
  if Top:
11
13
  text = f"\r{text}"
12
- if Enter:
14
+ if NoEnter:
13
15
  print(text, end="")
14
16
  else:
15
17
  print(text)
18
+ text = ansi_to_html(text)
19
+ if not gVar.index_message["enter"] and gVar.index_message["podflow"]:
20
+ if Top:
21
+ gVar.index_message["podflow"][0] = text
22
+ else:
23
+ gVar.index_message["podflow"][0] += (text)
24
+ else:
25
+ gVar.index_message["podflow"].append(text)
26
+ if NoEnter:
27
+ gVar.index_message["enter"] = False
@@ -3,6 +3,8 @@
3
3
 
4
4
  import re
5
5
  from datetime import datetime
6
+ from podflow import gVar
7
+ from podflow.httpfs.ansi_to_html import ansi_to_html
6
8
 
7
9
 
8
10
  # 日志模块
@@ -34,4 +36,5 @@ def write_log(
34
36
  formatted_time_mini = current_time.strftime("%H:%M:%S")
35
37
  log_print = f"{formatted_time_mini}|{log}" if time_display else f"{log}"
36
38
  log_print = f"{log_print}|{suffix}" if suffix else f"{log_print}"
39
+ gVar.index_message["podflow"].append(ansi_to_html(log_print))
37
40
  print(log_print)
@@ -0,0 +1,52 @@
1
+ # podflow/httpfs/ansi_to_htmlpy
2
+ # coding: utf-8
3
+
4
+ import re
5
+ import html
6
+
7
+
8
+ def ansi_to_html(ansi_text):
9
+ html_output = ""
10
+ ansi_codes = {
11
+ "\033[30m": "color: black;", # 黑色
12
+ "\033[31m": "color: red;", # 红色
13
+ "\033[32m": "color: green;", # 绿色
14
+ "\033[33m": "color: yellow;", # 黄色
15
+ "\033[34m": "color: blue;", # 蓝色
16
+ "\033[35m": "color: magenta;", # 品红
17
+ "\033[36m": "color: cyan;", # 青色
18
+ "\033[37m": "color: white;", # 白色
19
+ "\033[90m": "color: gray;", # 亮黑色 (通常显示为灰色)
20
+ "\033[91m": "color: #ff69b4;", # 亮红色 (例如:热粉色)
21
+ "\033[92m": "color: #90ee90;", # 亮绿色 (例如:浅绿色)
22
+ "\033[93m": "color: #ffff00;", # 亮黄色 (通常与黄色相同)
23
+ "\033[94m": "color: #add8e6;", # 亮蓝色 (例如:浅蓝色)
24
+ "\033[95m": "color: #ff00ff;", # 亮品红 (通常与品红相同)
25
+ "\033[96m": "color: #00ffff;", # 亮青色 (通常与青色相同)
26
+ "\033[97m": "color: #f0f8ff;", # 亮白色 (例如:爱丽丝蓝)
27
+ "\033[0m": "", # 重置
28
+ }
29
+ inside_span = False
30
+
31
+ parts = re.split(r"(\033\[\d+m)", ansi_text)
32
+
33
+ for part in parts:
34
+ if part in ansi_codes:
35
+ style = ansi_codes[part]
36
+ if style:
37
+ if inside_span:
38
+ html_output += "</span>"
39
+ html_output += f'<span style="{style}">'
40
+ inside_span = True
41
+ elif inside_span: # Reset code
42
+ html_output += "</span>"
43
+ inside_span = False
44
+ else:
45
+ escaped_part = html.escape(part)
46
+ html_output += escaped_part
47
+
48
+ if inside_span:
49
+ html_output += "</span>"
50
+
51
+ #html_output = html_output.replace("\n", "</p><p>")
52
+ return html_output
@@ -13,6 +13,7 @@ from podflow.basic.file_save import file_save
13
13
  from podflow.basic.write_log import write_log
14
14
  from podflow.upload.build_hash import build_hash
15
15
  from podflow.upload.time_key import check_time_key
16
+ from podflow.httpfs.ansi_to_html import ansi_to_html
16
17
  from podflow.httpfs.get_channelid import get_channelid
17
18
 
18
19
 
@@ -40,6 +41,7 @@ class bottle_app:
40
41
  self.app_bottle.route("/index", callback=self.index)
41
42
  self.app_bottle.route("/getid", method="POST", callback=self.getid)
42
43
  self.app_bottle.route("/<filename:path>", callback=self.serve_static)
44
+ self.app_bottle.route("/message", callback=self.message)
43
45
 
44
46
  # 设置日志文件名及写入判断
45
47
  def set_logname(self, logname="httpfs.log", http_fs=False):
@@ -92,7 +94,9 @@ class bottle_app:
92
94
  )
93
95
  for suffix in suffixs:
94
96
  filename = filename.replace(suffix, "")
95
- self.bottle_print.append(f"{now_time}|{client_ip} {filename} {status}")
97
+ bottle_text = f"{now_time}|{client_ip} {filename} {status}"
98
+ self.bottle_print.append(bottle_text)
99
+ gVar.index_message["http"].append(ansi_to_html(bottle_text))
96
100
 
97
101
  # CherryPy 服务器打印模块
98
102
  def cherry_print(self, flag_judgment=True):
@@ -376,16 +380,16 @@ class bottle_app:
376
380
  },
377
381
  }
378
382
 
383
+ # 使用pkg_resources获取模板文件路径
379
384
  def index(self):
380
- # 使用pkg_resources获取模板文件路径
381
385
  template_path = pkg_resources.resource_filename('podflow', 'templates/index.html')
382
386
  with open(template_path, 'r', encoding="UTF-8") as f:
383
387
  html_content = f.read()
384
388
  self.print_out("index", 200)
385
389
  return html_content
386
390
 
391
+ # 获取 JSON 数据,Bottle 会自动解析请求体中的 JSON 数据
387
392
  def getid(self):
388
- # 获取 JSON 数据,Bottle 会自动解析请求体中的 JSON 数据
389
393
  if getid_data := request.json:
390
394
  content = getid_data.get("content", "")
391
395
  else:
@@ -396,5 +400,10 @@ class bottle_app:
396
400
  response.content_type = 'application/json'
397
401
  return {"response": response_message}
398
402
 
403
+ # 处理消息的接收和发送。
404
+ def message(self):
405
+ response.content_type = 'application/json'
406
+ return gVar.index_message # 获取消息列表
407
+
399
408
 
400
409
  bottle_app_instance = bottle_app()
podflow/main_podcast.py CHANGED
@@ -120,8 +120,12 @@ def main_podcast():
120
120
  gVar.xmls_original, gVar.hash_rss_original, gVar.xmls_original_fail = (
121
121
  get_original_rss()
122
122
  )
123
+ # 暂停进程打印
124
+ gVar.server_process_print_flag[0] = "pause"
123
125
  # 连接上传服务器
124
126
  upload_url = connect_upload_server()
127
+ # 恢复进程打印
128
+ bottle_app_instance.cherry_print()
125
129
  # 登陆上传服务器
126
130
  if upload_url:
127
131
  upload_json = login_upload(upload_url)
@@ -31,9 +31,9 @@ def makeup_format(video_id, makeup_format_lock):
31
31
  "channel_data/yt_dlp_youtube.txt",
32
32
  )
33
33
  if fail_info in makeup_id_format:
34
- makeup_id_format = f"\x1b[31m{fail_info}\x1b[0m(Cookies错误)"
34
+ makeup_id_format = f"\033[31m{fail_info}\033[0m(Cookies错误)"
35
35
  else:
36
- makeup_id_format = f"\x1b[31m{fail_info}\x1b[0m(需要Cookies)"
36
+ makeup_id_format = f"\033[31m{fail_info}\033[0m(需要Cookies)"
37
37
  break
38
38
  if isinstance(makeup_id_format, list):
39
39
  if len(makeup_id_format) == 1:
@@ -53,15 +53,15 @@ def get_youtube_and_bilibili_video_format(
53
53
  gVar.video_id_update_format[id_num]["cookie"],
54
54
  )
55
55
  if fail_info in id_update_format:
56
- id_update_format = f"\x1b[31m{fail_info}\x1b[0m(Cookies错误)"
56
+ id_update_format = f"\033[31m{fail_info}\033[0m(Cookies错误)"
57
57
  else:
58
- id_update_format = f"\x1b[31m{fail_info}\x1b[0m(需要Cookies)"
58
+ id_update_format = f"\033[31m{fail_info}\033[0m(需要Cookies)"
59
59
  break
60
60
  else:
61
61
  if gVar.video_id_update_format[id_num]["power"] is True and (
62
62
  "试看" in id_update_format or id_update_format == "无法获取音频ID"
63
63
  ):
64
- id_update_format = "\x1b[31m充电专属\x1b[0m"
64
+ id_update_format = "\033[31m充电专属\033[0m"
65
65
  if isinstance(id_update_format, list):
66
66
  if len(id_update_format) == 1:
67
67
  one_format(id_update_format, id_num)
@@ -3,62 +3,155 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>输入输出示例</title>
6
+ <title>Podflow</title>
7
7
  <style>
8
8
  /* 定义颜色变量 - 浅色模式 */
9
9
  :root {
10
10
  --bg-color: #f8f9fa;
11
- --text-color: #333;
12
- --secondary-text: #666;
11
+ --text-color: #333333;
12
+ --secondary-text: #666666;
13
13
  --input-bg: #ffffff;
14
- --input-border: #ccc;
14
+ --input-border: #cccccc;
15
15
  --button-bg: #007bff;
16
16
  --button-hover: #0056b3;
17
- --button-text: white;
18
- --button-shadow: rgba(0, 0, 0, 0.2);
17
+ --button-text: #ffffff;
18
+ --button-shadow: hsla(0, 0%, 0%, 0.20);
19
+ --menu-bg: #f0f0f0;
20
+ --menu-text: #333333;
21
+ --menu-width: 170px;
22
+ --menu-selected-bg: #cccccc;
23
+ .ansi-black { color: black; }
24
+ .ansi-red { color: red; }
25
+ .ansi-green { color: green; }
26
+ .ansi-yellow { color: yellow; }
27
+ .ansi-blue { color: blue; }
28
+ .ansi-magenta { color: magenta; }
29
+ .ansi-cyan { color: cyan; }
30
+ .ansi-white { color: white; }
31
+ .ansi-bright-black { color: gray; }
32
+ .ansi-bright-red { color: #ff69b4; }
33
+ .ansi-bright-green { color: #90ee90; }
34
+ .ansi-bright-yellow { color: #ffff00; }
35
+ .ansi-bright-blue { color: #add8e6; }
36
+ .ansi-bright-magenta { color: #ff00ff; }
37
+ .ansi-bright-cyan { color: #00ffff; }
38
+ .ansi-bright-white { color: #f0f8ff; }
19
39
  }
40
+
20
41
  /* 深色模式变量 */
21
42
  @media (prefers-color-scheme: dark) {
22
43
  :root {
23
- --bg-color: #222;
44
+ --bg-color: #222222;
24
45
  --text-color: #e0e0e0;
25
- --secondary-text: #aaa;
26
- --input-bg: #333;
27
- --input-border: #555;
46
+ --secondary-text: #aaaaaa;
47
+ --input-bg: #333333;
48
+ --input-border: #555555;
28
49
  --button-bg: #0062cc;
29
50
  --button-hover: #0069d9;
30
51
  --button-text: #f0f0f0;
31
- --button-shadow: rgba(0, 0, 0, 0.4);
52
+ --button-shadow: hsla(0, 0%, 0%, 0.40);
53
+ --menu-bg: #333333;
54
+ --menu-text: #e0e0e0;
55
+ --menu-selected-bg: #555555;
56
+ .ansi-black { color: #d3d3d3; /* LightGray */ }
57
+ .ansi-red { color: #ff4d4d; /* Lighter Red */ }
58
+ .ansi-green { color: #98fb98; /* PaleGreen */ }
59
+ .ansi-yellow { color: #ffff66; /* Lighter Yellow */ }
60
+ .ansi-blue { color: #87cefa; /* LightSkyBlue */ }
61
+ .ansi-magenta { color: #ff80ff; /* Lighter Magenta */ }
62
+ .ansi-cyan { color: #00ced1; /* DarkTurquoise */ }
63
+ .ansi-white { color: #333; /* Dark Gray */ }
64
+ .ansi-bright-black { color: #a9a9a9; /* DarkGray */ }
65
+ .ansi-bright-red { color: #ff8080; /* Brighter Red */ }
66
+ .ansi-bright-green { color: #b0f0b0; /* Brighter Green */ }
67
+ .ansi-bright-yellow { color: #ffff80; /* Brighter Yellow */ }
68
+ .ansi-bright-blue { color: #a0cfff; /* Brighter Blue */ }
69
+ .ansi-bright-magenta { color: #ff80ff; /* Brighter Magenta */ }
70
+ .ansi-bright-cyan { color: #80ffff; /* Brighter Cyan */ }
71
+ .ansi-bright-white { color: #fff; /* White */ }
32
72
  }
33
73
  }
74
+
34
75
  /* 基本样式 */
35
76
  body {
36
77
  font-family: Arial, sans-serif;
37
- text-align: center;
38
- padding: 20px;
39
78
  background-color: var(--bg-color);
40
79
  color: var(--text-color);
41
80
  transition: background-color 0.3s, color 0.3s;
81
+ margin: 0;
82
+ display: flex;
42
83
  }
43
- h2 {
44
- color: var(--text-color);
84
+ nav {
85
+ width: var(--menu-width);
86
+ background-color: var(--menu-bg);
87
+ color: var(--menu-text);
88
+ height: 100vh;
89
+ position: sticky;
90
+ top: 0;
91
+ z-index: 1000;
92
+ transition: width 0.3s, padding 0.3s, color 0.3s;
45
93
  }
46
- /* 响应式输入框 */
47
- textarea {
48
- width: 90%;
49
- max-width: 600px;
50
- height: 250px;
51
- font-size: 16px;
52
- padding: 10px;
94
+ nav.hidden {
95
+ width: 0;
96
+ padding: 0;
97
+ overflow: hidden;
98
+ }
99
+ nav ul {
100
+ list-style: none;
101
+ padding: 0;
102
+ }
103
+ nav h3 {
104
+ text-align: center;
105
+ margin: 20px 0 0;
106
+ font-size: 20px;
107
+ }
108
+ nav li {
109
+ margin: 5px 0;
110
+ cursor: pointer;
111
+ transition: background-color 0.3s, color 0.3s;
112
+ padding: 0 20px;
113
+ height: 40px;
114
+ line-height: 40px;
115
+ }
116
+ nav li:hover {
117
+ background-color: var(--menu-selected-bg);
118
+ color: var(--button-hover);
119
+ }
120
+ main {
121
+ flex: 1;
122
+ padding: 40px;
123
+ max-width: 520px;
124
+ transition: margin-left 0.3s;
125
+ }
126
+ main.full {
127
+ margin-left: 0;
128
+ }
129
+
130
+ /* 表单与消息区域共用样式 */
131
+ .common-area {
132
+ width: 100%;
133
+ max-width: 520px;
134
+ font-size: 16px;
135
+ padding: 2px;
53
136
  border: 1px solid var(--input-border);
54
- border-radius: 8px;
55
- resize: vertical;
56
- overflow-y: auto;
137
+ border-radius: 4px;
57
138
  background-color: var(--input-bg);
58
139
  color: var(--text-color);
59
140
  transition: background-color 0.3s, color 0.3s, border-color 0.3s;
141
+ box-sizing: border-box;
142
+ line-height: 1.1;
143
+ overflow-x: auto; /* 当内容超出宽度时显示水平滚动条(可选) */
144
+ overflow-y: auto;
145
+ }
146
+ textarea {
147
+ height: 250px;
148
+ }
149
+ #messageArea {
150
+ height: 250px;
151
+ }
152
+ #messageHttp {
153
+ height: 150px;
60
154
  }
61
- /* 按钮样式 */
62
155
  .button-container {
63
156
  margin-top: 10px;
64
157
  }
@@ -70,124 +163,316 @@
70
163
  font-size: 16px;
71
164
  border-radius: 6px;
72
165
  cursor: pointer;
73
- transition: 0.3s;
166
+ transition: background-color 0.3s, box-shadow 0.3s;
74
167
  box-shadow: 2px 2px 8px var(--button-shadow);
75
168
  margin: 5px;
76
169
  }
77
170
  button:hover {
78
171
  background-color: var(--button-hover);
79
172
  }
173
+ .hint {
174
+ font-size: 14px;
175
+ color: var(--secondary-text);
176
+ margin-top: 10px;
177
+ }
178
+
179
+ /* 菜单切换按钮 */
180
+ #toggleMenu {
181
+ width: 35px;
182
+ height: 40px;
183
+ position: fixed;
184
+ left: var(--menu-width);
185
+ top: 5px;
186
+ background: var(--menu-bg);
187
+ border: none;
188
+ font-size: 20px;
189
+ color: var(--text-color);
190
+ cursor: pointer;
191
+ transition: left 0.3s, color 0.3s;
192
+ border-radius: 0 10px 10px 0;
193
+ display: flex;
194
+ justify-content: center;
195
+ align-items: center;
196
+ box-shadow: 0px 0px 8px var(--button-shadow);
197
+ margin: 0;
198
+ }
199
+ #toggleMenu:hover {
200
+ color: var(--button-hover);
201
+ }
202
+
80
203
  /* 手机端优化 */
81
204
  @media (max-width: 600px) {
205
+ #messageArea, textarea {
206
+ max-width: none;
207
+ }
82
208
  textarea {
83
209
  font-size: 18px;
84
- height: 180px;
85
210
  }
86
211
  button {
87
212
  width: 90%;
88
213
  font-size: 18px;
89
- padding: 14px;
90
214
  }
91
- }
92
- /* 提示信息 */
93
- .hint {
94
- font-size: 14px;
95
- color: var(--secondary-text);
96
- margin-top: 10px;
215
+ nav {
216
+ position: fixed;
217
+ }
97
218
  }
98
219
  </style>
99
220
  </head>
100
221
  <body>
101
- <h2>获取 Channel-ID</h2>
102
- <!-- 将表单的 action 指定为 channelid -->
103
- <form id="inputForm" method="post" action="getid">
104
- <label for="inputOutput">请输入:</label><br>
105
- <textarea name="inputOutput" id="inputOutput"></textarea><br>
106
- <div class="button-container">
107
- <button type="button" onclick="pasteFromClipboard()">📋 粘贴</button>
108
- <button type="submit">✅ 提交</button>
109
- <button type="button" onclick="copyText()">📄 拷贝</button>
110
- <button type="button" onclick="clearInput()">🗑️ 清空</button>
111
- </div>
112
- <p class="hint">📌 如果粘贴按钮无效,请长按输入框手动粘贴。</p>
113
- </form>
222
+ <!-- 菜单栏 -->
223
+ <nav id="menu">
224
+ <h3>菜单栏</h3>
225
+ <ul>
226
+ <li data-page="pageMessage">Podflow 运行情况</li>
227
+ <li data-page="pageChannel">获取媒体频道 ID</li>
228
+ </ul>
229
+ </nav>
230
+ <!-- 菜单切换按钮 -->
231
+ <button id="toggleMenu">❮</button>
232
+ <!-- 主体区域 -->
233
+ <main id="main">
234
+ <!-- 获取 Channel-ID 页面 -->
235
+ <section id="pageChannel" style="display: none;">
236
+ <h2>获取媒体频道 ID</h2>
237
+ <form id="inputForm" method="post" action="getid">
238
+ <label for="inputOutput">请输入:</label><br>
239
+ <textarea class="common-area" name="inputOutput" id="inputOutput"></textarea><br>
240
+ <div class="button-container">
241
+ <button type="button" id="pasteBtn">📋 粘贴</button>
242
+ <button type="submit">✅ 提交</button>
243
+ <button type="button" id="copyBtn">📄 拷贝</button>
244
+ <button type="button" id="clearBtn">🗑️ 清空</button>
245
+ </div>
246
+ <p class="hint">📌 如果粘贴按钮无效,请长按输入框手动粘贴。</p>
247
+ </form>
248
+ </section>
249
+ <!-- 消息滚动显示页面 -->
250
+ <section id="pageMessage">
251
+ <h2>Podflow 运行情况</h2>
252
+ <form>
253
+ <label>构建服务:</label><br>
254
+ <div class="common-area" id="messageArea"></div>
255
+ <label>服务器:</label><br>
256
+ <div class="common-area" id="messageHttp"></div>
257
+ </form>
258
+ </section>
259
+ </main>
114
260
  <script>
115
- // 监听表单提交事件,阻止默认提交并使用 fetch 异步发送请求
116
- document.getElementById('inputForm').addEventListener('submit', function(event) {
117
- event.preventDefault();
118
- let inputOutput = document.getElementById('inputOutput');
119
- const content = inputOutput.value;
120
-
121
- fetch('getid', {
122
- method: 'POST',
123
- headers: {
124
- 'Content-Type': 'application/json'
125
- },
126
- body: JSON.stringify({ content: content })
127
- })
128
- .then(response => {
129
- if (!response.ok) {
130
- throw new Error('网络响应异常');
261
+ (function() {
262
+ // 缓存常用节点
263
+ const menu = document.getElementById('menu');
264
+ const toggleMenuBtn = document.getElementById('toggleMenu');
265
+ const mainArea = document.getElementById('main');
266
+ const pages = {
267
+ pageChannel: document.getElementById('pageChannel'),
268
+ pageMessage: document.getElementById('pageMessage')
269
+ };
270
+ const inputForm = document.getElementById('inputForm');
271
+ const inputOutput = document.getElementById('inputOutput');
272
+ const messageArea = document.getElementById('messageArea');
273
+ const messageHttp = document.getElementById('messageHttp');
274
+ const pasteBtn = document.getElementById('pasteBtn');
275
+ const copyBtn = document.getElementById('copyBtn');
276
+ const clearBtn = document.getElementById('clearBtn');
277
+
278
+ let pollingTimer = null;
279
+
280
+ // 菜单切换函数
281
+ function toggleMenu() {
282
+ menu.classList.toggle('hidden');
283
+ if (menu.classList.contains('hidden')) {
284
+ toggleMenuBtn.style.left = '0px';
285
+ toggleMenuBtn.textContent = '❯';
286
+ } else {
287
+ toggleMenuBtn.style.left = 'var(--menu-width)';
288
+ toggleMenuBtn.textContent = '❮';
131
289
  }
132
- return response.json();
133
- })
134
- .then(data => {
135
- // 假设服务端返回的 JSON 数据中包含 response 字段
136
- inputOutput.value = data.response;
137
- })
138
- .catch(error => {
139
- console.error('请求失败:', error);
140
- alert('请求失败,请稍后重试!');
141
- });
142
- });
143
-
144
- // 从剪贴板粘贴内容
145
- function pasteFromClipboard() {
146
- let inputOutput = document.getElementById('inputOutput');
147
- if (navigator.clipboard && navigator.clipboard.readText) {
148
- navigator.clipboard.readText().then(text => {
149
- inputOutput.value = text;
150
- inputOutput.focus();
151
- }).catch(err => {
152
- console.warn("剪贴板读取失败:", err);
153
- alert("无法读取剪贴板,请手动粘贴!");
154
- });
155
- } else {
156
- try {
157
- inputOutput.focus();
158
- document.execCommand('paste');
159
- } catch (err) {
160
- console.warn("execCommand 粘贴失败:", err);
161
- alert("您的浏览器不支持自动粘贴,请手动操作!");
290
+ }
291
+
292
+ // 根据页面标识显示对应面板
293
+ function showPage(pageId) {
294
+ Object.values(pages).forEach(page => page.style.display = 'none');
295
+ if (pages[pageId]) {
296
+ pages[pageId].style.display = 'block';
297
+ // 手机模式下自动隐藏菜单
298
+ if (window.innerWidth <= 600 && !menu.classList.contains('hidden')) {
299
+ toggleMenu();
300
+ }
301
+ pageId === 'pageMessage' ? startMessagePolling() : stopMessagePolling();
162
302
  }
163
303
  }
164
- }
165
304
 
166
- // 复制文本到剪贴板
167
- function copyText() {
168
- let inputOutput = document.getElementById('inputOutput');
169
- if (navigator.clipboard && navigator.clipboard.writeText) {
170
- navigator.clipboard.writeText(inputOutput.value).then(() => {
171
- // 已成功复制到剪贴板
172
- }).catch(err => {
173
- console.warn("复制失败:", err);
174
- alert("无法复制,请手动选择文本后按 Ctrl+C 复制!");
175
- });
176
- } else {
177
- try {
178
- inputOutput.select();
179
- document.execCommand('copy');
180
- } catch (err) {
181
- console.warn("execCommand 复制失败:", err);
182
- alert("您的浏览器不支持复制,请手动操作!");
305
+ // 初始化默认页面
306
+ showPage('pageMessage');
307
+
308
+ let lastMessage = { podflow: [], http: [] };
309
+ let userScrolled = false;
310
+
311
+ // 监听滚动事件,检测用户是否手动滚动
312
+ function onUserScroll(event) {
313
+ const element = event.target;
314
+ // 判断是否接近底部
315
+ const nearBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 10;
316
+ userScrolled = !nearBottom;
317
+ }
318
+
319
+ messageArea.addEventListener('scroll', onUserScroll);
320
+ messageHttp.addEventListener('scroll', onUserScroll);
321
+
322
+ // 轮询消息更新,更新 messageArea 与 messageHttp
323
+ function getMessages() {
324
+ fetch('message')
325
+ .then(response => response.json()) // 解析 JSON 数据
326
+ .then(data => {
327
+ if (JSON.stringify(data) !== JSON.stringify(lastMessage)) {
328
+ // 追加新消息
329
+ appendMessages(messageArea, data.podflow, lastMessage.podflow);
330
+ appendMessages(messageHttp, data.http, lastMessage.http);
331
+ lastMessage = data;
332
+ }
333
+ })
334
+ .catch(error => console.error('获取消息失败:', error));
335
+ }
336
+
337
+ function appendMessages(container, newMessages, oldMessages) {
338
+ // 判断当前是否在底部
339
+ const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10;
340
+
341
+ // 只追加新数据,避免重复渲染
342
+ if (newMessages.length === oldMessages.length && newMessages.length > 0) {
343
+ const lastNewMessage = newMessages[newMessages.length - 1];
344
+ const lastOldMessage = oldMessages[oldMessages.length - 1];
345
+ if (lastNewMessage !== lastOldMessage) {
346
+ const br = document.createElement('br'); // 创建 <br> 标签
347
+ const textNode = document.createTextNode(lastNewMessage); // 创建文本节点
348
+
349
+ // 获取容器的最后一个子元素
350
+ const lastChild = container.lastElementChild;
351
+
352
+ // 如果容器有子元素,则替换最后一个(这里逻辑可能需要调整,因为你不再替换 <p>)
353
+ if (lastChild) {
354
+ container.removeChild(lastChild); // 移除最后一个元素
355
+ container.appendChild(textNode); // 添加新的文本节点
356
+ container.appendChild(br); // 添加换行符
357
+ } else {
358
+ container.appendChild(textNode);
359
+ container.appendChild(br);
360
+ }
361
+ }
362
+ } else {
363
+ // 如果 newMessages 和 oldMessages 元素数量不一致,则执行原来的添加逻辑
364
+ newMessages.slice(oldMessages.length).forEach(msg => {
365
+ const br = document.createElement('br');
366
+ const textNode = document.createTextNode(msg);
367
+ container.appendChild(textNode);
368
+ container.appendChild(br);
369
+ });
370
+ }
371
+
372
+ // 如果用户没有主动滚动,才自动滚动到底部
373
+ if (!userScrolled) {
374
+ container.scrollTop = container.scrollHeight;
183
375
  }
184
376
  }
185
- }
186
377
 
187
- // 清空输入框内容
188
- function clearInput() {
189
- document.getElementById('inputOutput').value = '';
190
- }
378
+ function startMessagePolling() {
379
+ getMessages();
380
+ pollingTimer = setInterval(getMessages, 1000);
381
+ }
382
+
383
+ function stopMessagePolling() {
384
+ if (pollingTimer !== null) {
385
+ clearInterval(pollingTimer);
386
+ pollingTimer = null;
387
+ }
388
+ }
389
+
390
+ startMessagePolling();
391
+
392
+ // 表单异步提交(获取 Channel-ID)
393
+ inputForm.addEventListener('submit', function(event) {
394
+ event.preventDefault();
395
+ const content = inputOutput.value;
396
+ fetch('getid', {
397
+ method: 'POST',
398
+ headers: { 'Content-Type': 'application/json' },
399
+ body: JSON.stringify({ content })
400
+ })
401
+ .then(response => {
402
+ if (!response.ok) {
403
+ throw new Error('网络响应异常');
404
+ }
405
+ return response.json();
406
+ })
407
+ .then(data => inputOutput.value = data.response)
408
+ .catch(error => {
409
+ console.error('请求失败:', error);
410
+ alert('请求失败,请稍后重试!');
411
+ });
412
+ });
413
+
414
+ // 粘贴功能
415
+ pasteBtn.addEventListener('click', function() {
416
+ if (navigator.clipboard && navigator.clipboard.readText) {
417
+ navigator.clipboard.readText().then(text => {
418
+ inputOutput.value = text;
419
+ inputOutput.focus();
420
+ }).catch(err => {
421
+ console.warn("剪贴板读取失败:", err);
422
+ alert("无法读取剪贴板,请手动粘贴!");
423
+ });
424
+ } else {
425
+ try {
426
+ inputOutput.focus();
427
+ document.execCommand('paste');
428
+ } catch (err) {
429
+ console.warn("execCommand 粘贴失败:", err);
430
+ alert("您的浏览器不支持自动粘贴,请手动操作!");
431
+ }
432
+ }
433
+ });
434
+
435
+ // 复制功能
436
+ copyBtn.addEventListener('click', function() {
437
+ if (navigator.clipboard && navigator.clipboard.writeText) {
438
+ navigator.clipboard.writeText(inputOutput.value).catch(err => {
439
+ console.warn("复制失败:", err);
440
+ alert("无法复制,请手动选择文本后按 Ctrl+C 复制!");
441
+ });
442
+ } else {
443
+ try {
444
+ inputOutput.select();
445
+ document.execCommand('copy');
446
+ } catch (err) {
447
+ console.warn("execCommand 复制失败:", err);
448
+ alert("您的浏览器不支持复制,请手动操作!");
449
+ }
450
+ }
451
+ });
452
+
453
+ // 清空输入框
454
+ clearBtn.addEventListener('click', function() {
455
+ inputOutput.value = '';
456
+ });
457
+
458
+ // 菜单项点击事件委托
459
+ menu.addEventListener('click', function(event) {
460
+ const target = event.target;
461
+ if (target.tagName.toLowerCase() === 'li' && target.dataset.page) {
462
+ showPage(target.dataset.page);
463
+ }
464
+ });
465
+
466
+ // 菜单切换按钮事件绑定
467
+ toggleMenuBtn.addEventListener('click', toggleMenu);
468
+
469
+ // 针对手机端,初始化时隐藏菜单
470
+ if (window.innerWidth <= 600) {
471
+ menu.classList.add('hidden');
472
+ toggleMenuBtn.style.left = '0px';
473
+ toggleMenuBtn.textContent = '❯';
474
+ }
475
+ })();
191
476
  </script>
192
477
  </body>
193
478
  </html>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: podflow
3
- Version: 20250328
3
+ Version: 20250330.1
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
@@ -1,8 +1,8 @@
1
- podflow/__init__.py,sha256=TlmiRQFXGXAP5WK6i_xjDCWtzj__MfkzK7S8ofjbEW4,6991
1
+ podflow/__init__.py,sha256=N8hpfDHmVdVyDQs50yt_EMscS2v6CTD5a8oBWUkUOf0,7222
2
2
  podflow/download_and_build.py,sha256=GKQ1uX8Nuwdhr4wgnGr3jP1Mu0llRUPFcboQ3S05WkU,671
3
3
  podflow/ffmpeg_judge.py,sha256=3EtNlHhinsFPtRp74JwCK2TFM6VOjsvpn7rW0OhEMi8,1275
4
4
  podflow/main.py,sha256=Cz2E33-Kcc_1_oxNs4Z1OoqJYhonmClsrtoCW1oQmZA,739
5
- podflow/main_podcast.py,sha256=z8aIxbZo0Z3BNjdYgO5EFVpogsS1oVOt-T7bg_uQww4,8874
5
+ podflow/main_podcast.py,sha256=ytBL4LsGLttOFrJ9MXjMR1EBeTtoGjA78KQleA7rKwQ,9027
6
6
  podflow/main_upload.py,sha256=H_T5KQMYzToqzQbjGQ6DWDGziy8iMnpmf7A1qOStJuo,2296
7
7
  podflow/parse_arguments.py,sha256=h3a7EaRZS04kNMFYbxTW9Ch29KgZ7dyS-yqEEt_etQI,2592
8
8
  podflow/basic/__init__.py,sha256=CAfI6mVQtz7KKbAiTIZ9_IbvaTXeAqxR1U7ov9GDoDo,44
@@ -16,10 +16,10 @@ podflow/basic/list_merge_tidy.py,sha256=7hWfSnsPh23edHNU92vxtI0nfpfN8m54GTEt2rGm
16
16
  podflow/basic/qr_code.py,sha256=PvtCIq1THH72-xfxmhlI4Lz3ncE04po4XfH2XSjbhm0,1520
17
17
  podflow/basic/split_dict.py,sha256=Ir6GTortcWMUeFITFgY1v-INMla5y0IN3RN3nTgzWqM,401
18
18
  podflow/basic/time_format.py,sha256=T3tw2vbOwxMYYXDaV4Sj76WOZtsspj2lWA_DzWqUEJA,487
19
- podflow/basic/time_print.py,sha256=CMaOC1jVNKTZwJGulVExjVpvQDWFEnZVGEfQeIjGlIg,323
19
+ podflow/basic/time_print.py,sha256=-OcqzMsvPqAp7HOCrpK6435DEWBQ5zP-s7EtbLctPTw,767
20
20
  podflow/basic/time_stamp.py,sha256=Kbz9PzgPtss1fRqPXdfz1q6MTGVMRi3LPClN7wrXSIk,1888
21
21
  podflow/basic/vary_replace.py,sha256=-TyvZxfak6U7Ak8F87ctYUBpHB2Il6iYZof37lwKjto,211
22
- podflow/basic/write_log.py,sha256=TYmkkjclJQ-foJcveaEZj43zozSeqcIRTbsS2pIaMvg,1169
22
+ podflow/basic/write_log.py,sha256=3m7S1G2rqe1gKz4ErI1ay17rymxmPOuHMC1IYh2BTzo,1317
23
23
  podflow/bilibili/__init__.py,sha256=6ZTpvhZTyn4f0LryXzH8lzyK1_rRHBeM-hkBoklKHRA,47
24
24
  podflow/bilibili/build.py,sha256=PKxkjUa8EedpoQTFZIgdcsVbiAWRISA-qKCpCaSTLTU,7957
25
25
  podflow/bilibili/get.py,sha256=Ld8lhb_3eiSagVXWyj1KIrRLjfH6p0sUolEG5VtN8mk,20337
@@ -40,14 +40,15 @@ podflow/download/show_progress.py,sha256=cWlyIh6WqoH3s4WpFSyL6xtiXhQFbrVJVcckxO-
40
40
  podflow/download/wait_animation.py,sha256=RnByMq0Ql_yr9OqQ3dl3W-41GgP0T8PvlbwwVeMb7_k,1056
41
41
  podflow/download/youtube_and_bilibili_download.py,sha256=dlUh9cPHrYgZAhtXlBUOdFIpZOhv9xOtgdcTaqUvLuY,1294
42
42
  podflow/httpfs/__init__.py,sha256=BxEXkufjcx-a0F7sDVXo65hmyANqCCbZUd6EH9i8T2c,45
43
- podflow/httpfs/app_bottle.py,sha256=dxoAkSpIhT4ddbGA9uclzMdvYyu44YF9FLFW3vqRsHI,16405
43
+ podflow/httpfs/ansi_to_html.py,sha256=tdTDGBIvvlRXyN4fe6s5s8Fo74s57RXNApWitFs99TU,1863
44
+ podflow/httpfs/app_bottle.py,sha256=XozAAbcV1kikS2NISKcN0UI7d6at1hpi8pBpjUoUvls,16792
44
45
  podflow/httpfs/browser.py,sha256=BJ4Xkfiki_tDr0Sc9RqAcEfIVpkAZ3RFOwo0aMHlY3U,197
45
46
  podflow/httpfs/get_channelid.py,sha256=gcwy4IVHBWNQz7qPCpjwiAklGFLRGzvM33-UZz7oFvo,2296
46
47
  podflow/httpfs/port_judge.py,sha256=l_nLpsSiIhAzfJGCOWyYC-KkCCWPUW2ybe_5hdMOEzE,764
47
48
  podflow/makeup/__init__.py,sha256=ligUtfj0stTcOHFXDF6eAN7Up2PxlB0GnGbLq7iDY3c,45
48
49
  podflow/makeup/del_makeup_format_fail.py,sha256=XizQh74QYXxRg0e1uerXjd4Tiq5qChks0fTAY7n3Z1I,642
49
50
  podflow/makeup/make_up_file.py,sha256=WJnKtdr6-CMEpxJAADCEhcyIwdUdisxbqXGmzo3R5KQ,2222
50
- podflow/makeup/make_up_file_format_mod.py,sha256=QLdGG2Mx8tJjy_ji6IUdga9ckAOEt9uumIbaOewvZGo,3613
51
+ podflow/makeup/make_up_file_format_mod.py,sha256=VlR4yG7N6C2laYIBKb7J6Alqq2_X5Y7kgXMQSN6lJ6E,3613
51
52
  podflow/makeup/make_up_file_mod.py,sha256=padTanSPw5Dysf_CcUUVy65nCC6zbz1gPJRX98tmUdY,1075
52
53
  podflow/message/__init__.py,sha256=pZkcrrtkdtxgMriEHBZ0_rptKaQrQeMPJvPSaoI1Awo,46
53
54
  podflow/message/backup_zip_save.py,sha256=0SIxVvAtgSWAr_TvdXLVDIpxvXOHeFUN8n8QzfnIB84,1740
@@ -59,7 +60,7 @@ podflow/message/get_media_name.py,sha256=5ULPQOQCZ2-lxdkILwlBP-ItzdFEgvEAKxeLtpl
59
60
  podflow/message/get_original_rss.py,sha256=PYA7UGhHXbUN66vWmxWOp5Ns423CopHxw4vR19zXaPs,2383
60
61
  podflow/message/get_video_format.py,sha256=-LsgmawTKcDvR1wMgr3ue09fCHnmDvJNJxh7ztk-SZA,4770
61
62
  podflow/message/get_video_format_multithread.py,sha256=2t2UlBWG1RF0IRBBbGzeeWshWXfMgyny6keaWbmgTaI,1406
62
- podflow/message/get_youtube_and_bilibili_video_format.py,sha256=zX5fOFPET5_GWbHYGdd9RAj1-7AC_fKFu8eJcmuR4Fo,4439
63
+ podflow/message/get_youtube_and_bilibili_video_format.py,sha256=BFlplFy-j3J_2UVvmtNsfCK-Uvfjj3JZu8pscn1U0Ts,4439
63
64
  podflow/message/media_format.py,sha256=Q4WoML4UqL0Ry-QN8DHFJqOQ2tXcFN6u5hmhdSLdP1g,7346
64
65
  podflow/message/original_rss_fail_print.py,sha256=7HM5Gwi3GqBIg2dtTTDlN_FRgZZjYv6ejizS3tDiePE,502
65
66
  podflow/message/rss_create_hash.py,sha256=M5OS9KcQ4mIxLes9ij4oNji-4VKgi56bg0Shv5nCIQ4,638
@@ -79,7 +80,7 @@ podflow/remove/remove_dir.py,sha256=xQIhrnqnYjMzXjoSWaTvm7JwPYOFTN1muuTPdaLDXpQ,
79
80
  podflow/remove/remove_file.py,sha256=8wAJQehs-XBqvu0vPlEme2_tt0FZxc5ELwGMxXA_558,982
80
81
  podflow/repair/__init__.py,sha256=Gpc1i6xiSLodKjjmzH66c_Y1z0HQ9E9CS3p95FRnVFM,45
81
82
  podflow/repair/reverse_log.py,sha256=Wc_vAH0WB-z1fNdWx7FYaVH4caRPtot7tDwDwFhmpz4,1106
82
- podflow/templates/index.html,sha256=tQISpLqCwqLEa7mvbEBLr2MgwyUoq4I_5tLltjOrVe0,5722
83
+ podflow/templates/index.html,sha256=HxFnXOwR390eoPGTJzxRAnF5N1NjVBPH0x_gjkFKWmk,15589
83
84
  podflow/upload/__init__.py,sha256=AtOSXDrE5EjUe3z-iBd1NTDaH8n_X9qA5WXdBLkONjA,45
84
85
  podflow/upload/add_upload.py,sha256=_2-V0z75Lwu-PUCfMD9HOSxZTB102yZlZW5hSdlHcsc,1432
85
86
  podflow/upload/build_hash.py,sha256=9opa3xLd7nJbGGX5xa3uuKPS6dxlbkAb87ZdEiUxmxI,473
@@ -94,8 +95,8 @@ podflow/youtube/__init__.py,sha256=pgXod8gq0IijZxIkPSwgAOcb9JI5rd1mqMomoR7bcJ4,4
94
95
  podflow/youtube/build.py,sha256=3LYk_ICVXj9XkE9jZ8jEVI8596xxS_QZkcoIwcBE3Ys,12006
95
96
  podflow/youtube/get.py,sha256=Of7PRgUknhpyW70nvyVAUYVb5KyFViKiBTfH3Y6Mke8,16970
96
97
  podflow/youtube/login.py,sha256=3nLj0KLdsc-kQmXxG5FyZfR-kiDzbQy2J0KhPXMxJGc,1380
97
- podflow-20250328.dist-info/METADATA,sha256=uPhMooTmoizsHhuJv2otAUfdhbHns9yMXXcz8VP-Zao,14163
98
- podflow-20250328.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
99
- podflow-20250328.dist-info/entry_points.txt,sha256=mn7hD_c_dmpKe3XU0KNekheBvD01LhlJ9htY-Df0j2A,131
100
- podflow-20250328.dist-info/top_level.txt,sha256=fUujhhz-RrMI8aGvi-3Ey5y7FQnpOOgoFw9OWM3yLCU,8
101
- podflow-20250328.dist-info/RECORD,,
98
+ podflow-20250330.1.dist-info/METADATA,sha256=zWGWzIQYTCyQ287SptXAXAmA8dmulHZiLdZBphRC9Ns,14165
99
+ podflow-20250330.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
100
+ podflow-20250330.1.dist-info/entry_points.txt,sha256=mn7hD_c_dmpKe3XU0KNekheBvD01LhlJ9htY-Df0j2A,131
101
+ podflow-20250330.1.dist-info/top_level.txt,sha256=fUujhhz-RrMI8aGvi-3Ey5y7FQnpOOgoFw9OWM3yLCU,8
102
+ podflow-20250330.1.dist-info/RECORD,,