@robertying/thu-learn-lib 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -227
- package/lib/auth.d.ts +15 -4
- package/lib/auth.d.ts.map +1 -1
- package/lib/auth.js +65 -55
- package/lib/auth.js.map +1 -1
- package/lib/calendar.d.ts +12 -5
- package/lib/calendar.d.ts.map +1 -1
- package/lib/calendar.js +118 -20
- package/lib/calendar.js.map +1 -1
- package/lib/contents.d.ts.map +1 -1
- package/lib/contents.js +10 -6
- package/lib/contents.js.map +1 -1
- package/lib/courses.d.ts.map +1 -1
- package/lib/courses.js +15 -29
- package/lib/courses.js.map +1 -1
- package/lib/discussions.d.ts.map +1 -1
- package/lib/discussions.js +8 -19
- package/lib/discussions.js.map +1 -1
- package/lib/favorites.d.ts +1 -0
- package/lib/favorites.d.ts.map +1 -1
- package/lib/favorites.js +25 -60
- package/lib/favorites.js.map +1 -1
- package/lib/files.d.ts +4 -4
- package/lib/files.d.ts.map +1 -1
- package/lib/files.js +112 -134
- package/lib/files.js.map +1 -1
- package/lib/homework.d.ts +35 -5
- package/lib/homework.d.ts.map +1 -1
- package/lib/homework.js +150 -92
- package/lib/homework.js.map +1 -1
- package/lib/index.d.ts +45 -20
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +52 -17
- package/lib/index.js.map +1 -1
- package/lib/notices.d.ts +4 -4
- package/lib/notices.d.ts.map +1 -1
- package/lib/notices.js +13 -33
- package/lib/notices.js.map +1 -1
- package/lib/questionnaires.d.ts +6 -3
- package/lib/questionnaires.d.ts.map +1 -1
- package/lib/questionnaires.js +26 -24
- package/lib/questionnaires.js.map +1 -1
- package/lib/request.d.ts +64 -20
- package/lib/request.d.ts.map +1 -1
- package/lib/request.js +174 -64
- package/lib/request.js.map +1 -1
- package/lib/timetable.d.ts +26 -0
- package/lib/timetable.d.ts.map +1 -0
- package/lib/timetable.js +110 -0
- package/lib/timetable.js.map +1 -0
- package/lib/types.d.ts +117 -76
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -1
- package/lib/urls.d.ts +10 -20
- package/lib/urls.d.ts.map +1 -1
- package/lib/urls.js +11 -56
- package/lib/urls.js.map +1 -1
- package/lib/user.d.ts +7 -2
- package/lib/user.d.ts.map +1 -1
- package/lib/user.js +20 -6
- package/lib/user.js.map +1 -1
- package/lib/utils.d.ts +42 -4
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +80 -29
- package/lib/utils.js.map +1 -1
- package/package.json +9 -8
- package/src/auth.ts +72 -70
- package/src/calendar.ts +136 -24
- package/src/contents.ts +13 -6
- package/src/courses.ts +21 -37
- package/src/discussions.ts +10 -21
- package/src/favorites.ts +44 -80
- package/src/files.ts +124 -142
- package/src/homework.ts +207 -135
- package/src/index.ts +79 -48
- package/src/notices.ts +29 -42
- package/src/questionnaires.ts +62 -45
- package/src/request.ts +192 -69
- package/src/timetable.ts +121 -0
- package/src/types.ts +123 -87
- package/src/urls.ts +13 -64
- package/src/user.ts +22 -7
- package/src/utils.ts +97 -29
package/src/favorites.ts
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
// Favorites (收藏) and comments (备注).
|
|
2
2
|
|
|
3
3
|
import type { Session } from './request.ts';
|
|
4
|
-
import { YES } from './request.ts';
|
|
5
4
|
import type { CommentItem, FavoriteItem } from './types.ts';
|
|
6
5
|
import { ContentType, FailReason } from './types.ts';
|
|
7
6
|
import * as URLS from './urls.ts';
|
|
8
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
CONTENT_TYPE_MAP_REVERSE,
|
|
9
|
+
YES,
|
|
10
|
+
decodeHTML,
|
|
11
|
+
expectSuccess,
|
|
12
|
+
toRequiredServerTime,
|
|
13
|
+
toServerTime,
|
|
14
|
+
} from './utils.ts';
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* Add an item to favorites. (收藏)
|
|
18
|
+
* Success is `result: 'success'`; the message is in the website's language ("收藏成功", "Added successfully.").
|
|
12
19
|
*/
|
|
13
20
|
export async function addToFavorites(s: Session, type: ContentType, id: string): Promise<void> {
|
|
14
|
-
const json = await
|
|
15
|
-
|
|
16
|
-
return Promise.reject({
|
|
17
|
-
reason: FailReason.OPERATION_FAILED,
|
|
18
|
-
extra: json,
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FAVORITE_ADD(type, id));
|
|
22
|
+
expectSuccess(json, FailReason.OPERATION_FAILED);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Remove an item from favorites. (取消收藏)
|
|
25
27
|
*/
|
|
26
28
|
export async function removeFromFavorites(s: Session, id: string): Promise<void> {
|
|
27
|
-
const json = await
|
|
28
|
-
|
|
29
|
-
return Promise.reject({
|
|
30
|
-
reason: FailReason.OPERATION_FAILED,
|
|
31
|
-
extra: json,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
29
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FAVORITE_REMOVE(id));
|
|
30
|
+
expectSuccess(json, FailReason.OPERATION_FAILED);
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
/**
|
|
@@ -38,18 +35,11 @@ export async function removeFromFavorites(s: Session, id: string): Promise<void>
|
|
|
38
35
|
* If `courseID` or `type` is specified, only return favorites of that course or type.
|
|
39
36
|
*/
|
|
40
37
|
export async function getFavorites(s: Session, courseID?: string, type?: ContentType): Promise<FavoriteItem[]> {
|
|
41
|
-
const json = await (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
).json();
|
|
47
|
-
if (json.result !== 'success') {
|
|
48
|
-
return Promise.reject({
|
|
49
|
-
reason: FailReason.INVALID_RESPONSE,
|
|
50
|
-
extra: json,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
38
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FAVORITE_LIST(type), {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: URLS.LEARN_PAGE_LIST_FORM_DATA(courseID),
|
|
41
|
+
});
|
|
42
|
+
expectSuccess(json, FailReason.INVALID_RESPONSE);
|
|
53
43
|
const result = (json.object?.aaData ?? []) as any[];
|
|
54
44
|
return result
|
|
55
45
|
.map((e): FavoriteItem | undefined => {
|
|
@@ -59,15 +49,17 @@ export async function getFavorites(s: Session, courseID?: string, type?: Content
|
|
|
59
49
|
id: e.ywid,
|
|
60
50
|
type: itemType,
|
|
61
51
|
title: decodeHTML(e.ywbt),
|
|
62
|
-
time:
|
|
52
|
+
time: toRequiredServerTime(
|
|
53
|
+
itemType === ContentType.DISCUSSION || itemType === ContentType.QUESTION ? e.tlsj : e.ywsj,
|
|
54
|
+
),
|
|
63
55
|
state: e.ywzt,
|
|
64
56
|
extra: e.ywbz ?? undefined,
|
|
65
57
|
semesterId: e.xnxq,
|
|
66
58
|
courseId: e.wlkcid,
|
|
67
59
|
pinned: e.sfzd === YES,
|
|
68
|
-
pinnedTime: e.zdsj
|
|
60
|
+
pinnedTime: toServerTime(e.zdsj),
|
|
69
61
|
comment: e.bznr ?? undefined,
|
|
70
|
-
addedTime: e.scsj,
|
|
62
|
+
addedTime: toRequiredServerTime(e.scsj),
|
|
71
63
|
itemId: e.id,
|
|
72
64
|
} satisfies FavoriteItem;
|
|
73
65
|
})
|
|
@@ -78,36 +70,22 @@ export async function getFavorites(s: Session, courseID?: string, type?: Content
|
|
|
78
70
|
* Pin a favorite item. (置顶)
|
|
79
71
|
*/
|
|
80
72
|
export async function pinFavoriteItem(s: Session, id: string): Promise<void> {
|
|
81
|
-
const json = await (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
).json();
|
|
87
|
-
if (json.result !== 'success') {
|
|
88
|
-
return Promise.reject({
|
|
89
|
-
reason: FailReason.OPERATION_FAILED,
|
|
90
|
-
extra: json,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
73
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FAVORITE_PIN, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
body: URLS.LEARN_FAVORITE_PIN_UNPIN_FORM_DATA(id),
|
|
76
|
+
});
|
|
77
|
+
expectSuccess(json, FailReason.OPERATION_FAILED);
|
|
93
78
|
}
|
|
94
79
|
|
|
95
80
|
/**
|
|
96
81
|
* Unpin a favorite item. (取消置顶)
|
|
97
82
|
*/
|
|
98
83
|
export async function unpinFavoriteItem(s: Session, id: string): Promise<void> {
|
|
99
|
-
const json = await (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
).json();
|
|
105
|
-
if (json.result !== 'success') {
|
|
106
|
-
return Promise.reject({
|
|
107
|
-
reason: FailReason.OPERATION_FAILED,
|
|
108
|
-
extra: json,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
84
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FAVORITE_UNPIN, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
body: URLS.LEARN_FAVORITE_PIN_UNPIN_FORM_DATA(id),
|
|
87
|
+
});
|
|
88
|
+
expectSuccess(json, FailReason.OPERATION_FAILED);
|
|
111
89
|
}
|
|
112
90
|
|
|
113
91
|
/**
|
|
@@ -115,18 +93,11 @@ export async function unpinFavoriteItem(s: Session, id: string): Promise<void> {
|
|
|
115
93
|
* Set an empty string to remove the comment.
|
|
116
94
|
*/
|
|
117
95
|
export async function setComment(s: Session, type: ContentType, id: string, content: string): Promise<void> {
|
|
118
|
-
const json = await (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
).json();
|
|
124
|
-
if (json.result !== 'success' || !json.msg?.endsWith?.('成功')) {
|
|
125
|
-
return Promise.reject({
|
|
126
|
-
reason: FailReason.OPERATION_FAILED,
|
|
127
|
-
extra: json,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
96
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_COMMENT_SET, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
body: URLS.LEARN_COMMENT_SET_FORM_DATA(type, id, content),
|
|
99
|
+
});
|
|
100
|
+
expectSuccess(json, FailReason.OPERATION_FAILED);
|
|
130
101
|
}
|
|
131
102
|
|
|
132
103
|
/**
|
|
@@ -134,18 +105,11 @@ export async function setComment(s: Session, type: ContentType, id: string, cont
|
|
|
134
105
|
* If `courseID` or `type` is specified, only return favorites of that course or type.
|
|
135
106
|
*/
|
|
136
107
|
export async function getComments(s: Session, courseID?: string, type?: ContentType): Promise<CommentItem[]> {
|
|
137
|
-
const json = await (
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
).json();
|
|
143
|
-
if (json.result !== 'success') {
|
|
144
|
-
return Promise.reject({
|
|
145
|
-
reason: FailReason.INVALID_RESPONSE,
|
|
146
|
-
extra: json,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
108
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_COMMENT_LIST(type), {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
body: URLS.LEARN_PAGE_LIST_FORM_DATA(courseID),
|
|
111
|
+
});
|
|
112
|
+
expectSuccess(json, FailReason.INVALID_RESPONSE);
|
|
149
113
|
const result = (json.object?.aaData ?? []) as any[];
|
|
150
114
|
return result
|
|
151
115
|
.map((e): CommentItem | undefined => {
|
|
@@ -159,7 +123,7 @@ export async function getComments(s: Session, courseID?: string, type?: ContentT
|
|
|
159
123
|
title: decodeHTML(e.ywbt),
|
|
160
124
|
semesterId: e.xnxq,
|
|
161
125
|
courseId: e.wlkcid,
|
|
162
|
-
commentTime: e.cjsj,
|
|
126
|
+
commentTime: toRequiredServerTime(e.cjsj),
|
|
163
127
|
itemId: e.id,
|
|
164
128
|
} satisfies CommentItem;
|
|
165
129
|
})
|
package/src/files.ts
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
// Course files (课程文件) and file categories.
|
|
2
2
|
|
|
3
3
|
import type { Session } from './request.ts';
|
|
4
|
-
import type {
|
|
4
|
+
import type { CourseFile, FileCategory } from './types.ts';
|
|
5
5
|
import { ContentType, CourseType, FailReason } from './types.ts';
|
|
6
6
|
import * as URLS from './urls.ts';
|
|
7
|
-
import { decodeHTML, formatFileSize } from './utils.ts';
|
|
7
|
+
import { YES, decodeHTML, expectSuccess, formatFileSize, toRequiredServerTime } from './utils.ts';
|
|
8
8
|
|
|
9
|
-
/** Get all files (课程文件) of the specified course. */
|
|
9
|
+
/** Get all files (课程文件) of the specified course; `category` is left out if the categories can't be fetched. */
|
|
10
10
|
export async function getFileList(
|
|
11
11
|
s: Session,
|
|
12
12
|
courseID: string,
|
|
13
13
|
courseType: CourseType = CourseType.STUDENT,
|
|
14
|
-
): Promise<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
14
|
+
): Promise<CourseFile[]> {
|
|
15
|
+
// categories only fill `category`; don't fail the whole list when they can't be fetched
|
|
16
|
+
const [json, categoryList] = await Promise.all([
|
|
17
|
+
s.fetchJsonWithToken(URLS.LEARN_FILE_LIST(courseID, courseType)),
|
|
18
|
+
getFileCategoryList(s, courseID, courseType).catch((): FileCategory[] => []),
|
|
19
|
+
]);
|
|
20
|
+
expectSuccess(json, FailReason.INVALID_RESPONSE);
|
|
22
21
|
|
|
23
22
|
let result: any[] = [];
|
|
24
23
|
if (Array.isArray(json.object?.resultsList)) {
|
|
@@ -29,40 +28,47 @@ export async function getFileList(
|
|
|
29
28
|
result = json.object;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
const categories = new Map(
|
|
31
|
+
const categories = new Map(categoryList.map((c) => [c.id, c]));
|
|
32
|
+
return result.map((f) => ({ ...parseFile(s, f, courseType), category: categories.get(f.kjflid) }));
|
|
33
|
+
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
/** a 0 / 1 flag, which the server sends as a number, a string or (in older answers) a boolean */
|
|
36
|
+
const isSet = (flag: unknown): boolean => flag === true || Number(flag) === 1;
|
|
37
|
+
|
|
38
|
+
/** a count or size, which the server sends as a number or a string */
|
|
39
|
+
const toNumber = (value: unknown): number => Number(value ?? 0) || 0;
|
|
40
|
+
|
|
41
|
+
/** a file of the object-shaped lists (`getFileList`, and `getFileListByCategory` for a teacher) */
|
|
42
|
+
function parseFile(s: Session, f: any, courseType: CourseType): CourseFile {
|
|
43
|
+
const title = decodeHTML(f.bt);
|
|
44
|
+
const fileId = f.wjid;
|
|
45
|
+
const uploadTime = toRequiredServerTime(f.scsj);
|
|
46
|
+
const downloadUrl = URLS.LEARN_FILE_DOWNLOAD(fileId, courseType);
|
|
47
|
+
const previewUrl = URLS.LEARN_FILE_PREVIEW(ContentType.FILE, fileId, courseType, s.previewFirstPage);
|
|
48
|
+
return {
|
|
49
|
+
id: f.kjxxid,
|
|
50
|
+
fileId,
|
|
51
|
+
title,
|
|
52
|
+
description: decodeHTML(f.ms),
|
|
53
|
+
rawSize: toNumber(f.wjdx),
|
|
54
|
+
size: f.fileSize,
|
|
55
|
+
uploadTime,
|
|
56
|
+
publishTime: uploadTime,
|
|
57
|
+
downloadUrl,
|
|
58
|
+
previewUrl,
|
|
59
|
+
isNew: isSet(f.isNew), // 1 / 0 for a student
|
|
60
|
+
markedImportant: isSet(f.sfqd), // string '1' for a teacher, number 1 for a student
|
|
61
|
+
visitCount: toNumber(f.xsllcs ?? f.llcs),
|
|
62
|
+
downloadCount: toNumber(f.xzcs),
|
|
63
|
+
fileType: f.wjlx,
|
|
64
|
+
remoteFile: {
|
|
65
|
+
id: fileId,
|
|
66
|
+
name: title,
|
|
50
67
|
downloadUrl,
|
|
51
68
|
previewUrl,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
downloadCount: f.xzcs ?? 0,
|
|
56
|
-
fileType: f.wjlx,
|
|
57
|
-
remoteFile: {
|
|
58
|
-
id: fileId,
|
|
59
|
-
name: title,
|
|
60
|
-
downloadUrl,
|
|
61
|
-
previewUrl,
|
|
62
|
-
size: f.fileSize,
|
|
63
|
-
},
|
|
64
|
-
} satisfies File;
|
|
65
|
-
});
|
|
69
|
+
size: f.fileSize,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
/** Get file categories of the specified course. */
|
|
@@ -71,13 +77,8 @@ export async function getFileCategoryList(
|
|
|
71
77
|
courseID: string,
|
|
72
78
|
courseType: CourseType = CourseType.STUDENT,
|
|
73
79
|
): Promise<FileCategory[]> {
|
|
74
|
-
const json = await
|
|
75
|
-
|
|
76
|
-
return Promise.reject({
|
|
77
|
-
reason: FailReason.INVALID_RESPONSE,
|
|
78
|
-
extra: json,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
80
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FILE_CATEGORY_LIST(courseID, courseType));
|
|
81
|
+
expectSuccess(json, FailReason.INVALID_RESPONSE);
|
|
81
82
|
|
|
82
83
|
const result = (json.object?.rows ?? []) as any[];
|
|
83
84
|
|
|
@@ -86,7 +87,7 @@ export async function getFileCategoryList(
|
|
|
86
87
|
({
|
|
87
88
|
id: c.kjflid,
|
|
88
89
|
title: decodeHTML(c.bt),
|
|
89
|
-
creationTime: c.czsj,
|
|
90
|
+
creationTime: toRequiredServerTime(c.czsj),
|
|
90
91
|
}) satisfies FileCategory,
|
|
91
92
|
);
|
|
92
93
|
}
|
|
@@ -100,98 +101,79 @@ export async function getFileListByCategory(
|
|
|
100
101
|
courseID: string,
|
|
101
102
|
categoryId: string,
|
|
102
103
|
courseType: CourseType = CourseType.STUDENT,
|
|
103
|
-
): Promise<
|
|
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
|
-
reason: FailReason.INVALID_RESPONSE,
|
|
159
|
-
extra: json,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const result = (json.object.aaData ?? []) as any[];
|
|
164
|
-
|
|
165
|
-
return result.map((f) => {
|
|
166
|
-
const title = decodeHTML(f.bt);
|
|
167
|
-
const fileId = f.wjid;
|
|
168
|
-
const uploadTime = f.scsj;
|
|
169
|
-
const downloadUrl = URLS.LEARN_FILE_DOWNLOAD(fileId, courseType);
|
|
170
|
-
const previewUrl = URLS.LEARN_FILE_PREVIEW(ContentType.FILE, fileId, courseType, s.previewFirstPage);
|
|
171
|
-
return {
|
|
172
|
-
id: f.kjxxid,
|
|
173
|
-
fileId,
|
|
174
|
-
title,
|
|
175
|
-
description: decodeHTML(f.ms),
|
|
176
|
-
rawSize: f.wjdx,
|
|
177
|
-
size: f.fileSize,
|
|
178
|
-
uploadTime,
|
|
179
|
-
publishTime: uploadTime,
|
|
104
|
+
): Promise<CourseFile[]> {
|
|
105
|
+
return courseType === CourseType.STUDENT
|
|
106
|
+
? getStudentFileListByCategory(s, courseID, categoryId)
|
|
107
|
+
: getTeacherFileListByCategory(s, courseID, categoryId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** the columns of a row of the student's file list of a category, which is an array instead of an object */
|
|
111
|
+
const COLUMN = {
|
|
112
|
+
ID: 0,
|
|
113
|
+
TITLE: 1,
|
|
114
|
+
MARKED_IMPORTANT: 2,
|
|
115
|
+
DESCRIPTION: 5,
|
|
116
|
+
UPLOAD_TIME: 6,
|
|
117
|
+
FILE_ID: 7,
|
|
118
|
+
IS_NEW: 8,
|
|
119
|
+
RAW_SIZE: 9,
|
|
120
|
+
PUBLISH_TIME: 10,
|
|
121
|
+
IS_FAVORITE: 11,
|
|
122
|
+
FILE_TYPE: 13,
|
|
123
|
+
COMMENT: 14,
|
|
124
|
+
} as const;
|
|
125
|
+
|
|
126
|
+
async function getStudentFileListByCategory(s: Session, courseID: string, categoryId: string): Promise<CourseFile[]> {
|
|
127
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FILE_LIST_BY_CATEGORY_STUDENT(courseID, categoryId));
|
|
128
|
+
expectSuccess(json, FailReason.INVALID_RESPONSE);
|
|
129
|
+
|
|
130
|
+
const result = (json.object ?? []) as any[][];
|
|
131
|
+
|
|
132
|
+
return result.map((f) => {
|
|
133
|
+
const fileId = f[COLUMN.FILE_ID];
|
|
134
|
+
const title = decodeHTML(f[COLUMN.TITLE]);
|
|
135
|
+
const rawSize = toNumber(f[COLUMN.RAW_SIZE]);
|
|
136
|
+
const size = formatFileSize(rawSize);
|
|
137
|
+
const downloadUrl = URLS.LEARN_FILE_DOWNLOAD(fileId, CourseType.STUDENT);
|
|
138
|
+
const previewUrl = URLS.LEARN_FILE_PREVIEW(ContentType.FILE, fileId, CourseType.STUDENT, s.previewFirstPage);
|
|
139
|
+
return {
|
|
140
|
+
id: f[COLUMN.ID],
|
|
141
|
+
fileId,
|
|
142
|
+
title,
|
|
143
|
+
description: decodeHTML(f[COLUMN.DESCRIPTION]),
|
|
144
|
+
rawSize,
|
|
145
|
+
size,
|
|
146
|
+
uploadTime: toRequiredServerTime(f[COLUMN.UPLOAD_TIME]),
|
|
147
|
+
publishTime: toRequiredServerTime(f[COLUMN.PUBLISH_TIME]),
|
|
148
|
+
downloadUrl,
|
|
149
|
+
previewUrl,
|
|
150
|
+
isNew: isSet(f[COLUMN.IS_NEW]),
|
|
151
|
+
markedImportant: isSet(f[COLUMN.MARKED_IMPORTANT]),
|
|
152
|
+
// not in this list
|
|
153
|
+
visitCount: 0,
|
|
154
|
+
downloadCount: 0,
|
|
155
|
+
fileType: f[COLUMN.FILE_TYPE],
|
|
156
|
+
remoteFile: {
|
|
157
|
+
id: fileId,
|
|
158
|
+
name: title,
|
|
180
159
|
downloadUrl,
|
|
181
160
|
previewUrl,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
161
|
+
size,
|
|
162
|
+
},
|
|
163
|
+
isFavorite: f[COLUMN.IS_FAVORITE] === YES, // '是' / '否' like the other lists
|
|
164
|
+
comment: f[COLUMN.COMMENT] ?? undefined,
|
|
165
|
+
} satisfies CourseFile;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function getTeacherFileListByCategory(s: Session, courseID: string, categoryId: string): Promise<CourseFile[]> {
|
|
170
|
+
const json = await s.fetchJsonWithToken(URLS.LEARN_FILE_LIST_BY_CATEGORY_TEACHER, {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
body: URLS.LEARN_FILE_LIST_BY_CATEGORY_TEACHER_FORM_DATA(courseID, categoryId),
|
|
173
|
+
});
|
|
174
|
+
expectSuccess(json, FailReason.INVALID_RESPONSE);
|
|
175
|
+
|
|
176
|
+
const result = (json.object?.aaData ?? []) as any[];
|
|
177
|
+
|
|
178
|
+
return result.map((f) => parseFile(s, f, CourseType.TEACHER));
|
|
197
179
|
}
|