Skip to content

Commit 35de5c1

Browse files
committed
v1.0.0
1 parent 3dc6da5 commit 35de5c1

File tree

3 files changed

+217
-0
lines changed

3 files changed

+217
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.json
2+
.idea/*

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
requests

test.py

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import json
2+
import queue
3+
import re
4+
import subprocess
5+
import sys
6+
from datetime import datetime
7+
8+
import requests
9+
10+
11+
def get_cookie(phone, password):
12+
url = "https://www.iqihang.com/api/ark/sso/login"
13+
headers = {
14+
"Content-Type": "application/json",
15+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
16+
}
17+
data = {"loginType": 2, "clientType": 2, "account": phone, "password": password}
18+
response = requests.post(url, headers=headers, json=data, timeout=5)
19+
return response.json()["data"]["token"]
20+
21+
22+
# def get_user_id(cookie):
23+
# url = "https://www.iqihang.com/api/ark/web/v1/user/info"
24+
# headers = {
25+
# "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
26+
# "Authorization": f"Bearer {cookie}",
27+
# }
28+
# response = requests.get(url, headers=headers, timeout=5)
29+
# data = response.json()["data"]
30+
# return data["id"]
31+
32+
33+
def get_course_list(cookie):
34+
url = "https://www.iqihang.com/api/ark/web/v1/user/course/course-list?isMarketingCourse=0&type=1"
35+
headers = {
36+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
37+
"Authorization": f"Bearer {cookie}",
38+
}
39+
response = requests.get(url, headers=headers, timeout=5)
40+
data = response.json()["data"]
41+
course_list = []
42+
for course in data:
43+
course_dict = {
44+
"id": course["id"],
45+
"name": course["productName"],
46+
"progress_id": course["lastLearningChapterId"] or None,
47+
"progress_name": course["lastLearningChapterName"] or None,
48+
"user_id": course["userId"],
49+
"product_id": course["productId"],
50+
"skuId": course["skuId"],
51+
"catalog": course["productCurriculumId"],
52+
}
53+
course_list.append(course_dict)
54+
return course_list
55+
56+
57+
def get_lesson_tree(course_catalog, cookie):
58+
url = f"https://www.iqihang.com/api/ark/web/v1/course/catalog/{course_catalog}"
59+
headers = {
60+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
61+
"Authorization": f"Bearer {cookie}",
62+
}
63+
response = requests.get(url, headers=headers, timeout=5)
64+
data = response.json()["data"]
65+
lesson_tree = []
66+
for tree in data["courseNodes"]:
67+
root = {
68+
"id": tree["id"],
69+
"name": tree["name"],
70+
"children": tree["children"],
71+
"vid": tree["resourceList"][0]["vid"] if tree["resourceList"] else None,
72+
}
73+
node_queue = queue.Queue()
74+
node_queue.put(root)
75+
while not node_queue.empty():
76+
parent = node_queue.get()
77+
children = parent["children"]
78+
parent["children"] = []
79+
if not children:
80+
continue
81+
for node in children:
82+
child = {
83+
"id": node["id"],
84+
"name": node["name"],
85+
"children": node["children"],
86+
"vid": (
87+
node["resourceList"][0]["vid"] if node["resourceList"] else None
88+
),
89+
}
90+
node_queue.put(child)
91+
parent["children"].append(child)
92+
lesson_tree.append(root)
93+
return lesson_tree
94+
95+
96+
def choose_lesson(children):
97+
for index, lesson in enumerate(children):
98+
if len(lesson["children"]) == 0 and not lesson["vid"]:
99+
print(f"Index: {index}, Name: {lesson["name"]}, 暂不支持处理直播回放")
100+
continue
101+
print(f"Index: {index}, Name: {lesson["name"]}")
102+
lesson_index = int(input("选择课程:"))
103+
print()
104+
lesson = children[lesson_index]
105+
if len(lesson["children"]) == 0 and not lesson["vid"]:
106+
return None
107+
if len(lesson["children"]) == 0:
108+
return lesson
109+
return choose_lesson(lesson["children"])
110+
111+
112+
def get_video_url(vid, cookie):
113+
url = "https://p.bokecc.com/servlet/getvideofile"
114+
headers = {
115+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
116+
"Authorization": f"Bearer {cookie}",
117+
}
118+
params = {
119+
"vid": vid,
120+
"siteid": "A183AC83A2983CCC",
121+
}
122+
response = requests.get(url, headers=headers, params=params, timeout=5)
123+
match = re.search(r"\((.*)\)", response.text)
124+
if not match:
125+
return None
126+
json_str = match.group(1)
127+
data = json.loads(json_str)
128+
copies = data["copies"]
129+
for copy in copies:
130+
if copy["desp"] == "1080P":
131+
return copy["playurl"]
132+
return None
133+
134+
135+
def finish_progress(course, lesson, cookie):
136+
url = "https://www.iqihang.com/api/ark/report/v1/user/course/user/learn/new/report"
137+
headers = {
138+
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
139+
"Authorization": f"Bearer {cookie}",
140+
}
141+
data = {
142+
"userId": course["user_id"],
143+
"productId": course["product_id"],
144+
"productSkuId": course["skuId"],
145+
"userProductId": course["id"],
146+
"curriculumId": course["catalog"],
147+
"chapterId": lesson["id"],
148+
"chapterName": lesson["name"],
149+
"sourceType": 3,
150+
"position": 0,
151+
"learnProgress": "1.00",
152+
"reportTime": 0,
153+
"isDrag": 1,
154+
"source": 4,
155+
"updateTime": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
156+
}
157+
requests.post(url, headers=headers, json=data, timeout=5)
158+
159+
160+
if __name__ == "__main__":
161+
with open("config.json", "r", encoding="utf8") as file:
162+
config = json.load(file)
163+
cookie = get_cookie(config["phone"], config["password"])
164+
course_list = get_course_list(cookie)
165+
for index, course in enumerate(course_list):
166+
print(
167+
f"Index: {index}, Name: {course["name"]}, 上次学到:{course["progress_name"] or "无"}"
168+
)
169+
course_index = int(input("选择课程:"))
170+
print()
171+
course_catalog = course_list[course_index]["catalog"]
172+
lesson_tree = get_lesson_tree(course_catalog, cookie)
173+
lesson = choose_lesson(lesson_tree)
174+
if lesson is None:
175+
print("暂不支持处理直播回放")
176+
sys.exit()
177+
video_url = get_video_url(lesson["vid"], cookie)
178+
if not video_url:
179+
print("获取视频源出错")
180+
sys.exit()
181+
print("Index: 0, 使用mpv播放课程")
182+
print("Index: 1, 下载课程")
183+
print("Index: 2, 标记课程为已学习")
184+
user_option = int(input("选择要进行的操作:"))
185+
print()
186+
match user_option:
187+
case 0:
188+
print("调用mpv播放器中...")
189+
subprocess.run(
190+
[
191+
str(config["mpv_path"]),
192+
str(video_url),
193+
f"--force-media-title={lesson["name"]}",
194+
],
195+
check=False,
196+
)
197+
case 1:
198+
print("调用ffmpeg中...")
199+
subprocess.run(
200+
[
201+
str(config["ffmpeg_path"]),
202+
"-i",
203+
str(video_url),
204+
"-c",
205+
"copy",
206+
f"{lesson["name"]}.mp4",
207+
],
208+
check=False,
209+
)
210+
case 2:
211+
finish_progress(course_list[course_index], lesson, cookie)
212+
print("标记完成")
213+
case _:
214+
pass

0 commit comments

Comments
 (0)