Skip to content

Commit b52b06c

Browse files
committed
feat: re-add parse_and_copy function
Signed-off-by: 晨叶梦春 <65224318+wuziqian211@users.noreply.github.com>
1 parent 9677777 commit b52b06c

File tree

1 file changed

+50
-12
lines changed

1 file changed

+50
-12
lines changed

src/tchMaterial-parser.pyw

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ from tkinter import ttk, messagebox, filedialog
1010
import os, platform
1111
import sys
1212
from functools import partial
13-
import base64, tempfile
13+
import base64, tempfile, pyperclip
1414
import threading, requests, psutil
1515
import json, re
1616

@@ -61,7 +61,7 @@ def parse(url: str) -> tuple[str, str, str] | tuple[None, None, None]: # 解析
6161
"""
6262
# 其中 $.ti_items 的每一项对应一个资源
6363

64-
if re.search(r"^https?://([^/]+)/syncClassroom/basicWork/detail", url): # 对于“基础性作业”的解析
64+
if re.search(r"^https?://([^/]+)/syncClassroom/basicWork/detail", url): # 对于 “基础性作业” 的解析
6565
response = session.get(f"https://s-file-1.ykt.cbern.com.cn/zxx/ndrs/special_edu/resources/details/{content_id}.json")
6666
else: # 对于课本的解析
6767
if content_type == "thematic_course": # 对专题课程(含电子课本、视频等)的解析
@@ -105,8 +105,8 @@ def download_file(url: str, save_path: str) -> None: # 下载文件
105105
# 服务器返回 401 或 403 状态码
106106
if response.status_code == 401 or response.status_code == 403:
107107
messagebox.showerror("授权失败", "Access Token 可能已过期或无效,请重新设置后再试!")
108-
open_access_token_window()
109-
download_btn.config(state="normal") # 当弹出 “设置 token” 窗口后,恢复下载按钮
108+
show_access_token_window()
109+
download_btn.config(state="normal") # 当弹出 “设置 Token” 窗口后,恢复下载按钮
110110
return
111111
if response.status_code >= 400:
112112
messagebox.showerror("下载失败", f"下载失败,服务器返回状态码:{response.status_code}")
@@ -158,6 +158,25 @@ def format_bytes(size: float) -> str: # 将数据单位进行格式化,返回
158158
size /= 1024.0
159159
return f"{size:3.1f} PB"
160160

161+
def parse_and_copy() -> None: # 解析并复制链接
162+
urls = [line.strip() for line in url_text.get("1.0", tk.END).splitlines() if line.strip()] # 获取所有非空行
163+
resource_links = []
164+
failed_links = []
165+
166+
for url in urls:
167+
resource_url = parse(url)[0]
168+
if not resource_url:
169+
failed_links.append(url) # 添加到失败链接
170+
continue
171+
resource_links.append(resource_url)
172+
173+
if failed_links:
174+
messagebox.showwarning("警告", "以下 “行” 无法解析:\n" + "\n".join(failed_links)) # 显示警告对话框
175+
176+
if resource_links:
177+
pyperclip.copy("\n".join(resource_links)) # 将链接复制到剪贴板
178+
messagebox.showinfo("提示", "资源链接已复制到剪贴板")
179+
161180
def download() -> None: # 下载资源文件
162181
global download_states
163182
download_btn.config(state="disabled") # 设置下载按钮为禁用状态
@@ -197,18 +216,20 @@ def download() -> None: # 下载资源文件
197216
thread_it(download_file, (resource_url, save_path)) # 开始下载(多线程,防止窗口卡死)
198217

199218
if failed_links:
200-
messagebox.showwarning("警告", "以下“行”无法解析:\n" + "\n".join(failed_links)) # 显示警告对话框
219+
messagebox.showwarning("警告", "以下 “行” 无法解析:\n" + "\n".join(failed_links)) # 显示警告对话框
201220
download_btn.config(state="normal") # 设置下载按钮为启用状态
202221

203222
if not urls and not failed_links:
204223
download_btn.config(state="normal") # 设置下载按钮为启用状态
205224

206-
def open_access_token_window() -> None: # 打开输入 Access Token 的窗口
225+
def show_access_token_window() -> None: # 打开输入 Access Token 的窗口
207226
token_window = tk.Toplevel(root)
208227
token_window.title("设置 Access Token")
209228
# 让窗口自动根据控件自适应尺寸;如需最小尺寸可用 token_window.minsize(...)
210229

211-
token_window.protocol("WM_DELETE_WINDOW", lambda: token_window.destroy())
230+
token_window.focus_force() # 自动获得焦点
231+
token_window.grab_set() # 阻止主窗口操作
232+
token_window.bind("<Escape>", lambda event: token_window.destroy()) # 绑定 Esc 键关闭窗口
212233

213234
# 设置一个 Frame 用于留白、布局更美观
214235
frame = ttk.Frame(token_window, padding=20)
@@ -252,7 +273,7 @@ def open_access_token_window() -> None: # 打开输入 Access Token 的窗口
252273
def save_token():
253274
user_token = token_text.get("1.0", tk.END).strip()
254275
tip_info = set_access_token(user_token)
255-
# 重新启用“下载”按钮,并提示用户
276+
# 重新启用下载按钮,并提示用户
256277
download_btn.config(state="normal")
257278
# 显示提示
258279
messagebox.showinfo("提示", tip_info)
@@ -266,14 +287,21 @@ def open_access_token_window() -> None: # 打开输入 Access Token 的窗口
266287
def show_token_help():
267288
help_win = tk.Toplevel(token_window)
268289
help_win.title("获取 Access Token 方法")
290+
291+
help_win.focus_force() # 自动获得焦点
292+
help_win.grab_set() # 阻止主窗口操作
293+
help_win.bind("<Escape>", lambda event: help_win.destroy()) # 绑定 Esc 键关闭窗口
294+
269295
help_frame = ttk.Frame(help_win, padding=20)
270296
help_frame.pack(fill="both", expand=True)
271297

272298
help_text = """\
273-
自 2025 年 2 月起,国家中小学智慧教育平台需要登录后才可获取教材,因此要使用本程序下载教材,您需要在平台内登录账号(如没有需注册),然后获得登录凭据(Access Token)。本程序仅保存该凭据至本地。
299+
国家中小学智慧教育平台需要登录后才可获取教材,因此要使用本程序下载教材,您需要在平台内登录账号(如没有需注册),然后获得登录凭据(Access Token)。本程序仅保存该凭据至本地。
274300
275301
获取方法如下:
276-
请先在浏览器登录国家中小学智慧教育平台(https://auth.smartedu.cn/uias/login),然后按 F12 或 Ctrl+Shift+I 或 右键-检查(审查元素),打开开发人员工具,点击“控制台(Console)”选项卡,在里面粘贴以下代码后回车(Enter):
302+
1. 打开浏览器,访问国家中小学智慧教育平台(https://auth.smartedu.cn/uias/login)并登录账号。
303+
2. 按下 F12 或 Ctrl+Shift+I,或右键——检查(审查元素)打开开发者工具,选择控制台(Console)。
304+
3. 在控制台粘贴以下代码后回车(Enter):
277305
---------------------------------------------------------
278306
(function() {
279307
const authKey = Object.keys(localStorage).find(key => key.startsWith("ND_UC_AUTH"));
@@ -307,7 +335,7 @@ def open_access_token_window() -> None: # 打开输入 Access Token 的窗口
307335
help_btn = ttk.Button(frame, text="如何获取?", command=show_token_help)
308336
help_btn.pack(pady=5)
309337

310-
# 让弹窗大致居中
338+
# 让弹窗居中
311339
token_window.update_idletasks()
312340
w = token_window.winfo_width()
313341
h = token_window.winfo_height()
@@ -412,6 +440,8 @@ def thread_it(func, args: tuple = ()) -> None: # args 为元组,且默认值
412440

413441
# 初始化请求
414442
session = requests.Session()
443+
# 初始化下载状态
444+
download_states = []
415445
# 设置请求头部,包含认证信息
416446
access_token = None
417447
headers = { "X-ND-AUTH": 'MAC id="0",nonce="0",mac="0"' } # “MAC id”等同于“access_token”,“nonce”和“mac”不可缺省但无需有效
@@ -532,6 +562,10 @@ def set_icon() -> None: # 设置窗口图标
532562
thread_it(set_icon) # 设置窗口图标耗时较长,为不影响窗口绘制,采用多线程
533563

534564
def on_closing() -> None: # 处理窗口关闭事件
565+
if not all(state["finished"] for state in download_states): # 当正在下载时,询问用户
566+
if not messagebox.askokcancel("提示", "下载任务未完成,是否退出?"):
567+
return
568+
535569
current_process = psutil.Process(os.getpid()) # 获取自身的进程 ID
536570
child_processes = current_process.children(recursive=True) # 获取自身的所有子进程
537571

@@ -687,13 +721,17 @@ for i in range(8):
687721
drops.append(drop)
688722

689723
# 按钮:设置 Token
690-
token_btn = ttk.Button(container_frame, text="设置 Token", command=open_access_token_window)
724+
token_btn = ttk.Button(container_frame, text="设置 Token", command=show_access_token_window)
691725
token_btn.pack(side="left", padx=int(5 * scale), pady=int(5 * scale), ipady=int(5 * scale))
692726

693727
# 按钮:下载
694728
download_btn = ttk.Button(container_frame, text="下载", command=download)
695729
download_btn.pack(side="right", padx=int(5 * scale), pady=int(5 * scale), ipady=int(5 * scale))
696730

731+
# 按钮:解析并复制
732+
copy_btn = ttk.Button(container_frame, text="解析并复制", command=parse_and_copy)
733+
copy_btn.pack(side="right", padx=int(5 * scale), pady=int(5 * scale), ipady=int(5 * scale))
734+
697735
# 下载进度条
698736
download_progress_bar = ttk.Progressbar(container_frame, length=(125 * scale), mode="determinate") # 添加下载进度条
699737
download_progress_bar.pack(side="bottom", padx=int(40 * scale), pady=int(10 * scale), ipady=int(5 * scale)) # 设置水平外边距、垂直外边距(跟随缩放),设置进度条高度(跟随缩放)

0 commit comments

Comments
 (0)