-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapi.py
More file actions
162 lines (137 loc) · 6.38 KB
/
Copy pathapi.py
File metadata and controls
162 lines (137 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""橙啦课程下载器 - API 客户端
负责与橙啦(clapp.orangevip.com)后端 API 通信
"""
import os
import requests
from config import (
COURSE_LIST_URL, COURSE_DETAIL_URL,
PERIOD_LIST_URL, REVIEW_PLAY_INFO_URL, DEFAULT_HEADERS
)
class OrangeAPI:
"""橙啦 API 客户端"""
def __init__(self, cookies: dict):
"""
Args:
cookies: 登录后的 Cookie 字典 {"ClubAuth": "xxx", "access_token": "xxx", ...}
"""
self.session = requests.Session()
self.session.headers.update(DEFAULT_HEADERS)
self.session.cookies.update(cookies)
# ─────────────────────────────────────────────
# 1. 课程列表
# ─────────────────────────────────────────────
def get_course_list(self) -> list:
"""获取用户的课程列表
Returns:
课程列表, 每个元素包含:
- guid: 课程ID (如 57374)
- courseName: 课程名称
- totalPeriodCount: 总课时数
- learnedCount: 已学课时
- courseType: 课程类型
- busId: 业务ID
"""
resp = self.session.post(COURSE_LIST_URL)
resp.raise_for_status()
data = resp.json()
if data.get("success") != 1000:
raise Exception(f"获取课程列表失败: {data.get('msg')}")
return data.get("courseList", [])
# ─────────────────────────────────────────────
# 2. 课程详情(章节结构)
# ─────────────────────────────────────────────
def get_course_detail(self, course_id: int) -> dict:
"""获取课程详情,包含章节结构
Args:
course_id: 课程ID (guid)
Returns:
包含 chapterClass (章节列表) 和 courseChapterList 的字典
"""
resp = self.session.post(COURSE_DETAIL_URL, data={"courseModelId": course_id})
resp.raise_for_status()
data = resp.json()
if data.get("success") != 1000:
raise Exception(f"获取课程详情失败: {data.get('msg')}")
return data
# ─────────────────────────────────────────────
# 3. 课时列表(完整版:从课程详情中提取所有课时)
# ─────────────────────────────────────────────
def get_period_list(self, course_id: int, detail: dict = None) -> list:
"""获取课程的所有课时(从 courseDetail 的章节结构中提取)
Args:
course_id: 课程ID
detail: 预获取的课程详情(避免重复调用 API)
Returns:
所有课时列表
"""
if detail is None:
detail = self.get_course_detail(course_id)
all_periods = []
# 方法1:从 courseChapterList 提取(每个章节下有课时列表)
for chapter in detail.get("courseChapterList", []):
chapter_name = chapter.get("chapterName", "未分类")
for period in chapter.get("coursePeriodList", []):
period["_chapterName"] = chapter_name
all_periods.append(period)
# 方法2:如果 courseChapterList 为空,用 periodList API
if not all_periods:
resp = self.session.post(PERIOD_LIST_URL, data={"courseModelId": course_id})
resp.raise_for_status()
data = resp.json()
if data.get("success") == 1000:
all_periods = data.get("coursePeriodList", [])
return all_periods
# ─────────────────────────────────────────────
# 4. 播放信息(roomId + token)
# ─────────────────────────────────────────────
def get_review_play_info(self, course_id: int, period_id: int) -> dict:
"""获取课时的播放信息
Args:
course_id: 课程ID
period_id: 课时ID (guid)
Returns:
classInfo: {roomId, token, periodName, videoId}
userInfo: {userId, userName}
videoLength: 视频时长(秒)
"""
resp = self.session.post(
REVIEW_PLAY_INFO_URL,
data={
"courseId": course_id,
"periodId": period_id,
"shareUuid": "",
"clientType": 1,
},
)
resp.raise_for_status()
data = resp.json()
if data.get("success") != 1000:
raise Exception(f"获取播放信息失败: {data.get('msg')}")
return data.get("data", {})
# ─────────────────────────────────────────────────
# 测试入口
# ─────────────────────────────────────────────────
if __name__ == "__main__":
import json
from config import COOKIE_FILE
if not os.path.exists(COOKIE_FILE):
print("❌ 未找到 cookies.json,请先运行 login.py 登录")
exit(1)
with open(COOKIE_FILE, "r", encoding="utf-8") as f:
cookies = json.load(f)
api = OrangeAPI(cookies)
# 测试课程列表
print("=== 课程列表 ===")
courses = api.get_course_list()
for c in courses:
print(f" [{c['guid']}] {c['courseName']} "
f"({c.get('learnedCount', 0)}/{c.get('totalPeriodCount', 0)} 课时)")
if courses:
cid = courses[0]["guid"]
print(f"\n=== 课时列表 (课程 {cid}) ===")
periods = api.get_period_list(cid)
for p in periods[:10]: # 只显示前10个
print(f" [{p['guid']}] {p['coursePeriodTitle']} "
f"- {p.get('teacherName', '')} ({p.get('periodTypeStr', '')})")
if len(periods) > 10:
print(f" ... 共 {len(periods)} 个课时")