Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.3 #2

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Python application

on:
push:
branches: [ "main" ]
branches: [ "main", "pre-release" ]
pull_request:
branches: [ "main" ]

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

电子课本预览页面的网址格式如 <https://basic.smartedu.cn/tchMaterial/detail?contentType=assets_document&contentId=b8e9a3fe-dae7-49c0-86cb-d146f883fd8e&catalogType=tchMaterial&subCatalog=tchMaterial>。

**本工具暂时仅支持 Windows 操作系统。**
本工具支持 Windows、Linux 等操作系统。
wuziqian211 marked this conversation as resolved.
Show resolved Hide resolved

## 工具特点

Expand Down
Binary file added releases/v1.3/tchMaterial-parser.exe
Binary file not shown.
167 changes: 167 additions & 0 deletions releases/v1.3/tchMaterial-parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# -*- coding: utf-8 -*-
# 国家中小学智慧教育平台 电子课本下载工具 v1.3
# 作者:肥宅水水呀(https://space.bilibili.com/324042405)
# wuziqian211(https://space.bilibili.com/425503913)

# 导入相关库
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading
import requests
import os
import platform
import json
import pyperclip

# 获取操作系统类型
os_name = platform.system()

# 如果是Windows操作系统,导入Windows相关库
if os_name == 'Windows':
import win32print, win32gui, win32con, win32api, ctypes

# 解析URL
def parse(url):
try:
# 简单提取URL中的contentId(这种方法不严谨,但为了减少导入的库只能这样了)
for q in url[url.find("?") + 1:].split("&"):
if q.split("=")[0] == "contentId":
contentId = q.split("=")[1]
break

response = requests.get(f"https://s-file-1.ykt.cbern.com.cn/zxx/ndrv2/resources/tch_material/details/{contentId}.json")
data = json.loads(response.text)
for item in list(data["ti_items"]):
if item["lc_ti_format"] == "pdf": # 找到存有PDF链接列表的项
pdf_url = item["ti_storages"][0].replace("-private", "") # 获取并构建PDF的URL
break

return pdf_url, contentId
except:
return None, None # 如果解析失败,返回None

# 获取默认文件名
def getDefaultFilename(contentId):
response = requests.get(f"https://s-file-1.ykt.cbern.com.cn/zxx/ndrv2/resources/tch_material/details/{contentId}.json")
try:
data = json.loads(response.text)
return data["title"] # 返回教材标题
except:
return None

# 下载文件的函数
def downloadFile(url, save_path):
response = requests.get(url, stream=True)
with open(save_path, "wb") as file:
for chunk in response.iter_content(chunk_size=8192): # 分块下载
file.write(chunk)
messagebox.showinfo("完成", f"文件已下载到:{save_path}") # 显示完成对话框

# 解析并复制链接的函数
def parseAndCopy():
urls = [line.strip() for line in url_text.get("1.0", tk.END).splitlines() if line.strip()] # 获取所有非空行
pdf_links = []
failed_links = []

for url in urls:
pdf_url = parse(url)[0]
if not pdf_url:
failed_links.append(url) # 添加到失败链接
continue
pdf_links.append(pdf_url)

if failed_links:
failed_msg = "以下链接无法解析:\n" + "\n".join(failed_links)
messagebox.showwarning("警告", failed_msg) # 显示警告对话框
if pdf_links:
pyperclip.copy("\n".join(pdf_links)) # 将链接复制到剪贴板
messagebox.showinfo("提示", "PDF链接已复制到剪贴板")

# 下载PDF文件的函数
def download():
urls = [line.strip() for line in url_text.get("1.0", tk.END).splitlines() if line.strip()] # 获取所有非空行
failed_links = []

if len(urls) > 1:
messagebox.showinfo("提示", "您选择了多个链接,将在选定的文件夹中使用教材名称作为文件名进行下载。")
dir_path = filedialog.askdirectory() # 选择文件夹
if os_name == 'Windows':
dir_path = dir_path.replace("/", "\\")
if not dir_path:
return
else:
dir_path = None

