podflow 20250430__py3-none-any.whl → 20250522__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/basic/file_save.py +12 -8
- podflow/basic/http_client.py +5 -5
- podflow/httpfs/app_bottle.py +18 -3
- podflow/templates/css/config.css +225 -0
- podflow/templates/index.html +18 -6
- podflow/templates/js/config.js +832 -0
- podflow/upload/linked_client.py +64 -27
- podflow/upload/upload_files.py +58 -0
- {podflow-20250430.dist-info → podflow-20250522.dist-info}/METADATA +2 -2
- {podflow-20250430.dist-info → podflow-20250522.dist-info}/RECORD +13 -10
- {podflow-20250430.dist-info → podflow-20250522.dist-info}/WHEEL +0 -0
- {podflow-20250430.dist-info → podflow-20250522.dist-info}/entry_points.txt +0 -0
- {podflow-20250430.dist-info → podflow-20250522.dist-info}/top_level.txt +0 -0
podflow/basic/file_save.py
CHANGED
@@ -6,7 +6,7 @@ import json
|
|
6
6
|
|
7
7
|
|
8
8
|
# 文件保存模块
|
9
|
-
def file_save(content, file_name, folder=None):
|
9
|
+
def file_save(content, file_name, folder=None, binary=False):
|
10
10
|
# 如果指定了文件夹则将文件保存到指定的文件夹中
|
11
11
|
if folder:
|
12
12
|
file_path = os.path.join(os.getcwd(), folder, file_name)
|
@@ -14,10 +14,14 @@ def file_save(content, file_name, folder=None):
|
|
14
14
|
# 如果没有指定文件夹则将文件保存在当前工作目录中
|
15
15
|
file_path = os.path.join(os.getcwd(), file_name)
|
16
16
|
# 保存文件
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
|
17
|
+
if binary:
|
18
|
+
with open(file_path, "wb") as file:
|
19
|
+
file.write(content.read())
|
20
|
+
else:
|
21
|
+
with open(file_path, "w", encoding="utf-8") as file:
|
22
|
+
# 如果文件名中包含"."且文件类型为json,则将内容以json格式保存
|
23
|
+
if "." in file_name and file_name.split(".")[-1] == "json":
|
24
|
+
json.dump(content, file, ensure_ascii=False, indent=4)
|
25
|
+
else:
|
26
|
+
# 否则将内容以文本格式保存
|
27
|
+
file.write(content)
|
podflow/basic/http_client.py
CHANGED
@@ -16,7 +16,7 @@ def http_client(
|
|
16
16
|
cookies=None,
|
17
17
|
data=None,
|
18
18
|
mode="get",
|
19
|
-
|
19
|
+
file=None,
|
20
20
|
):
|
21
21
|
user_agent = {
|
22
22
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
|
@@ -55,10 +55,10 @@ def http_client(
|
|
55
55
|
try:
|
56
56
|
if mode.lower() != "post":
|
57
57
|
response = session.get(url, timeout=8)
|
58
|
-
elif
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
elif file:
|
59
|
+
file.seek(0)
|
60
|
+
files = {"file": file} # 这里 "file" 对应服务器端接收文件的字段名称
|
61
|
+
response = session.post(url, files=files, timeout=8)
|
62
62
|
else:
|
63
63
|
response = session.post(url, timeout=8)
|
64
64
|
response.raise_for_status()
|
podflow/httpfs/app_bottle.py
CHANGED
@@ -17,6 +17,7 @@ from podflow.basic.write_log import write_log
|
|
17
17
|
from podflow.httpfs.to_html import ansi_to_html
|
18
18
|
from podflow.upload.build_hash import build_hash
|
19
19
|
from podflow.upload.time_key import check_time_key
|
20
|
+
from podflow.basic.folder_build import folder_build
|
20
21
|
from podflow.httpfs.get_channelid import get_channelid
|
21
22
|
|
22
23
|
|
@@ -44,6 +45,7 @@ class bottle_app:
|
|
44
45
|
else:
|
45
46
|
self.app_bottle.route("/index", callback=self.index)
|
46
47
|
self.app_bottle.route("/getid", method="POST", callback=self.getid)
|
48
|
+
self.app_bottle.route("/getconfig", callback=self.getconfig)
|
47
49
|
self.app_bottle.route(
|
48
50
|
"/templates/<filepath:path>", callback=self.serve_template_file
|
49
51
|
)
|
@@ -346,11 +348,15 @@ class bottle_app:
|
|
346
348
|
"message": "No File Provided",
|
347
349
|
}
|
348
350
|
# 判断文件是否完整
|
349
|
-
|
351
|
+
uploadfile = upload_file.file
|
352
|
+
uploadfile.seek(0)
|
353
|
+
uploadfile_hash = build_hash(uploadfile)
|
354
|
+
if upload_hash != uploadfile_hash:
|
350
355
|
self.print_out("upload", 401)
|
351
356
|
return {
|
352
357
|
"code": -5,
|
353
358
|
"message": "Incomplete File",
|
359
|
+
"hash": uploadfile_hash,
|
354
360
|
}
|
355
361
|
if not channelid:
|
356
362
|
# 打印错误信息并返回错误码
|
@@ -369,7 +375,7 @@ class bottle_app:
|
|
369
375
|
"code": -6,
|
370
376
|
"message": "File Format Error",
|
371
377
|
}
|
372
|
-
address = f"
|
378
|
+
address = f"channel_audiovisual/{channelid}"
|
373
379
|
if os.path.exists(address):
|
374
380
|
file_list = os.listdir(address)
|
375
381
|
else:
|
@@ -380,6 +386,7 @@ class bottle_app:
|
|
380
386
|
filename = f"{name}.{num}.{suffix}"
|
381
387
|
if filename in file_list:
|
382
388
|
with open(f"{address}/{filename}", "rb") as original_file:
|
389
|
+
original_file.seek(0)
|
383
390
|
if upload_hash == build_hash(original_file):
|
384
391
|
self.print_out("upload", 200)
|
385
392
|
return {
|
@@ -391,7 +398,9 @@ class bottle_app:
|
|
391
398
|
}
|
392
399
|
num += 1
|
393
400
|
else:
|
394
|
-
|
401
|
+
folder_build(channelid, "channel_audiovisual")
|
402
|
+
uploadfile.seek(0)
|
403
|
+
file_save(uploadfile, filename, address, True)
|
395
404
|
# 打印成功信息并返回成功码
|
396
405
|
self.print_out("upload", 200)
|
397
406
|
return {
|
@@ -427,6 +436,12 @@ class bottle_app:
|
|
427
436
|
# 设置响应头为 application/json
|
428
437
|
response.content_type = "application/json"
|
429
438
|
return {"response": response_message}
|
439
|
+
|
440
|
+
def getconfig(self):
|
441
|
+
self.print_out("getconfig", 200)
|
442
|
+
# 设置响应头为 application/json
|
443
|
+
response.content_type = "application/json"
|
444
|
+
return {"response": gVar.config}
|
430
445
|
|
431
446
|
# --- 新增 SSE 流处理路由 ---
|
432
447
|
def stream(self):
|
@@ -0,0 +1,225 @@
|
|
1
|
+
/* /templates/css/config.css */
|
2
|
+
|
3
|
+
#pageConfig label {
|
4
|
+
display: block; /* 让标签独占一行 */
|
5
|
+
margin-top: 12px;
|
6
|
+
font-weight: bold;
|
7
|
+
color: #333;
|
8
|
+
}
|
9
|
+
|
10
|
+
#pageConfig input[type="text"],
|
11
|
+
#pageConfig input[type="number"],
|
12
|
+
#pageConfig input[type="url"],
|
13
|
+
#pageConfig select,
|
14
|
+
#pageConfig textarea {
|
15
|
+
width: 95%; /* 调整宽度 */
|
16
|
+
padding: 8px;
|
17
|
+
margin-top: 5px;
|
18
|
+
border: 1px solid #ccc;
|
19
|
+
border-radius: 4px;
|
20
|
+
box-sizing: border-box;
|
21
|
+
font-size: 1em;
|
22
|
+
}
|
23
|
+
|
24
|
+
#pageConfig input[type="checkbox"] {
|
25
|
+
margin-left: 10px;
|
26
|
+
vertical-align: middle; /* 垂直居中对齐 */
|
27
|
+
}
|
28
|
+
|
29
|
+
.config-item {
|
30
|
+
margin-bottom: 15px;
|
31
|
+
padding-bottom: 10px;
|
32
|
+
border-bottom: 1px dotted #eee; /* 分隔线 */
|
33
|
+
}
|
34
|
+
.config-item:last-child {
|
35
|
+
border-bottom: none;
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
/* 可折叠部分的样式 */
|
40
|
+
.collapsible-section {
|
41
|
+
border: 1px solid #e0e0e0;
|
42
|
+
margin-top: 15px;
|
43
|
+
border-radius: 5px;
|
44
|
+
background-color: #f9f9f9;
|
45
|
+
overflow: hidden; /* 防止子元素溢出圆角 */
|
46
|
+
}
|
47
|
+
|
48
|
+
.collapsible-header {
|
49
|
+
cursor: pointer;
|
50
|
+
font-weight: bold;
|
51
|
+
padding: 10px 15px;
|
52
|
+
background-color: #f0f0f0;
|
53
|
+
border-bottom: 1px solid #e0e0e0;
|
54
|
+
display: flex;
|
55
|
+
justify-content: space-between;
|
56
|
+
align-items: center;
|
57
|
+
transition: background-color 0.2s ease;
|
58
|
+
}
|
59
|
+
.collapsible-header:hover {
|
60
|
+
background-color: #e5e5e5;
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
.collapsible-header .channel-key {
|
65
|
+
color: #0056b3; /* 突出频道 Key */
|
66
|
+
}
|
67
|
+
|
68
|
+
.collapsible-header .channel-id {
|
69
|
+
font-size: 0.9em;
|
70
|
+
color: #555;
|
71
|
+
margin-left: 10px;
|
72
|
+
}
|
73
|
+
|
74
|
+
|
75
|
+
.collapsible-header .toggle-icon {
|
76
|
+
font-size: 1.2em;
|
77
|
+
transition: transform 0.3s ease; /* 添加旋转动画 */
|
78
|
+
}
|
79
|
+
|
80
|
+
.collapsible-content {
|
81
|
+
padding: 15px;
|
82
|
+
padding-top: 5px; /* 减少顶部内边距 */
|
83
|
+
border-top: 1px solid #eee; /* 在内容和头部之间加条细线 */
|
84
|
+
background-color: #ffffff;
|
85
|
+
/* display: none; 由 JS 控制 */
|
86
|
+
}
|
87
|
+
|
88
|
+
.collapsible-content.hidden {
|
89
|
+
display: none;
|
90
|
+
}
|
91
|
+
|
92
|
+
/* 旋转展开/收起图标 */
|
93
|
+
.collapsible-header.expanded .toggle-icon {
|
94
|
+
transform: rotate(90deg);
|
95
|
+
}
|
96
|
+
|
97
|
+
|
98
|
+
/* title_change 数组项样式 */
|
99
|
+
.title-change-list {
|
100
|
+
margin-top: 10px;
|
101
|
+
padding-left: 15px;
|
102
|
+
border-left: 2px solid #f0f0f0;
|
103
|
+
}
|
104
|
+
.title-change-item {
|
105
|
+
border: 1px dashed #ccc;
|
106
|
+
padding: 10px;
|
107
|
+
margin-top: 10px;
|
108
|
+
margin-bottom: 10px;
|
109
|
+
background-color: #fff;
|
110
|
+
border-radius: 4px;
|
111
|
+
}
|
112
|
+
.title-change-item > label { /* 嵌套标签样式调整 */
|
113
|
+
font-weight: normal;
|
114
|
+
font-size: 0.95em;
|
115
|
+
color: #444;
|
116
|
+
}
|
117
|
+
.title-change-item > input,
|
118
|
+
.title-change-item > select {
|
119
|
+
width: 90%; /* 稍微缩进 */
|
120
|
+
}
|
121
|
+
|
122
|
+
/* styles.css */
|
123
|
+
|
124
|
+
/* Button Styles */
|
125
|
+
.config-button {
|
126
|
+
margin-left: 10px;
|
127
|
+
padding: 3px 8px;
|
128
|
+
font-size: 0.9em;
|
129
|
+
cursor: pointer;
|
130
|
+
border: 1px solid #ccc;
|
131
|
+
border-radius: 4px;
|
132
|
+
background-color: #f0f0f0;
|
133
|
+
transition: background-color 0.2s ease;
|
134
|
+
vertical-align: middle; /* Align buttons vertically with text/elements */
|
135
|
+
}
|
136
|
+
|
137
|
+
.config-button:hover {
|
138
|
+
background-color: #e0e0e0;
|
139
|
+
}
|
140
|
+
|
141
|
+
.add-button {
|
142
|
+
background-color: #d4edda; /* Light green */
|
143
|
+
border-color: #c3e6cb;
|
144
|
+
color: #155724; /* Dark green */
|
145
|
+
}
|
146
|
+
|
147
|
+
.add-button:hover {
|
148
|
+
background-color: #c8e5bc;
|
149
|
+
}
|
150
|
+
|
151
|
+
.delete-button {
|
152
|
+
background-color: #f8d7da; /* Light red */
|
153
|
+
border-color: #f5c6cb;
|
154
|
+
color: #721c24; /* Dark red */
|
155
|
+
}
|
156
|
+
|
157
|
+
.delete-button:hover {
|
158
|
+
background-color: #f1b0b7;
|
159
|
+
}
|
160
|
+
|
161
|
+
/* Adjust layout for buttons in header */
|
162
|
+
.collapsible-header .config-button {
|
163
|
+
/* Specific adjustments for buttons inside headers */
|
164
|
+
margin-left: 10px; /* Space between title and buttons */
|
165
|
+
}
|
166
|
+
|
167
|
+
.title-change-list label .config-button {
|
168
|
+
margin-left: 10px;
|
169
|
+
}
|
170
|
+
|
171
|
+
.title-change-item strong .config-button {
|
172
|
+
margin-left: 10px;
|
173
|
+
}
|
174
|
+
|
175
|
+
/* Style for text inputs and selects */
|
176
|
+
.config-item input.config-input-text,
|
177
|
+
.config-item input[type="number"], /* Keep number type selector */
|
178
|
+
.config-item input[type="url"], /* Keep url type selector */
|
179
|
+
.config-item select.config-input-select {
|
180
|
+
width: calc(100% - 20px); /* Adjust width considering padding/margin */
|
181
|
+
padding: 5px;
|
182
|
+
margin-top: 5px;
|
183
|
+
border: 1px solid #ccc;
|
184
|
+
border-radius: 4px;
|
185
|
+
box-sizing: border-box; /* Include padding and border in element's total width and height */
|
186
|
+
display: block; /* Make inputs/selects take their own line */
|
187
|
+
}
|
188
|
+
|
189
|
+
.config-item input[type="checkbox"] {
|
190
|
+
width: auto; /* Checkbox should not take full width */
|
191
|
+
margin-top: 0;
|
192
|
+
display: inline-block; /* Checkbox stays inline with label */
|
193
|
+
}
|
194
|
+
|
195
|
+
/* Adjust display for channel key/ID display */
|
196
|
+
.channel-item .collapsible-header .channel-dom-key {
|
197
|
+
font-weight: bold;
|
198
|
+
}
|
199
|
+
|
200
|
+
.channel-item .collapsible-header .channel-id {
|
201
|
+
font-size: 0.9em;
|
202
|
+
color: #555;
|
203
|
+
margin-left: 5px;
|
204
|
+
}
|
205
|
+
|
206
|
+
.channel-item .collapsible-header .id-display {
|
207
|
+
font-weight: normal; /* Don't bold the displayed ID */
|
208
|
+
}
|
209
|
+
|
210
|
+
/* Basic styling for channel items */
|
211
|
+
.channel-item {
|
212
|
+
border: 1px solid #eee;
|
213
|
+
margin-bottom: 10px;
|
214
|
+
padding: 5px;
|
215
|
+
border-radius: 4px;
|
216
|
+
background-color: #f9f9f9;
|
217
|
+
}
|
218
|
+
|
219
|
+
.title-change-item {
|
220
|
+
border: 1px solid #eee;
|
221
|
+
margin-bottom: 10px;
|
222
|
+
padding: 5px;
|
223
|
+
border-radius: 4px;
|
224
|
+
background-color: #f9f9f9;
|
225
|
+
}
|
podflow/templates/index.html
CHANGED
@@ -5,22 +5,20 @@
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
6
|
<title>Podflow</title>
|
7
7
|
<link rel="stylesheet" href="/templates/css/index.css" />
|
8
|
+
<link rel="stylesheet" href="/templates/css/config.css" />
|
8
9
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
9
10
|
</head>
|
10
11
|
<body>
|
11
|
-
<!-- 菜单栏 -->
|
12
12
|
<nav id="menu">
|
13
13
|
<h3>菜单栏</h3>
|
14
14
|
<ul>
|
15
15
|
<li data-page="pageMessage">Podflow 运行情况</li>
|
16
16
|
<li data-page="pageChannel">获取媒体频道 ID</li>
|
17
|
+
<li data-page="pageConfig">修改配置</li>
|
17
18
|
</ul>
|
18
19
|
</nav>
|
19
|
-
<!-- 菜单切换按钮 -->
|
20
20
|
<button id="toggleMenu">❮</button>
|
21
|
-
<!-- 主体区域 -->
|
22
21
|
<main id="main">
|
23
|
-
<!-- 获取 Channel-ID 页面 -->
|
24
22
|
<section id="pageChannel" style="display: none;">
|
25
23
|
<h2>获取媒体频道 ID</h2>
|
26
24
|
<form id="inputForm" method="post" action="getid">
|
@@ -35,7 +33,7 @@
|
|
35
33
|
<p class="hint">📌 如果粘贴按钮无效,请长按输入框手动粘贴。</p>
|
36
34
|
</form>
|
37
35
|
</section>
|
38
|
-
|
36
|
+
|
39
37
|
<section id="pageMessage">
|
40
38
|
<h2>Podflow 运行情况</h2>
|
41
39
|
<form>
|
@@ -53,8 +51,22 @@
|
|
53
51
|
<div class="common-area" id="messageHttp"></div>
|
54
52
|
</form>
|
55
53
|
</section>
|
54
|
+
|
55
|
+
<section id="pageConfig" style="display: none;">
|
56
|
+
<h2>修改配置</h2>
|
57
|
+
<div id="configContainer">
|
58
|
+
<p>正在加载配置...</p>
|
59
|
+
</div>
|
60
|
+
<div class="button-container">
|
61
|
+
<button type="button" id="refreshConfigBtn">🔄 刷新配置</button>
|
62
|
+
<button type="button" id="saveConfigBtn">💾 保存配置</button>
|
63
|
+
</div>
|
64
|
+
<div id="configStatus" style="margin-top: 10px; color: green;"></div>
|
65
|
+
</section>
|
66
|
+
|
56
67
|
</main>
|
57
68
|
<script src="/templates/js/index.js"></script>
|
58
69
|
<script src="/templates/js/qrcode.min.js"></script>
|
70
|
+
<script src="/templates/js/config.js"></script>
|
59
71
|
</body>
|
60
|
-
</html>
|
72
|
+
</html>
|