Spaces:
Running
Running
admin
commited on
Commit
·
7cf86e5
1
Parent(s):
75ba758
sync ms
Browse files- .gitignore +2 -1
- app.py +64 -42
- config.py +0 -13
- data.py → modules/data.py +79 -48
- exif.py → modules/exif.py +99 -97
- gif.py → modules/gif.py +49 -45
- github.py → modules/github.py +46 -40
- modules/qr.py +48 -0
- rct.py → modules/rct.py +43 -23
- modules/smtp.py +69 -0
- trans.py → modules/trans.py +33 -13
- url.py → modules/url.py +51 -44
- qr.py +0 -21
- smtp.py +0 -53
- utils.py +71 -0
.gitignore
CHANGED
@@ -1,2 +1,3 @@
|
|
|
|
|
|
1 |
*__pycache__*
|
2 |
-
test.*
|
|
|
1 |
+
*.gif
|
2 |
+
test.*
|
3 |
*__pycache__*
|
|
app.py
CHANGED
@@ -1,42 +1,64 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
from data import data_converter
|
3 |
-
from exif import clexif
|
4 |
-
from gif import video2gif
|
5 |
-
from github import github_release_creator
|
6 |
-
from qr import qrcode
|
7 |
-
from rct import rct_generator
|
8 |
-
from smtp import smtp_tester
|
9 |
-
from trans import translator
|
10 |
-
from url import url_shortner
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from modules.data import data_converter
|
3 |
+
from modules.exif import clexif
|
4 |
+
from modules.gif import video2gif
|
5 |
+
from modules.github import github_release_creator
|
6 |
+
from modules.qr import qrcode
|
7 |
+
from modules.rct import rct_generator
|
8 |
+
from modules.smtp import smtp_tester
|
9 |
+
from modules.trans import translator
|
10 |
+
from modules.url import url_shortner
|
11 |
+
from utils import LANG
|
12 |
+
|
13 |
+
ZH2EN = {
|
14 |
+
"# 在线工具合集": "# Online Tools Collection",
|
15 |
+
"数据文件转换": "Data Converter",
|
16 |
+
"图片 EXIF 清理": "Image EXIF Cleaner",
|
17 |
+
"视频转 GIF 动图": "Video to GIF",
|
18 |
+
"GitHub 发布工具": "GitHub Releaser",
|
19 |
+
"二维码生成": "QR Code",
|
20 |
+
"随机对照试验生成": "RCT Generator",
|
21 |
+
"SMTP 测试": "SMTP Test",
|
22 |
+
"翻译器": "Translator",
|
23 |
+
"短链接生成": "URL Shortner",
|
24 |
+
}
|
25 |
+
|
26 |
+
|
27 |
+
def _L(zh_txt: str):
|
28 |
+
if LANG:
|
29 |
+
return ZH2EN[zh_txt]
|
30 |
+
else:
|
31 |
+
return zh_txt
|
32 |
+
|
33 |
+
|
34 |
+
if __name__ == "__main__":
|
35 |
+
with gr.Blocks() as demo:
|
36 |
+
gr.Markdown(_L("# 在线工具合集"))
|
37 |
+
with gr.Tab(_L("数据文件转换")):
|
38 |
+
data_converter()
|
39 |
+
|
40 |
+
with gr.Tab(_L("图片 EXIF 清理")):
|
41 |
+
clexif()
|
42 |
+
|
43 |
+
with gr.Tab(_L("视频转 GIF 动图")):
|
44 |
+
video2gif()
|
45 |
+
|
46 |
+
with gr.Tab(_L("GitHub 发布工具")):
|
47 |
+
github_release_creator()
|
48 |
+
|
49 |
+
with gr.Tab(_L("二维码生成")):
|
50 |
+
qrcode()
|
51 |
+
|
52 |
+
with gr.Tab(_L("随机对照试验生成")):
|
53 |
+
rct_generator()
|
54 |
+
|
55 |
+
with gr.Tab(_L("SMTP 测试")):
|
56 |
+
smtp_tester()
|
57 |
+
|
58 |
+
with gr.Tab(_L("翻译器")):
|
59 |
+
translator()
|
60 |
+
|
61 |
+
with gr.Tab(_L("短链接生成")):
|
62 |
+
url_shortner()
|
63 |
+
|
64 |
+
demo.launch()
|
config.py
DELETED
@@ -1,13 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
|
3 |
-
TMP_DIR = "./__pycache__"
|
4 |
-
|
5 |
-
TAB_CONFIG = ["jsonl ⇆ csv", "json ⇆ csv", "json ⇆ jsonl"]
|
6 |
-
MODE = {"from": "jsonl", "to": "csv"}
|
7 |
-
HEADER = {
|
8 |
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0",
|
9 |
-
}
|
10 |
-
|
11 |
-
API_SMTP = os.getenv("api_smtp")
|
12 |
-
API_TRANS = os.getenv("api_caiyun")
|
13 |
-
KEY_TRANS = os.getenv("apikey_caiyun")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data.py → modules/data.py
RENAMED
@@ -1,18 +1,29 @@
|
|
1 |
-
import os
|
2 |
import csv
|
3 |
import json
|
4 |
-
import shutil
|
5 |
import gradio as gr
|
6 |
import pandas as pd
|
7 |
-
from
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
|
18 |
def encoder_json(file_path: str):
|
@@ -26,6 +37,7 @@ def encoder_jsonl(file_path: str):
|
|
26 |
data_list = []
|
27 |
with open(file_path, "r", encoding="utf-8") as file:
|
28 |
for line in file:
|
|
|
29 |
json_data = json.loads(line.strip())
|
30 |
data_list.append(json_data)
|
31 |
|
@@ -34,45 +46,55 @@ def encoder_jsonl(file_path: str):
|
|
34 |
|
35 |
def encoder_csv(file_path: str):
|
36 |
data_list = []
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
return data_list
|
43 |
|
44 |
|
45 |
-
def decoder_json(data_list: list, file_path
|
46 |
if data_list:
|
47 |
with open(file_path, "w", encoding="utf-8") as file:
|
|
|
48 |
json.dump(data_list, file, ensure_ascii=False, indent=4)
|
49 |
|
50 |
return file_path
|
51 |
|
52 |
|
53 |
-
def decoder_csv(data_list: list, file_path
|
54 |
-
if data_list:
|
55 |
header = list(data_list[0].keys())
|
56 |
with open(file_path, "w", newline="", encoding="utf-8") as file:
|
57 |
-
csv_writer = csv.writer(file)
|
58 |
-
csv_writer.writerow(header)
|
59 |
for item in data_list:
|
60 |
csv_writer.writerow([item[key] for key in header])
|
61 |
|
62 |
return file_path
|
63 |
|
64 |
|
65 |
-
def decoder_jsonl(data_list: list, file_path
|
66 |
if data_list:
|
67 |
with open(file_path, "w", encoding="utf-8") as file:
|
68 |
for data in data_list:
|
|
|
69 |
json_line = json.dumps(data, ensure_ascii=False)
|
70 |
-
file.write(json_line
|
71 |
|
72 |
return file_path
|
73 |
|
74 |
|
75 |
def change_mode(input: str):
|
|
|
76 |
affix = input.split(" ")
|
77 |
if affix[1] == "→":
|
78 |
MODE["from"] = affix[0]
|
@@ -83,20 +105,27 @@ def change_mode(input: str):
|
|
83 |
MODE["to"] = affix[0]
|
84 |
|
85 |
|
86 |
-
|
87 |
-
|
|
|
|
|
88 |
try:
|
89 |
-
|
90 |
-
|
91 |
-
|
|
|
|
|
|
|
92 |
|
93 |
except Exception as e:
|
94 |
-
|
|
|
|
|
95 |
|
96 |
|
97 |
-
def data_converter():
|
98 |
with gr.Blocks() as data:
|
99 |
-
for item in
|
100 |
types = item.split(" ⇆ ")
|
101 |
with gr.Tab(item) as tab:
|
102 |
with gr.Row():
|
@@ -106,55 +135,57 @@ def data_converter():
|
|
106 |
f"{types[0]} → {types[1]}",
|
107 |
f"{types[0]} ← {types[1]}",
|
108 |
],
|
109 |
-
label="
|
110 |
value=f"{types[0]} → {types[1]}",
|
111 |
)
|
112 |
-
input_file = gr.
|
113 |
type="filepath",
|
114 |
-
label="
|
115 |
file_types=[f".{types[0]}", f".{types[1]}"],
|
116 |
)
|
117 |
-
convert_btn = gr.Button("
|
118 |
|
119 |
with gr.Column():
|
120 |
-
|
121 |
-
|
|
|
122 |
)
|
123 |
-
|
|
|
124 |
|
125 |
option.change(change_mode, inputs=option)
|
126 |
tab.select(change_mode, inputs=option)
|
127 |
convert_btn.click(
|
128 |
infer,
|
129 |
inputs=input_file,
|
130 |
-
outputs=[output_file, data_viewer],
|
131 |
)
|
132 |
|
133 |
gr.Markdown(
|
134 |
-
"""
|
135 |
-
##
|
136 |
```
|
137 |
[
|
138 |
-
{
|
139 |
"key1": "val11",
|
140 |
"key2": "val12",
|
141 |
...
|
142 |
-
},
|
143 |
-
{
|
144 |
"key1": "val21",
|
145 |
"key2": "val22",
|
146 |
...
|
147 |
-
},
|
148 |
...
|
149 |
]
|
150 |
```
|
151 |
-
##
|
152 |
```
|
153 |
-
{"key1": "val11", "key2": "val12", ...}
|
154 |
-
{"key1": "val21", "key2": "val22", ...}
|
155 |
...
|
156 |
```
|
157 |
-
##
|
158 |
```
|
159 |
key1, key2, ...
|
160 |
val11, val12, ...
|
|
|
|
|
1 |
import csv
|
2 |
import json
|
|
|
3 |
import gradio as gr
|
4 |
import pandas as pd
|
5 |
+
from utils import clean_dir, TMP_DIR, LANG
|
6 |
+
|
7 |
+
|
8 |
+
MODE = {"from": "jsonl", "to": "csv"}
|
9 |
+
ZH2EN = {
|
10 |
+
"模式": "Mode",
|
11 |
+
"上传原数据": "Upload input file",
|
12 |
+
"转换": "Convert",
|
13 |
+
"下载转换数据": "Download output file",
|
14 |
+
"数据预览": "Data viewer",
|
15 |
+
"支持的 JSON 格式": "Supported JSON format",
|
16 |
+
"支持的 JSON Lines 格式": "Supported jsonl format",
|
17 |
+
"支持的 CSV 格式": "Supported CSV format",
|
18 |
+
"状态栏": "Status",
|
19 |
+
}
|
20 |
+
|
21 |
+
|
22 |
+
def _L(zh_txt: str):
|
23 |
+
if LANG:
|
24 |
+
return ZH2EN[zh_txt]
|
25 |
+
else:
|
26 |
+
return zh_txt
|
27 |
|
28 |
|
29 |
def encoder_json(file_path: str):
|
|
|
37 |
data_list = []
|
38 |
with open(file_path, "r", encoding="utf-8") as file:
|
39 |
for line in file:
|
40 |
+
# 加载每一行的 JSON 数据
|
41 |
json_data = json.loads(line.strip())
|
42 |
data_list.append(json_data)
|
43 |
|
|
|
46 |
|
47 |
def encoder_csv(file_path: str):
|
48 |
data_list = []
|
49 |
+
try:
|
50 |
+
with open(file_path, "r", encoding="utf-8") as file:
|
51 |
+
csv_reader = csv.DictReader(file)
|
52 |
+
for row in csv_reader:
|
53 |
+
data_list.append(dict(row))
|
54 |
+
|
55 |
+
except UnicodeDecodeError:
|
56 |
+
with open(file_path, "r", encoding="GBK") as file:
|
57 |
+
csv_reader = csv.DictReader(file)
|
58 |
+
for row in csv_reader:
|
59 |
+
data_list.append(dict(row))
|
60 |
|
61 |
return data_list
|
62 |
|
63 |
|
64 |
+
def decoder_json(data_list: list, file_path: str):
|
65 |
if data_list:
|
66 |
with open(file_path, "w", encoding="utf-8") as file:
|
67 |
+
# 将整个列表转换成 JSON 格式并写入文件
|
68 |
json.dump(data_list, file, ensure_ascii=False, indent=4)
|
69 |
|
70 |
return file_path
|
71 |
|
72 |
|
73 |
+
def decoder_csv(data_list: list, file_path: str):
|
74 |
+
if data_list: # 提取第一个字典的键作为表头
|
75 |
header = list(data_list[0].keys())
|
76 |
with open(file_path, "w", newline="", encoding="utf-8") as file:
|
77 |
+
csv_writer = csv.writer(file) # 写入表头
|
78 |
+
csv_writer.writerow(header) # 逐项写入字典的值
|
79 |
for item in data_list:
|
80 |
csv_writer.writerow([item[key] for key in header])
|
81 |
|
82 |
return file_path
|
83 |
|
84 |
|
85 |
+
def decoder_jsonl(data_list: list, file_path: str):
|
86 |
if data_list:
|
87 |
with open(file_path, "w", encoding="utf-8") as file:
|
88 |
for data in data_list:
|
89 |
+
# 将每个 JSON 对象转换成字符串并写入文件,每行一个对象
|
90 |
json_line = json.dumps(data, ensure_ascii=False)
|
91 |
+
file.write(f"{json_line}\n")
|
92 |
|
93 |
return file_path
|
94 |
|
95 |
|
96 |
def change_mode(input: str):
|
97 |
+
global MODE
|
98 |
affix = input.split(" ")
|
99 |
if affix[1] == "→":
|
100 |
MODE["from"] = affix[0]
|
|
|
105 |
MODE["to"] = affix[0]
|
106 |
|
107 |
|
108 |
+
# outer func
|
109 |
+
def infer(input_file: str, cache=f"{TMP_DIR}/data"):
|
110 |
+
status = "Success"
|
111 |
+
output_file = previews = None
|
112 |
try:
|
113 |
+
clean_dir(cache)
|
114 |
+
src_fmt = MODE["from"]
|
115 |
+
dst_fmt = MODE["to"]
|
116 |
+
data_list = eval(f"encoder_{src_fmt}")(input_file)
|
117 |
+
output_file = eval(f"decoder_{dst_fmt}")(data_list, f"{cache}/output.{dst_fmt}")
|
118 |
+
previews = pd.DataFrame(data_list)
|
119 |
|
120 |
except Exception as e:
|
121 |
+
status = f"{e}"
|
122 |
+
|
123 |
+
return status, output_file, previews
|
124 |
|
125 |
|
126 |
+
def data_converter(tab_cfgs=["jsonl ⇆ csv", "json ⇆ csv", "json ⇆ jsonl"]):
|
127 |
with gr.Blocks() as data:
|
128 |
+
for item in tab_cfgs:
|
129 |
types = item.split(" ⇆ ")
|
130 |
with gr.Tab(item) as tab:
|
131 |
with gr.Row():
|
|
|
135 |
f"{types[0]} → {types[1]}",
|
136 |
f"{types[0]} ← {types[1]}",
|
137 |
],
|
138 |
+
label=_L("模式"),
|
139 |
value=f"{types[0]} → {types[1]}",
|
140 |
)
|
141 |
+
input_file = gr.File(
|
142 |
type="filepath",
|
143 |
+
label=_L("上传原数据"),
|
144 |
file_types=[f".{types[0]}", f".{types[1]}"],
|
145 |
)
|
146 |
+
convert_btn = gr.Button(_L("转换"))
|
147 |
|
148 |
with gr.Column():
|
149 |
+
status_bar = gr.Textbox(
|
150 |
+
label=_L("状态栏"),
|
151 |
+
show_copy_button=True,
|
152 |
)
|
153 |
+
output_file = gr.File(type="filepath", label=_L("下载转换数据"))
|
154 |
+
data_viewer = gr.Dataframe(label=_L("数据预览"))
|
155 |
|
156 |
option.change(change_mode, inputs=option)
|
157 |
tab.select(change_mode, inputs=option)
|
158 |
convert_btn.click(
|
159 |
infer,
|
160 |
inputs=input_file,
|
161 |
+
outputs=[status_bar, output_file, data_viewer],
|
162 |
)
|
163 |
|
164 |
gr.Markdown(
|
165 |
+
f"""
|
166 |
+
## {_L('支持的 JSON 格式')}
|
167 |
```
|
168 |
[
|
169 |
+
{{
|
170 |
"key1": "val11",
|
171 |
"key2": "val12",
|
172 |
...
|
173 |
+
}},
|
174 |
+
{{
|
175 |
"key1": "val21",
|
176 |
"key2": "val22",
|
177 |
...
|
178 |
+
}},
|
179 |
...
|
180 |
]
|
181 |
```
|
182 |
+
## {_L('支持的 JSON Lines 格式')}
|
183 |
```
|
184 |
+
{{"key1": "val11", "key2": "val12", ...}}
|
185 |
+
{{"key1": "val21", "key2": "val22", ...}}
|
186 |
...
|
187 |
```
|
188 |
+
## {_L('支持的 CSV 格式')}
|
189 |
```
|
190 |
key1, key2, ...
|
191 |
val11, val12, ...
|
exif.py → modules/exif.py
RENAMED
@@ -1,16 +1,35 @@
|
|
1 |
import os
|
2 |
import imghdr
|
3 |
-
import shutil
|
4 |
import hashlib
|
5 |
-
import zipfile
|
6 |
import exifread
|
7 |
import gradio as gr
|
8 |
import pandas as pd
|
9 |
from PIL import Image
|
10 |
-
from
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
with open(origin_file_path, "rb") as image_file:
|
15 |
tags = exifread.process_file(image_file)
|
16 |
|
@@ -22,112 +41,91 @@ def get_exif_data(origin_file_path):
|
|
22 |
return output
|
23 |
|
24 |
|
25 |
-
def
|
26 |
-
save_path = f"{
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
)
|
34 |
-
else:
|
35 |
-
img_mode = img.mode
|
36 |
-
|
37 |
-
img_without_exif = Image.new(img_mode, img.size)
|
38 |
-
img_without_exif.putdata(data)
|
39 |
-
img_without_exif.save(save_path)
|
40 |
-
|
41 |
-
except Exception as e:
|
42 |
-
print(f"\n{image_path} Error: {e}")
|
43 |
|
|
|
|
|
|
|
44 |
return save_path
|
45 |
|
46 |
|
47 |
-
def
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
# 打开ZIP文件
|
52 |
-
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
53 |
-
# 解压文件
|
54 |
-
zip_ref.extractall(extract_to)
|
55 |
-
|
56 |
-
return extract_to
|
57 |
-
|
58 |
-
|
59 |
-
def find_files(directory: str):
|
60 |
-
found_files = []
|
61 |
-
for root, _, files in os.walk(directory):
|
62 |
for file in files:
|
63 |
fpath = os.path.join(root, file).replace("\\", "/")
|
64 |
if imghdr.what(fpath) != None:
|
65 |
-
|
66 |
-
|
67 |
-
return found_files
|
68 |
-
|
69 |
-
|
70 |
-
def compress(folder_path=f"{TMP_DIR}/outputs", zip_file_path=f"{TMP_DIR}/outputs.zip"):
|
71 |
-
if not os.path.exists(folder_path):
|
72 |
-
print(f"Error: Folder '{folder_path}' does not exist.")
|
73 |
-
return
|
74 |
-
|
75 |
-
with zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) as zipf:
|
76 |
-
for root, _, files in os.walk(folder_path):
|
77 |
-
for file in files:
|
78 |
-
file_path = os.path.join(root, file)
|
79 |
-
relative_path = os.path.relpath(file_path, folder_path)
|
80 |
-
zipf.write(
|
81 |
-
file_path,
|
82 |
-
arcname=os.path.join(os.path.basename(folder_path), relative_path),
|
83 |
-
)
|
84 |
|
85 |
-
return
|
86 |
|
87 |
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
|
|
|
|
|
100 |
|
101 |
-
|
102 |
-
if not imgs_zip:
|
103 |
-
return None, pd.DataFrame([{"Warning": "Please upload pictures zip!"}])
|
104 |
|
105 |
-
if os.path.exists(TMP_DIR):
|
106 |
-
shutil.rmtree(TMP_DIR)
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
-
|
118 |
-
|
119 |
|
120 |
-
return
|
121 |
|
122 |
|
123 |
def clexif():
|
124 |
with gr.Blocks() as iface:
|
125 |
-
with gr.Tab("
|
126 |
gr.Interface(
|
127 |
fn=infer,
|
128 |
inputs=[
|
129 |
gr.File(
|
130 |
-
label="
|
131 |
file_types=[
|
132 |
".jpg",
|
133 |
".jpeg",
|
@@ -141,43 +139,47 @@ def clexif():
|
|
141 |
],
|
142 |
),
|
143 |
gr.Checkbox(
|
144 |
-
label="
|
145 |
value=False,
|
146 |
),
|
147 |
],
|
148 |
outputs=[
|
|
|
149 |
gr.Image(
|
150 |
-
label="
|
151 |
type="filepath",
|
152 |
show_share_button=False,
|
153 |
),
|
154 |
gr.Textbox(label="EXIF", show_copy_button=True),
|
155 |
],
|
156 |
-
|
157 |
)
|
158 |
|
159 |
-
with gr.Tab("
|
160 |
gr.Interface(
|
161 |
fn=batch_infer,
|
162 |
inputs=[
|
163 |
gr.File(
|
164 |
-
label="
|
165 |
file_types=[".zip"],
|
166 |
),
|
167 |
gr.Checkbox(
|
168 |
-
label="
|
169 |
value=False,
|
170 |
),
|
171 |
],
|
172 |
outputs=[
|
|
|
173 |
gr.File(
|
174 |
-
label="
|
175 |
type="filepath",
|
176 |
),
|
177 |
-
gr.Dataframe(label="EXIF
|
178 |
],
|
179 |
-
description=
|
180 |
-
|
|
|
|
|
181 |
)
|
182 |
|
183 |
return iface
|
|
|
1 |
import os
|
2 |
import imghdr
|
|
|
3 |
import hashlib
|
|
|
4 |
import exifread
|
5 |
import gradio as gr
|
6 |
import pandas as pd
|
7 |
from PIL import Image
|
8 |
+
from utils import clean_dir, compress, mk_dir, unzip, TMP_DIR, LANG
|
9 |
+
|
10 |
+
ZH2EN = {
|
11 |
+
"单图片处理": "Process single picture",
|
12 |
+
"上传图片": "Upload picture",
|
13 |
+
"导出原格式": "Export original format",
|
14 |
+
"下载清理 EXIF 后的图片": "Download cleaned picture",
|
15 |
+
"批量处理": "Batch processor",
|
16 |
+
"上传包含多图片的 zip 压缩包": "Upload pictures zip",
|
17 |
+
"导出原格式": "Export original format",
|
18 |
+
"下载清理 EXIF 后的多图片压缩包": "Download cleaned pictures",
|
19 |
+
"EXIF 列表": "EXIF list",
|
20 |
+
"上传包含多图片的 zip 压缩包时确保上传进度至 100% 后再提交": "When uploading pictures zip, please make sure the zip is completely uploaded before clicking Submit",
|
21 |
+
"状态栏": "Status",
|
22 |
+
}
|
23 |
+
|
24 |
+
|
25 |
+
def _L(zh_txt: str):
|
26 |
+
if LANG:
|
27 |
+
return ZH2EN[zh_txt]
|
28 |
+
else:
|
29 |
+
return zh_txt
|
30 |
+
|
31 |
+
|
32 |
+
def get_exif(origin_file_path):
|
33 |
with open(origin_file_path, "rb") as image_file:
|
34 |
tags = exifread.process_file(image_file)
|
35 |
|
|
|
41 |
return output
|
42 |
|
43 |
|
44 |
+
def clear_exif(img_path: str, cache: str, img_mode=None, outdir=""):
|
45 |
+
save_path = f"{cache}/{outdir}output." + img_path.split(".")[-1]
|
46 |
+
img = Image.open(img_path)
|
47 |
+
data = list(img.getdata())
|
48 |
+
if img_mode:
|
49 |
+
save_path = f"{cache}/{outdir}{hashlib.md5(img_path.encode()).hexdigest()}.jpg"
|
50 |
+
else:
|
51 |
+
img_mode = img.mode
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
+
img_without_exif = Image.new(img_mode, img.size)
|
54 |
+
img_without_exif.putdata(data)
|
55 |
+
img_without_exif.save(save_path)
|
56 |
return save_path
|
57 |
|
58 |
|
59 |
+
def find_images(dir_path: str):
|
60 |
+
found_images = []
|
61 |
+
for root, _, files in os.walk(dir_path):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
for file in files:
|
63 |
fpath = os.path.join(root, file).replace("\\", "/")
|
64 |
if imghdr.what(fpath) != None:
|
65 |
+
found_images.append(fpath)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
|
67 |
+
return found_images
|
68 |
|
69 |
|
70 |
+
# outer func
|
71 |
+
def infer(img_path: str, keep_ext: bool, cache=f"{TMP_DIR}/exif"):
|
72 |
+
status = "Success"
|
73 |
+
out_img = out_exif = None
|
74 |
+
try:
|
75 |
+
if not img_path or imghdr.what(img_path) == None:
|
76 |
+
raise ValueError("请输入图片!")
|
77 |
|
78 |
+
clean_dir(cache)
|
79 |
+
img_mode = "RGB" if not keep_ext else None
|
80 |
+
out_img = clear_exif(img_path, cache, img_mode)
|
81 |
+
out_exif = get_exif(img_path)
|
82 |
|
83 |
+
except Exception as e:
|
84 |
+
status = f"{e}"
|
85 |
|
86 |
+
return status, out_img, out_exif
|
|
|
|
|
87 |
|
|
|
|
|
88 |
|
89 |
+
# outer func
|
90 |
+
def batch_infer(imgs_zip: str, keep_ext: bool, cache=f"{TMP_DIR}/exif"):
|
91 |
+
status = "Success"
|
92 |
+
out_images = out_exifs = None
|
93 |
+
try:
|
94 |
+
if not imgs_zip:
|
95 |
+
raise ValueError("Please upload pictures zip!")
|
96 |
+
|
97 |
+
clean_dir(cache)
|
98 |
+
mk_dir(f"{cache}/outputs")
|
99 |
+
extract_to = f"{cache}/inputs"
|
100 |
+
unzip(imgs_zip, extract_to)
|
101 |
+
imgs = find_images(extract_to)
|
102 |
+
img_mode = "RGB" if not keep_ext else None
|
103 |
+
exifs = []
|
104 |
+
for img in imgs:
|
105 |
+
clear_exif(img, cache, img_mode, "outputs/")
|
106 |
+
exifs.append({"filename": os.path.basename(img), "exif": get_exif(img)})
|
107 |
+
|
108 |
+
if not exifs:
|
109 |
+
raise ValueError("No picture in the zip")
|
110 |
+
|
111 |
+
out_images = f"{cache}/outputs.zip"
|
112 |
+
compress(f"{cache}/outputs", out_images)
|
113 |
+
out_exifs = pd.DataFrame(exifs)
|
114 |
|
115 |
+
except Exception as e:
|
116 |
+
status = f"{e}"
|
117 |
|
118 |
+
return status, out_images, out_exifs
|
119 |
|
120 |
|
121 |
def clexif():
|
122 |
with gr.Blocks() as iface:
|
123 |
+
with gr.Tab(_L("单图片处理")):
|
124 |
gr.Interface(
|
125 |
fn=infer,
|
126 |
inputs=[
|
127 |
gr.File(
|
128 |
+
label=_L("上传图片"),
|
129 |
file_types=[
|
130 |
".jpg",
|
131 |
".jpeg",
|
|
|
139 |
],
|
140 |
),
|
141 |
gr.Checkbox(
|
142 |
+
label=_L("导出原格式"),
|
143 |
value=False,
|
144 |
),
|
145 |
],
|
146 |
outputs=[
|
147 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
148 |
gr.Image(
|
149 |
+
label=_L("下载清理 EXIF 后的图片"),
|
150 |
type="filepath",
|
151 |
show_share_button=False,
|
152 |
),
|
153 |
gr.Textbox(label="EXIF", show_copy_button=True),
|
154 |
],
|
155 |
+
flagging_mode="never",
|
156 |
)
|
157 |
|
158 |
+
with gr.Tab(_L("批量处理")):
|
159 |
gr.Interface(
|
160 |
fn=batch_infer,
|
161 |
inputs=[
|
162 |
gr.File(
|
163 |
+
label=_L("上传包含多图片的 zip 压缩包"),
|
164 |
file_types=[".zip"],
|
165 |
),
|
166 |
gr.Checkbox(
|
167 |
+
label=_L("导出原格式"),
|
168 |
value=False,
|
169 |
),
|
170 |
],
|
171 |
outputs=[
|
172 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
173 |
gr.File(
|
174 |
+
label=_L("下载清理 EXIF 后的多图片压缩包"),
|
175 |
type="filepath",
|
176 |
),
|
177 |
+
gr.Dataframe(label=_L("EXIF 列表")),
|
178 |
],
|
179 |
+
description=_L(
|
180 |
+
"上传包含多图片的 zip 压缩包时确保上传进度至 100% 后再提交"
|
181 |
+
),
|
182 |
+
flagging_mode="never",
|
183 |
)
|
184 |
|
185 |
return iface
|
gif.py → modules/gif.py
RENAMED
@@ -1,45 +1,44 @@
|
|
1 |
import os
|
2 |
import math
|
3 |
-
import shutil
|
4 |
import gradio as gr
|
5 |
from PIL import Image, ImageSequence
|
6 |
from moviepy.editor import VideoFileClip
|
7 |
-
from
|
8 |
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
-
os.mkdir(tmp_dir)
|
15 |
-
return f"{tmp_dir}/input.gif"
|
16 |
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
-
def get_frame_duration(gif: Image):
|
19 |
-
duration = gif.info.get("duration", 100)
|
20 |
-
return [
|
21 |
-
frame.info.get("duration", duration) for frame in ImageSequence.Iterator(gif)
|
22 |
-
]
|
23 |
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
target_height: int,
|
28 |
-
input_path=f"{TMP_DIR}/input.gif",
|
29 |
-
output_path=f"{TMP_DIR}/output.gif",
|
30 |
-
):
|
31 |
-
# Open the GIF image
|
32 |
gif = Image.open(input_path)
|
33 |
-
# Create a list to hold the modified frames
|
34 |
modified_frames = []
|
35 |
-
# Loop through each frame of the GIF
|
36 |
for frame in ImageSequence.Iterator(gif):
|
37 |
-
# Resize the frame
|
38 |
resized_frame = frame.resize((target_width, target_height), Image.LANCZOS)
|
39 |
-
# Append the resized frame to the list
|
40 |
modified_frames.append(resized_frame)
|
41 |
|
42 |
-
frame_durations =
|
|
|
43 |
modified_frames[0].save(
|
44 |
output_path,
|
45 |
format="GIF",
|
@@ -49,45 +48,50 @@ def resize_gif(
|
|
49 |
loop=0,
|
50 |
)
|
51 |
|
52 |
-
return output_path
|
53 |
-
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
58 |
try:
|
|
|
59 |
with VideoFileClip(video_path, audio_fps=16000) as clip:
|
60 |
if clip.duration > 5:
|
61 |
-
raise ValueError("
|
62 |
|
63 |
-
|
64 |
-
clip.speedx(speed).to_gif(gif_path, fps=12)
|
65 |
w, h = clip.size
|
66 |
|
|
|
|
|
67 |
target_h = math.ceil(target_w * h / w)
|
68 |
-
|
|
|
69 |
|
70 |
except Exception as e:
|
71 |
-
|
|
|
|
|
72 |
|
73 |
|
74 |
def video2gif():
|
75 |
return gr.Interface(
|
76 |
fn=infer,
|
77 |
inputs=[
|
78 |
-
gr.Video(label="
|
79 |
-
gr.Slider(
|
80 |
-
label="Speed",
|
81 |
-
minimum=0.5,
|
82 |
-
maximum=2.0,
|
83 |
-
step=0.25,
|
84 |
-
value=1.0,
|
85 |
-
),
|
86 |
],
|
87 |
outputs=[
|
88 |
-
gr.Textbox(label="
|
89 |
-
gr.
|
|
|
90 |
],
|
91 |
-
description="
|
92 |
flagging_mode="never",
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
)
|
|
|
1 |
import os
|
2 |
import math
|
|
|
3 |
import gradio as gr
|
4 |
from PIL import Image, ImageSequence
|
5 |
from moviepy.editor import VideoFileClip
|
6 |
+
from utils import clean_dir, TMP_DIR, LANG
|
7 |
|
8 |
|
9 |
+
ZH2EN = {
|
10 |
+
"上传视频": "Upload video",
|
11 |
+
"倍速": "Speed",
|
12 |
+
"状态栏": "Status",
|
13 |
+
"文件名": "Filename",
|
14 |
+
"下载动图": "Download GIF",
|
15 |
+
"请确保视频上传完整后再点击提交,若时长大于五秒可先在线裁剪": "Please make sure the video is completely uploaded before clicking Submit, you can crop it online first if the video size is >5s",
|
16 |
+
}
|
17 |
|
|
|
|
|
18 |
|
19 |
+
def _L(zh_txt: str):
|
20 |
+
if LANG:
|
21 |
+
return ZH2EN[zh_txt]
|
22 |
+
else:
|
23 |
+
return zh_txt
|
24 |
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
+
def get_frame_dur(gif: Image):
|
27 |
+
# 获取 GIF 图像中第一帧的 duration
|
28 |
+
dur = gif.info.get("duration", 100)
|
29 |
+
# 返回每一帧的 duration
|
30 |
+
return [frame.info.get("duration", dur) for frame in ImageSequence.Iterator(gif)]
|
31 |
|
32 |
+
|
33 |
+
def resize_gif(target_width: int, target_height: int, input_path, output_path):
|
|
|
|
|
|
|
|
|
|
|
34 |
gif = Image.open(input_path)
|
|
|
35 |
modified_frames = []
|
|
|
36 |
for frame in ImageSequence.Iterator(gif):
|
|
|
37 |
resized_frame = frame.resize((target_width, target_height), Image.LANCZOS)
|
|
|
38 |
modified_frames.append(resized_frame)
|
39 |
|
40 |
+
frame_durations = get_frame_dur(gif)
|
41 |
+
# 将修改后的帧作为新的 GIF 保存
|
42 |
modified_frames[0].save(
|
43 |
output_path,
|
44 |
format="GIF",
|
|
|
48 |
loop=0,
|
49 |
)
|
50 |
|
|
|
|
|
51 |
|
52 |
+
# outer func
|
53 |
+
def infer(video_path: str, speed: float, target_w=640, cache=f"{TMP_DIR}/gif"):
|
54 |
+
status = "Success"
|
55 |
+
gif_name = out_gif = None
|
56 |
try:
|
57 |
+
clean_dir(cache)
|
58 |
with VideoFileClip(video_path, audio_fps=16000) as clip:
|
59 |
if clip.duration > 5:
|
60 |
+
raise ValueError("上传的视频过长")
|
61 |
|
62 |
+
clip.speedx(speed).to_gif(f"{cache}/input.gif", fps=12)
|
|
|
63 |
w, h = clip.size
|
64 |
|
65 |
+
in_gif = f"{cache}/input.gif"
|
66 |
+
out_gif = f"{cache}/output.gif"
|
67 |
target_h = math.ceil(target_w * h / w)
|
68 |
+
gif_name = os.path.basename(video_path)
|
69 |
+
resize_gif(target_w, target_h, in_gif, out_gif)
|
70 |
|
71 |
except Exception as e:
|
72 |
+
status = f"{e}"
|
73 |
+
|
74 |
+
return status, gif_name, out_gif
|
75 |
|
76 |
|
77 |
def video2gif():
|
78 |
return gr.Interface(
|
79 |
fn=infer,
|
80 |
inputs=[
|
81 |
+
gr.Video(label=_L("上传视频")),
|
82 |
+
gr.Slider(label=_L("倍速"), minimum=0.5, maximum=2.0, step=0.25, value=1.0),
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
],
|
84 |
outputs=[
|
85 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
86 |
+
gr.Textbox(label=_L("文件名"), show_copy_button=True),
|
87 |
+
gr.Image(label=_L("下载动图"), type="filepath", show_share_button=False),
|
88 |
],
|
89 |
+
description=_L("请确保视频上传完整后再点击提交,若时长大于五秒可先在线裁剪"),
|
90 |
flagging_mode="never",
|
91 |
+
examples=[
|
92 |
+
[
|
93 |
+
"https://www.modelscope.cn/studio/Genius-Society/online_tools/resolve/master/examples/herta.mp4",
|
94 |
+
2,
|
95 |
+
]
|
96 |
+
],
|
97 |
)
|
github.py → modules/github.py
RENAMED
@@ -1,47 +1,61 @@
|
|
1 |
import os
|
2 |
import requests
|
3 |
import gradio as gr
|
|
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
):
|
15 |
try:
|
16 |
# 创建 Release
|
17 |
-
|
18 |
f"https://api.github.com/repos/{owner}/{repo}/releases",
|
19 |
headers={
|
20 |
"Authorization": f"token {token}",
|
21 |
"Accept": "application/vnd.github.v3+json",
|
22 |
},
|
23 |
json={
|
24 |
-
"tag_name":
|
25 |
-
"name":
|
26 |
-
"body":
|
27 |
"draft": False,
|
28 |
"prerelease": False,
|
29 |
},
|
30 |
)
|
31 |
|
32 |
-
if
|
33 |
-
|
34 |
|
35 |
# 获取上传 URL
|
36 |
-
release =
|
37 |
upload_url = release["upload_url"].split("{")[0]
|
38 |
|
39 |
# 上传多个二进制文件
|
40 |
-
results =
|
41 |
for file_path in files:
|
42 |
file_name = os.path.basename(file_path)
|
43 |
with open(file_path, "rb") as binary_file:
|
44 |
-
|
45 |
f"{upload_url}?name={file_name}",
|
46 |
headers={
|
47 |
"Authorization": f"token {token}",
|
@@ -50,42 +64,34 @@ def create_github_release(
|
|
50 |
data=binary_file,
|
51 |
)
|
52 |
|
53 |
-
if
|
54 |
-
results
|
55 |
else:
|
56 |
-
results.
|
57 |
-
f"Failed to upload binary file '{file_name}': {upload_response.status_code}, {upload_response.json()}"
|
58 |
-
)
|
59 |
|
60 |
-
return
|
61 |
|
62 |
except Exception as e:
|
63 |
-
return f"
|
64 |
|
65 |
|
66 |
def github_release_creator():
|
67 |
return gr.Interface(
|
68 |
fn=create_github_release,
|
69 |
inputs=[
|
70 |
-
gr.Textbox(
|
71 |
-
|
72 |
-
placeholder="username / organization name",
|
73 |
-
),
|
74 |
-
gr.Textbox(label="GitHub Repo", placeholder="repo name"),
|
75 |
gr.Textbox(
|
76 |
label="GitHub Token",
|
77 |
-
placeholder="personal access token",
|
78 |
type="password",
|
79 |
),
|
80 |
-
gr.Textbox(label="
|
81 |
-
gr.Textbox(label="
|
82 |
-
gr.TextArea(
|
83 |
-
|
84 |
-
placeholder="Release with binary file(s) and source code.",
|
85 |
-
),
|
86 |
-
gr.File(label="Binary File(s)", file_count="multiple"),
|
87 |
],
|
88 |
-
outputs=gr.TextArea(label="
|
89 |
-
description="
|
90 |
flagging_mode="never",
|
91 |
)
|
|
|
1 |
import os
|
2 |
import requests
|
3 |
import gradio as gr
|
4 |
+
from utils import LANG
|
5 |
|
6 |
+
ZH2EN = {
|
7 |
+
"仓库拥有者": "Repo owner",
|
8 |
+
"输入用户名或组织名": "Username / organization name",
|
9 |
+
"仓库名": "GitHub repo",
|
10 |
+
"输入仓库名": "Repo name",
|
11 |
+
"输入 personal access token": "Personal access token",
|
12 |
+
"发布标签": "Release tag",
|
13 |
+
"发布名": "Release title",
|
14 |
+
"发布描述": "Describe this release",
|
15 |
+
"上传发布文件(可多选)": "Binary File(s)",
|
16 |
+
"状态栏": "Status",
|
17 |
+
"上传文件创建一个新的 GitHub 发布": "Upload binary file(s) to create a new GitHub release.",
|
18 |
+
}
|
19 |
|
20 |
+
|
21 |
+
def _L(zh_txt: str):
|
22 |
+
if LANG:
|
23 |
+
return ZH2EN[zh_txt]
|
24 |
+
else:
|
25 |
+
return zh_txt
|
26 |
+
|
27 |
+
|
28 |
+
def create_github_release(owner, repo, token, tag, name, description, files):
|
29 |
try:
|
30 |
# 创建 Release
|
31 |
+
response = requests.post(
|
32 |
f"https://api.github.com/repos/{owner}/{repo}/releases",
|
33 |
headers={
|
34 |
"Authorization": f"token {token}",
|
35 |
"Accept": "application/vnd.github.v3+json",
|
36 |
},
|
37 |
json={
|
38 |
+
"tag_name": tag,
|
39 |
+
"name": name,
|
40 |
+
"body": description,
|
41 |
"draft": False,
|
42 |
"prerelease": False,
|
43 |
},
|
44 |
)
|
45 |
|
46 |
+
if response.status_code != 201:
|
47 |
+
raise ConnectionError(f"{response.status_code}: {response.json()}")
|
48 |
|
49 |
# 获取上传 URL
|
50 |
+
release = response.json()
|
51 |
upload_url = release["upload_url"].split("{")[0]
|
52 |
|
53 |
# 上传多个二进制文件
|
54 |
+
results = ""
|
55 |
for file_path in files:
|
56 |
file_name = os.path.basename(file_path)
|
57 |
with open(file_path, "rb") as binary_file:
|
58 |
+
upl_response = requests.post(
|
59 |
f"{upload_url}?name={file_name}",
|
60 |
headers={
|
61 |
"Authorization": f"token {token}",
|
|
|
64 |
data=binary_file,
|
65 |
)
|
66 |
|
67 |
+
if upl_response.status_code == 201:
|
68 |
+
results += f"Upload '{file_name}' success!"
|
69 |
else:
|
70 |
+
results += f"Failed to upload {file_name}: {upl_response.status_code}, {upl_response.json()}"
|
|
|
|
|
71 |
|
72 |
+
return results
|
73 |
|
74 |
except Exception as e:
|
75 |
+
return f"{e}"
|
76 |
|
77 |
|
78 |
def github_release_creator():
|
79 |
return gr.Interface(
|
80 |
fn=create_github_release,
|
81 |
inputs=[
|
82 |
+
gr.Textbox(label=_L("仓库拥有者"), placeholder=_L("输入用户名或组织名")),
|
83 |
+
gr.Textbox(label=_L("仓库名"), placeholder=_L("输入仓库名")),
|
|
|
|
|
|
|
84 |
gr.Textbox(
|
85 |
label="GitHub Token",
|
86 |
+
placeholder=_L("输入 personal access token"),
|
87 |
type="password",
|
88 |
),
|
89 |
+
gr.Textbox(label=_L("发布标签")),
|
90 |
+
gr.Textbox(label=_L("发布名")),
|
91 |
+
gr.TextArea(label=_L("发布描述")),
|
92 |
+
gr.File(label=_L("上传发布文件(可多选)"), file_count="multiple"),
|
|
|
|
|
|
|
93 |
],
|
94 |
+
outputs=gr.TextArea(label=_L("状态栏"), show_copy_button=True),
|
95 |
+
description=_L("上传文件创建一个新的 GitHub 发布"),
|
96 |
flagging_mode="never",
|
97 |
)
|
modules/qr.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from utils import LANG
|
3 |
+
|
4 |
+
ZH2EN = {
|
5 |
+
"二维码输出尺寸": "Image size",
|
6 |
+
"输入文本": "Input text",
|
7 |
+
"输出二维码": "QR code",
|
8 |
+
"输入文字在线生成二维码": "Enter text to generate a QR code.",
|
9 |
+
"状态栏": "Status",
|
10 |
+
}
|
11 |
+
|
12 |
+
|
13 |
+
def _L(zh_txt: str):
|
14 |
+
if LANG:
|
15 |
+
return ZH2EN[zh_txt]
|
16 |
+
else:
|
17 |
+
return zh_txt
|
18 |
+
|
19 |
+
|
20 |
+
def infer(img_size: int, input_txt: str):
|
21 |
+
status = "Success"
|
22 |
+
url = None
|
23 |
+
try:
|
24 |
+
if not input_txt:
|
25 |
+
raise ValueError("Please input text!")
|
26 |
+
|
27 |
+
url = f"https://api.qrserver.com/v1/create-qr-code/?size={img_size}x{img_size}&data={input_txt}"
|
28 |
+
|
29 |
+
except Exception as e:
|
30 |
+
status = f"{e}"
|
31 |
+
|
32 |
+
return status, url
|
33 |
+
|
34 |
+
|
35 |
+
def qrcode():
|
36 |
+
return gr.Interface(
|
37 |
+
fn=infer,
|
38 |
+
inputs=[
|
39 |
+
gr.Slider(35, 1000, 217, label=_L("二维码输出尺寸")),
|
40 |
+
gr.Textbox(label=_L("输入文本")),
|
41 |
+
],
|
42 |
+
outputs=[
|
43 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
44 |
+
gr.Image(label=_L("输出二维码"), show_share_button=False),
|
45 |
+
],
|
46 |
+
description=_L("输入文字在线生成二维码"),
|
47 |
+
flagging_mode="never",
|
48 |
+
)
|
rct.py → modules/rct.py
RENAMED
@@ -1,26 +1,39 @@
|
|
1 |
-
import os
|
2 |
import csv
|
3 |
import random
|
4 |
-
import shutil
|
5 |
import pandas as pd
|
6 |
import gradio as gr
|
7 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
|
10 |
def list_to_csv(list_of_dicts: list, filename: str):
|
11 |
keys = dict(list_of_dicts[0]).keys()
|
|
|
12 |
with open(filename, "w", newline="", encoding="utf-8") as csvfile:
|
13 |
writer = csv.DictWriter(csvfile, fieldnames=keys)
|
14 |
writer.writeheader()
|
15 |
for data in list_of_dicts:
|
16 |
writer.writerow(data)
|
17 |
|
18 |
-
return filename
|
19 |
-
|
20 |
|
21 |
-
def
|
22 |
-
total = sum(ratio)
|
23 |
splits = [0]
|
|
|
24 |
for i, r in enumerate(ratio):
|
25 |
splits.append(splits[i] + int(1.0 * r / total * participants))
|
26 |
|
@@ -36,40 +49,47 @@ def random_allocation(participants: int, ratio: list):
|
|
36 |
allocation.append({"id": participant, "group": i + 1})
|
37 |
|
38 |
sorted_data = sorted(allocation, key=lambda x: x["id"])
|
39 |
-
|
40 |
-
return
|
41 |
|
42 |
|
43 |
-
|
44 |
-
|
45 |
-
shutil.rmtree(TMP_DIR)
|
46 |
-
|
47 |
-
os.makedirs(TMP_DIR, exist_ok=True)
|
48 |
-
ratio_list = ratios.split(":")
|
49 |
ratio = []
|
|
|
|
|
|
|
50 |
try:
|
|
|
|
|
51 |
for r in ratio_list:
|
52 |
current_ratio = float(r.strip())
|
53 |
if current_ratio > 0:
|
54 |
ratio.append(current_ratio)
|
55 |
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
58 |
|
59 |
-
return
|
60 |
|
61 |
|
62 |
def rct_generator():
|
63 |
return gr.Interface(
|
64 |
fn=infer,
|
65 |
inputs=[
|
66 |
-
gr.Number(label="
|
67 |
-
gr.Textbox(label="
|
68 |
],
|
69 |
outputs=[
|
70 |
-
gr.
|
71 |
-
gr.
|
|
|
72 |
],
|
73 |
-
description=
|
|
|
|
|
74 |
flagging_mode="never",
|
75 |
)
|
|
|
|
|
1 |
import csv
|
2 |
import random
|
|
|
3 |
import pandas as pd
|
4 |
import gradio as gr
|
5 |
+
from utils import clean_dir, TMP_DIR, LANG
|
6 |
+
|
7 |
+
ZH2EN = {
|
8 |
+
"输入参与者数量": "Number of participants",
|
9 |
+
"输入分组比率": "Grouping ratio",
|
10 |
+
"状态栏": "Status",
|
11 |
+
"下载随机分组数据 CSV": "Download data CSV",
|
12 |
+
"随机分组数据预览": "Data preview",
|
13 |
+
"输入参与者数量和分组比率,格式为用:隔开的数字,生成随机分组数据。": "Enter the number of participants and the grouping ratio in the format of numbers separated by : to generate randomized controlled trial.",
|
14 |
+
}
|
15 |
+
|
16 |
+
|
17 |
+
def _L(zh_txt: str):
|
18 |
+
if LANG:
|
19 |
+
return ZH2EN[zh_txt]
|
20 |
+
else:
|
21 |
+
return zh_txt
|
22 |
|
23 |
|
24 |
def list_to_csv(list_of_dicts: list, filename: str):
|
25 |
keys = dict(list_of_dicts[0]).keys()
|
26 |
+
# 将列表中的字典写入 CSV 文件
|
27 |
with open(filename, "w", newline="", encoding="utf-8") as csvfile:
|
28 |
writer = csv.DictWriter(csvfile, fieldnames=keys)
|
29 |
writer.writeheader()
|
30 |
for data in list_of_dicts:
|
31 |
writer.writerow(data)
|
32 |
|
|
|
|
|
33 |
|
34 |
+
def random_allocate(participants: int, ratio: list, out_csv: str):
|
|
|
35 |
splits = [0]
|
36 |
+
total = sum(ratio)
|
37 |
for i, r in enumerate(ratio):
|
38 |
splits.append(splits[i] + int(1.0 * r / total * participants))
|
39 |
|
|
|
49 |
allocation.append({"id": participant, "group": i + 1})
|
50 |
|
51 |
sorted_data = sorted(allocation, key=lambda x: x["id"])
|
52 |
+
list_to_csv(sorted_data, out_csv)
|
53 |
+
return pd.DataFrame(sorted_data)
|
54 |
|
55 |
|
56 |
+
# outer func
|
57 |
+
def infer(participants: float, ratios: str, cache=f"{TMP_DIR}/rct"):
|
|
|
|
|
|
|
|
|
58 |
ratio = []
|
59 |
+
previews = None
|
60 |
+
status = "Success"
|
61 |
+
out_csv = f"{cache}/output.csv"
|
62 |
try:
|
63 |
+
ratio_list = ratios.split(":")
|
64 |
+
clean_dir(cache)
|
65 |
for r in ratio_list:
|
66 |
current_ratio = float(r.strip())
|
67 |
if current_ratio > 0:
|
68 |
ratio.append(current_ratio)
|
69 |
|
70 |
+
previews = random_allocate(int(participants), ratio, out_csv)
|
71 |
+
|
72 |
+
except Exception as e:
|
73 |
+
out_csv = None
|
74 |
+
status = f"{e}"
|
75 |
|
76 |
+
return status, out_csv, previews
|
77 |
|
78 |
|
79 |
def rct_generator():
|
80 |
return gr.Interface(
|
81 |
fn=infer,
|
82 |
inputs=[
|
83 |
+
gr.Number(label=_L("输入参与者数量"), value=10),
|
84 |
+
gr.Textbox(label=_L("输入分组比率"), value="8:1:1"),
|
85 |
],
|
86 |
outputs=[
|
87 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
88 |
+
gr.File(label=_L("下载随机分组数据 CSV")),
|
89 |
+
gr.Dataframe(label=_L("随机分组数据预览")),
|
90 |
],
|
91 |
+
description=_L(
|
92 |
+
"输入参与者数量和分组比率,格式为用:隔开的数字,生成随机分组数据。"
|
93 |
+
),
|
94 |
flagging_mode="never",
|
95 |
)
|
modules/smtp.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
import gradio as gr
|
3 |
+
from utils import API_SMTP, LANG
|
4 |
+
|
5 |
+
ZH2EN = {
|
6 |
+
"收信人邮箱": "To email",
|
7 |
+
"标题": "Title",
|
8 |
+
"测试标题": "Test title",
|
9 |
+
"正文": "Content",
|
10 |
+
"测试正文": "Test content",
|
11 |
+
"发信人昵称": "Sender name",
|
12 |
+
"测试昵称": "Test nickname",
|
13 |
+
"发信人邮箱": "From email",
|
14 |
+
"应用密钥": "API key",
|
15 |
+
"SMTP服务器": "SMTP host",
|
16 |
+
"端口": "Port",
|
17 |
+
"发送状态": "Status",
|
18 |
+
}
|
19 |
+
|
20 |
+
|
21 |
+
def _L(zh_txt: str):
|
22 |
+
if LANG:
|
23 |
+
return ZH2EN[zh_txt]
|
24 |
+
else:
|
25 |
+
return zh_txt
|
26 |
+
|
27 |
+
|
28 |
+
def infer(target, title, content, name, email, password, host, port):
|
29 |
+
try:
|
30 |
+
response = requests.get(
|
31 |
+
API_SMTP,
|
32 |
+
params={
|
33 |
+
"host": host,
|
34 |
+
"Port": port,
|
35 |
+
"key": password, # apikey
|
36 |
+
"email": email, # from
|
37 |
+
"mail": target, # to
|
38 |
+
"title": title, # subject
|
39 |
+
"name": name, # nickname
|
40 |
+
"text": content, # content
|
41 |
+
},
|
42 |
+
)
|
43 |
+
if response.status_code == 200:
|
44 |
+
result: dict = response.json()
|
45 |
+
return result.get("status")
|
46 |
+
|
47 |
+
else:
|
48 |
+
raise ConnectionError(f"{response.status_code}")
|
49 |
+
|
50 |
+
except Exception as e:
|
51 |
+
return f"{e}"
|
52 |
+
|
53 |
+
|
54 |
+
def smtp_tester():
|
55 |
+
return gr.Interface(
|
56 |
+
fn=infer,
|
57 |
+
inputs=[
|
58 |
+
gr.Textbox(label=_L("收信人邮箱")),
|
59 |
+
gr.Textbox(label=_L("标题"), value=_L("测试标题")),
|
60 |
+
gr.TextArea(label=_L("正文"), value=_L("测试正文")),
|
61 |
+
gr.Textbox(label=_L("发信人昵称"), value=_L("测试昵称")),
|
62 |
+
gr.Textbox(label=_L("发信人邮箱")),
|
63 |
+
gr.Textbox(label=_L("应用密钥")),
|
64 |
+
gr.Textbox(label=_L("SMTP服务器"), value="smtp.163.com"),
|
65 |
+
gr.Slider(label=_L("端口"), minimum=0, maximum=65535, step=1, value=25),
|
66 |
+
],
|
67 |
+
outputs=gr.TextArea(label=_L("发送状态"), show_copy_button=True),
|
68 |
+
flagging_mode="never",
|
69 |
+
)
|
trans.py → modules/trans.py
RENAMED
@@ -1,14 +1,27 @@
|
|
1 |
-
import os
|
2 |
import json
|
3 |
import requests
|
4 |
import gradio as gr
|
5 |
-
from
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
def translate(source, direction):
|
9 |
-
if not source or not direction:
|
10 |
-
return "Please enter valid text and select the mode!"
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
# WARNING, this token is a test token for new developers, and it should be replaced by your token
|
13 |
payload = {
|
14 |
"source": source,
|
@@ -21,6 +34,9 @@ def translate(source, direction):
|
|
21 |
"x-authorization": f"token {KEY_TRANS}",
|
22 |
}
|
23 |
try:
|
|
|
|
|
|
|
24 |
response = requests.request(
|
25 |
"POST",
|
26 |
API_TRANS,
|
@@ -28,24 +44,28 @@ def translate(source, direction):
|
|
28 |
headers=headers,
|
29 |
)
|
30 |
|
31 |
-
|
32 |
|
33 |
except Exception as e:
|
34 |
-
|
|
|
|
|
35 |
|
36 |
|
37 |
def translator():
|
38 |
return gr.Interface(
|
39 |
-
fn=
|
40 |
inputs=[
|
41 |
-
gr.TextArea(label="
|
42 |
-
gr.Dropdown(choices=["auto2en", "auto2zh", "auto2ja"], label="
|
|
|
|
|
|
|
|
|
43 |
],
|
44 |
-
outputs=gr.TextArea(label="Translation results", show_copy_button=True),
|
45 |
flagging_mode="never",
|
46 |
examples=[
|
47 |
-
["彩云小译は最高の翻訳サービスです", "
|
48 |
["Lingocloud is the best translation service.", "auto2zh"],
|
49 |
],
|
50 |
-
cache_examples=False,
|
51 |
)
|
|
|
|
|
1 |
import json
|
2 |
import requests
|
3 |
import gradio as gr
|
4 |
+
from utils import API_TRANS, KEY_TRANS, LANG
|
5 |
|
6 |
+
ZH2EN = {
|
7 |
+
"输入文本区域": "Input text area",
|
8 |
+
"在这里输入文本...": "Type the text here...",
|
9 |
+
"模式": "Mode",
|
10 |
+
"翻译结果": "Translation results",
|
11 |
+
"状态栏": "Status",
|
12 |
+
}
|
13 |
|
|
|
|
|
|
|
14 |
|
15 |
+
def _L(zh_txt: str):
|
16 |
+
if LANG:
|
17 |
+
return ZH2EN[zh_txt]
|
18 |
+
else:
|
19 |
+
return zh_txt
|
20 |
+
|
21 |
+
|
22 |
+
def infer(source, direction):
|
23 |
+
status = "Success"
|
24 |
+
result = None
|
25 |
# WARNING, this token is a test token for new developers, and it should be replaced by your token
|
26 |
payload = {
|
27 |
"source": source,
|
|
|
34 |
"x-authorization": f"token {KEY_TRANS}",
|
35 |
}
|
36 |
try:
|
37 |
+
if not source or not direction:
|
38 |
+
raise ValueError("请输入有效文本并选择模式!")
|
39 |
+
|
40 |
response = requests.request(
|
41 |
"POST",
|
42 |
API_TRANS,
|
|
|
44 |
headers=headers,
|
45 |
)
|
46 |
|
47 |
+
result = json.loads(response.text)["target"]
|
48 |
|
49 |
except Exception as e:
|
50 |
+
status = f"{e}"
|
51 |
+
|
52 |
+
return status, result
|
53 |
|
54 |
|
55 |
def translator():
|
56 |
return gr.Interface(
|
57 |
+
fn=infer,
|
58 |
inputs=[
|
59 |
+
gr.TextArea(label=_L("输入文本区域"), placeholder=_L("在这里输入文本...")),
|
60 |
+
gr.Dropdown(choices=["auto2en", "auto2zh", "auto2ja"], label=_L("模式")),
|
61 |
+
],
|
62 |
+
outputs=[
|
63 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
64 |
+
gr.TextArea(label=_L("翻译结果"), show_copy_button=True),
|
65 |
],
|
|
|
66 |
flagging_mode="never",
|
67 |
examples=[
|
68 |
+
["彩云小译は最高の翻訳サービスです", "auto2zh"],
|
69 |
["Lingocloud is the best translation service.", "auto2zh"],
|
70 |
],
|
|
|
71 |
)
|
url.py → modules/url.py
RENAMED
@@ -1,83 +1,90 @@
|
|
1 |
-
import re
|
2 |
import json
|
3 |
import requests
|
4 |
import gradio as gr
|
5 |
-
from
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
13 |
|
14 |
|
15 |
def noxlink(longUrl: str):
|
16 |
domain = "https://noxlink.net"
|
17 |
api = f"{domain}/zh-CN/shorten"
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
if
|
22 |
-
|
23 |
-
if retcode["success"]:
|
24 |
-
return f"{domain}/" + retcode["message"]
|
25 |
-
|
26 |
-
return response.text
|
27 |
|
28 |
-
|
29 |
-
return f"{e}"
|
30 |
|
31 |
|
32 |
def monojson(longUrl: str):
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
except Exception as e:
|
44 |
-
return f"{e}"
|
45 |
|
46 |
|
|
|
47 |
def infer(longUrl: str, tool: str):
|
|
|
48 |
shortUrl = ""
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
55 |
|
56 |
-
|
57 |
-
|
58 |
|
59 |
-
return shortUrl
|
60 |
|
61 |
|
62 |
def url_shortner():
|
63 |
return gr.Interface(
|
64 |
fn=infer,
|
65 |
inputs=[
|
66 |
-
gr.Textbox(label="
|
67 |
gr.Dropdown(
|
68 |
choices=["noxlink", "monojson"],
|
69 |
-
label="
|
70 |
value="noxlink",
|
71 |
),
|
72 |
],
|
73 |
outputs=[
|
|
|
74 |
gr.HTML(
|
75 |
container=True,
|
76 |
show_label=True,
|
77 |
-
label="
|
78 |
-
)
|
79 |
],
|
80 |
-
description="
|
81 |
flagging_mode="never",
|
82 |
examples=[
|
83 |
["https://www.bing.com", "noxlink"],
|
|
|
|
|
1 |
import json
|
2 |
import requests
|
3 |
import gradio as gr
|
4 |
+
from utils import is_valid_url, HEADER, LANG
|
5 |
|
6 |
+
ZH2EN = {
|
7 |
+
"输入长 URL": "Input a long URL",
|
8 |
+
"输入待变短的长 URL": "Input a long URL",
|
9 |
+
"选择 API 提供商": "Select an API provider",
|
10 |
+
"输出短 URL": "Output short URL",
|
11 |
+
"将长 URL 转换为短的、易于共享的链接": "Convert long urls into short, easy-to-share links",
|
12 |
+
"状态栏": "Status",
|
13 |
+
}
|
14 |
|
15 |
+
|
16 |
+
def _L(zh_txt: str):
|
17 |
+
if LANG:
|
18 |
+
return ZH2EN[zh_txt]
|
19 |
+
else:
|
20 |
+
return zh_txt
|
21 |
|
22 |
|
23 |
def noxlink(longUrl: str):
|
24 |
domain = "https://noxlink.net"
|
25 |
api = f"{domain}/zh-CN/shorten"
|
26 |
+
response = requests.post(api, json={"longUrl": longUrl}, headers=HEADER)
|
27 |
+
if response.status_code == 200:
|
28 |
+
retcode = json.loads(response.text)
|
29 |
+
if retcode["success"]:
|
30 |
+
return f"{domain}/" + retcode["message"]
|
|
|
|
|
|
|
|
|
31 |
|
32 |
+
raise ConnectionError(response.text)
|
|
|
33 |
|
34 |
|
35 |
def monojson(longUrl: str):
|
36 |
+
response = requests.post(
|
37 |
+
"https://monojson.com/api/short-link",
|
38 |
+
json={"url": longUrl},
|
39 |
+
headers=HEADER,
|
40 |
+
)
|
41 |
+
if response.status_code == 200:
|
42 |
+
return json.loads(response.text)["shortUrl"]
|
43 |
+
else:
|
44 |
+
raise ConnectionError(response.text)
|
|
|
|
|
|
|
45 |
|
46 |
|
47 |
+
# outer func
|
48 |
def infer(longUrl: str, tool: str):
|
49 |
+
status = "Success"
|
50 |
shortUrl = ""
|
51 |
+
try:
|
52 |
+
if tool == "monojson":
|
53 |
+
shortUrl = monojson(longUrl)
|
54 |
+
elif tool == "noxlink":
|
55 |
+
shortUrl = noxlink(longUrl)
|
56 |
+
else:
|
57 |
+
raise ValueError("请选择一个 API 提供商!")
|
58 |
+
|
59 |
+
if is_valid_url(shortUrl):
|
60 |
+
shortUrl = f'<a href="{shortUrl}" target="_blank">{shortUrl}</a>'
|
61 |
|
62 |
+
except Exception as e:
|
63 |
+
status = f"{e}"
|
64 |
|
65 |
+
return status, shortUrl
|
66 |
|
67 |
|
68 |
def url_shortner():
|
69 |
return gr.Interface(
|
70 |
fn=infer,
|
71 |
inputs=[
|
72 |
+
gr.Textbox(label=_L("输入长 URL"), placeholder=_L("输入待变短的长 URL")),
|
73 |
gr.Dropdown(
|
74 |
choices=["noxlink", "monojson"],
|
75 |
+
label=_L("选择 API 提供商"),
|
76 |
value="noxlink",
|
77 |
),
|
78 |
],
|
79 |
outputs=[
|
80 |
+
gr.Textbox(label=_L("状态栏"), show_copy_button=True),
|
81 |
gr.HTML(
|
82 |
container=True,
|
83 |
show_label=True,
|
84 |
+
label=_L("输出短 URL"),
|
85 |
+
),
|
86 |
],
|
87 |
+
description=_L("将长 URL 转换为短的、易于共享的链接"),
|
88 |
flagging_mode="never",
|
89 |
examples=[
|
90 |
["https://www.bing.com", "noxlink"],
|
qr.py
DELETED
@@ -1,21 +0,0 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
|
3 |
-
|
4 |
-
def infer(img_size: int, input_txt: str):
|
5 |
-
if not input_txt:
|
6 |
-
return None
|
7 |
-
|
8 |
-
return f"https://api.qrserver.com/v1/create-qr-code/?size={img_size}x{img_size}&data={input_txt}"
|
9 |
-
|
10 |
-
|
11 |
-
def qrcode():
|
12 |
-
return gr.Interface(
|
13 |
-
fn=infer,
|
14 |
-
inputs=[
|
15 |
-
gr.Slider(35, 1000, 217, label="Image size"),
|
16 |
-
gr.Textbox(label="Input text"),
|
17 |
-
],
|
18 |
-
outputs=gr.Image(label="QR code", show_share_button=False),
|
19 |
-
description="Enter text to generate a QR code.",
|
20 |
-
flagging_mode="never",
|
21 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
smtp.py
DELETED
@@ -1,53 +0,0 @@
|
|
1 |
-
import requests
|
2 |
-
import gradio as gr
|
3 |
-
from config import API_SMTP
|
4 |
-
|
5 |
-
|
6 |
-
def send_email(
|
7 |
-
target: str,
|
8 |
-
title: str,
|
9 |
-
content: str,
|
10 |
-
name: str,
|
11 |
-
email: str,
|
12 |
-
password: str,
|
13 |
-
host: str,
|
14 |
-
port: int,
|
15 |
-
):
|
16 |
-
response = requests.get(
|
17 |
-
API_SMTP,
|
18 |
-
params={
|
19 |
-
"host": host,
|
20 |
-
"Port": port,
|
21 |
-
"key": password, # apikey
|
22 |
-
"email": email, # from
|
23 |
-
"mail": target, # to
|
24 |
-
"title": title, # subject
|
25 |
-
"name": name, # nickname
|
26 |
-
"text": content, # content
|
27 |
-
},
|
28 |
-
)
|
29 |
-
if response.status_code == 200:
|
30 |
-
result: dict = response.json()
|
31 |
-
return result.get("status")
|
32 |
-
|
33 |
-
else:
|
34 |
-
return f"Request failed with status code: {response.status_code}"
|
35 |
-
|
36 |
-
|
37 |
-
def smtp_tester():
|
38 |
-
return gr.Interface(
|
39 |
-
fn=send_email,
|
40 |
-
inputs=[
|
41 |
-
gr.Textbox(label="To email"),
|
42 |
-
gr.Textbox(label="Title", value="Test title"),
|
43 |
-
gr.TextArea(label="Content", value="Test content"),
|
44 |
-
gr.Textbox(label="Sender name", value="Test nickname"),
|
45 |
-
gr.Textbox(label="From email"),
|
46 |
-
gr.Textbox(label="API key"),
|
47 |
-
gr.Textbox(label="SMTP host", value="smtp.163.com"),
|
48 |
-
gr.Slider(label="Port", minimum=0, maximum=65535, step=1, value=25),
|
49 |
-
],
|
50 |
-
outputs=gr.Textbox(label="status", show_copy_button=True),
|
51 |
-
description="SMTP Online Test Tool",
|
52 |
-
flagging_mode="never",
|
53 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import re
|
3 |
+
import shutil
|
4 |
+
import zipfile
|
5 |
+
|
6 |
+
LANG = os.getenv("language")
|
7 |
+
API_SMTP = os.getenv("api_smtp")
|
8 |
+
API_TRANS = os.getenv("api_caiyun")
|
9 |
+
KEY_TRANS = os.getenv("apikey_caiyun")
|
10 |
+
if not (API_SMTP and API_TRANS and KEY_TRANS):
|
11 |
+
print("请检查环境变量")
|
12 |
+
exit()
|
13 |
+
|
14 |
+
|
15 |
+
TMP_DIR = "./__pycache__"
|
16 |
+
HEADER = {
|
17 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0",
|
18 |
+
}
|
19 |
+
|
20 |
+
|
21 |
+
def mk_dir(dir_path: str):
|
22 |
+
if not os.path.exists(dir_path):
|
23 |
+
os.makedirs(dir_path)
|
24 |
+
|
25 |
+
|
26 |
+
def rm_dir(dir_path: str):
|
27 |
+
if os.path.exists(dir_path):
|
28 |
+
shutil.rmtree(dir_path)
|
29 |
+
|
30 |
+
|
31 |
+
def clean_dir(dir_path: str):
|
32 |
+
rm_dir(dir_path)
|
33 |
+
os.makedirs(dir_path)
|
34 |
+
|
35 |
+
|
36 |
+
def unzip(zip_path: str, extract_to: str):
|
37 |
+
mk_dir(extract_to)
|
38 |
+
# 打开ZIP文件
|
39 |
+
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
40 |
+
# 解压文件
|
41 |
+
zip_ref.extractall(extract_to)
|
42 |
+
|
43 |
+
|
44 |
+
def compress(folder_path: str, zip_file: str):
|
45 |
+
# 确保文件夹存在
|
46 |
+
if not os.path.exists(folder_path):
|
47 |
+
raise ValueError(f"错误: 文件夹 '{folder_path}' 不存在")
|
48 |
+
# 打开 ZIP 文件,使用 'w' 模式表示写入
|
49 |
+
with zipfile.ZipFile(zip_file, "w", zipfile.ZIP_DEFLATED) as zipf:
|
50 |
+
# 遍历文件夹中的文件和子文件夹
|
51 |
+
for root, _, files in os.walk(folder_path):
|
52 |
+
for file in files:
|
53 |
+
file_path = os.path.join(root, file)
|
54 |
+
# 计算相对路径,保留文件夹的根目录
|
55 |
+
relative_path = os.path.relpath(file_path, folder_path)
|
56 |
+
zipf.write(
|
57 |
+
file_path,
|
58 |
+
arcname=os.path.join(os.path.basename(folder_path), relative_path),
|
59 |
+
)
|
60 |
+
|
61 |
+
|
62 |
+
def is_valid_url(url):
|
63 |
+
# 定义 URL 的正则表达式
|
64 |
+
pattern = re.compile(
|
65 |
+
r"^(https?://)?" # 协议(http 或 https,可选)
|
66 |
+
r"([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}" # 域名
|
67 |
+
r"(:\d+)?" # 端口号(可选)
|
68 |
+
r"(/[^ ]*)?$" # 路径(可选)
|
69 |
+
)
|
70 |
+
# 使用正则表达式匹配 URL
|
71 |
+
return bool(pattern.match(url))
|