for url in urls:
pdf_url, contentId = parse(url)
if not pdf_url:
failed_links.append(url) # 添加到失败链接
continue

if dir_path:
default_filename = getDefaultFilename(contentId) or "download"
save_path = os.path.join(dir_path, f"{default_filename}.pdf") # 构造完整路径
else:
default_filename = getDefaultFilename(contentId) or "download"
save_path = filedialog.asksaveasfilename(defaultextension=".pdf", filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")], initialfile=default_filename) # 选择保存路径
if os_name == 'Windows':
save_path = save_path.replace("/", "\\")

if not save_path:
return

threading.Thread(target=downloadFile, args=(pdf_url, save_path)).start() # 开始下载

if failed_links:
failed_msg = "以下链接无法解析:\n" + "\n".join(failed_links)
messagebox.showwarning("警告", failed_msg) # 显示警告对话框

# GUI
root = tk.Tk()

# ----------高DPI适配start---------
if os_name == 'Windows':
scale = round(win32print.GetDeviceCaps(win32gui.GetDC(0), win32con.DESKTOPHORZRES) / win32api.GetSystemMetrics(0), 2) # 获取屏幕缩放比例

# 调用api设置成由应用程序缩放
try: # 系统版本 >= Win 8.1
ctypes.windll.shcore.SetProcessDpiAwareness(2)
except: # 系统版本 <= Win 8.0
ctypes.windll.user32.SetProcessDPIAware()
else:
scale = 1

# 设置缩放因子
root.tk.call("tk", "scaling", scale / 0.75)

# ----------高DPI适配end---------

root.title("国家中小学智慧教育平台 电子课本解析") # 设置窗口标题
# root.geometry("900x600") # 设置窗口大小

# 创建一个容器框架
container_frame = ttk.Frame(root)
container_frame.pack(anchor="center", expand="yes", padx=int(20 * scale), pady=int(20 * scale)) # 容器的中心位置放置,允许组件在容器中扩展,水平外边距40,垂直外边距40

title_label = ttk.Label(container_frame, text="国家中小学智慧教育平台 电子课本解析", font=("微软雅黑", 16, "bold")) # 添加标题标签
title_label.pack(pady=int(5 * scale)) # 设置垂直外边距(跟随缩放)

description = """请在下面的文本框中粘贴一个或多个课本原网址(支持批量每个URL一行)。
例如:
https://basic.smartedu.cn/tchMaterial/detail?contentType=
assets_document&contentId=b8e9a3fe-dae7-49c0-86cb-d146
f883fd8e&catalogType=tchMaterial&subCatalog=tchMaterial
点击下载按钮后,程序会解析并下载所有PDF文件。"""
description_label = ttk.Label(container_frame, text=description, justify="left") # 添加描述标签
description_label.pack(pady=int(5 * scale)) # 设置垂直外边距(跟随缩放)

url_text = tk.Text(container_frame, width=70, height=12) # 添加URL输入框,长度和宽度不使用缩放!!!
url_text.pack(padx=int(15 * scale), pady=int(15 * scale)) # 设置水平外边距、垂直外边距(跟随缩放)

download_btn = ttk.Button(container_frame, text="下载", command=download) # 添加下载按钮
download_btn.pack(side="left", padx=int(40 * scale), pady=int(5 * scale), ipady=int(5 * scale)) # 设置水平外边距、垂直外边距(跟随缩放),设置按钮高度(跟随缩放)

copy_btn = ttk.Button(container_frame, text="解析并复制", command=parseAndCopy) # 添加“解析并复制”按钮
copy_btn.pack(side="right", padx=int(40 * scale), pady=int(5 * scale), ipady=int(5 * scale)) # 设置水平外边距、垂直外边距(跟随缩放),设置按钮高度(跟随缩放)

root.mainloop() # 开始主循环