@mo7yw4ng/openape 1.0.3 → 1.0.5
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 +30 -5
- package/esm/deno.js +1 -1
- package/esm/src/commands/announcements.d.ts.map +1 -1
- package/esm/src/commands/announcements.js +16 -17
- package/esm/src/commands/assignments.d.ts +3 -0
- package/esm/src/commands/assignments.d.ts.map +1 -0
- package/esm/src/commands/assignments.js +230 -0
- package/esm/src/commands/auth.d.ts.map +1 -1
- package/esm/src/commands/auth.js +45 -15
- package/esm/src/commands/calendar.d.ts.map +1 -1
- package/esm/src/commands/calendar.js +20 -21
- package/esm/src/commands/courses.js +6 -6
- package/esm/src/commands/forums.d.ts.map +1 -1
- package/esm/src/commands/forums.js +128 -36
- package/esm/src/commands/grades.js +3 -3
- package/esm/src/commands/materials.d.ts.map +1 -1
- package/esm/src/commands/materials.js +115 -224
- package/esm/src/commands/quizzes.d.ts.map +1 -1
- package/esm/src/commands/quizzes.js +179 -68
- package/esm/src/commands/skills.d.ts.map +1 -1
- package/esm/src/commands/skills.js +4 -8
- package/esm/src/commands/upload.d.ts +3 -0
- package/esm/src/commands/upload.d.ts.map +1 -0
- package/esm/src/commands/upload.js +58 -0
- package/esm/src/commands/videos.d.ts.map +1 -1
- package/esm/src/commands/videos.js +10 -9
- package/esm/src/index.d.ts.map +1 -1
- package/esm/src/index.js +12 -1
- package/esm/src/lib/auth.d.ts +23 -1
- package/esm/src/lib/auth.d.ts.map +1 -1
- package/esm/src/lib/auth.js +36 -3
- package/esm/src/lib/logger.d.ts +1 -1
- package/esm/src/lib/logger.d.ts.map +1 -1
- package/esm/src/lib/logger.js +7 -4
- package/esm/src/lib/moodle.d.ts +183 -1
- package/esm/src/lib/moodle.d.ts.map +1 -1
- package/esm/src/lib/moodle.js +498 -13
- package/esm/src/lib/types.d.ts +81 -164
- package/esm/src/lib/types.d.ts.map +1 -1
- package/esm/src/lib/types.js +1 -0
- package/esm/src/lib/utils.d.ts +20 -0
- package/esm/src/lib/utils.d.ts.map +1 -1
- package/esm/src/lib/utils.js +48 -1
- package/package.json +1 -1
- package/script/deno.js +1 -1
- package/script/src/commands/announcements.d.ts.map +1 -1
- package/script/src/commands/announcements.js +15 -16
- package/script/src/commands/assignments.d.ts +3 -0
- package/script/src/commands/assignments.d.ts.map +1 -0
- package/script/src/commands/assignments.js +269 -0
- package/script/src/commands/auth.d.ts.map +1 -1
- package/script/src/commands/auth.js +44 -14
- package/script/src/commands/calendar.d.ts.map +1 -1
- package/script/src/commands/calendar.js +19 -20
- package/script/src/commands/courses.js +5 -5
- package/script/src/commands/forums.d.ts.map +1 -1
- package/script/src/commands/forums.js +128 -36
- package/script/src/commands/grades.js +3 -3
- package/script/src/commands/materials.d.ts.map +1 -1
- package/script/src/commands/materials.js +115 -224
- package/script/src/commands/quizzes.d.ts.map +1 -1
- package/script/src/commands/quizzes.js +177 -66
- package/script/src/commands/skills.d.ts.map +1 -1
- package/script/src/commands/skills.js +4 -8
- package/script/src/commands/upload.d.ts +3 -0
- package/script/src/commands/upload.d.ts.map +1 -0
- package/script/src/commands/upload.js +64 -0
- package/script/src/commands/videos.d.ts.map +1 -1
- package/script/src/commands/videos.js +10 -9
- package/script/src/index.d.ts.map +1 -1
- package/script/src/index.js +12 -1
- package/script/src/lib/auth.d.ts +23 -1
- package/script/src/lib/auth.d.ts.map +1 -1
- package/script/src/lib/auth.js +70 -3
- package/script/src/lib/logger.d.ts +1 -1
- package/script/src/lib/logger.d.ts.map +1 -1
- package/script/src/lib/logger.js +7 -4
- package/script/src/lib/moodle.d.ts +183 -1
- package/script/src/lib/moodle.d.ts.map +1 -1
- package/script/src/lib/moodle.js +511 -13
- package/script/src/lib/types.d.ts +81 -164
- package/script/src/lib/types.d.ts.map +1 -1
- package/script/src/lib/types.js +1 -0
- package/script/src/lib/utils.d.ts +20 -0
- package/script/src/lib/utils.d.ts.map +1 -1
- package/script/src/lib/utils.js +52 -0
- package/skills/openape/SKILL.md +74 -270
|
@@ -6,108 +6,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.registerMaterialsCommand = registerMaterialsCommand;
|
|
7
7
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
8
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const logger_js_1 = require("../lib/logger.js");
|
|
10
9
|
const auth_js_1 = require("../lib/auth.js");
|
|
11
|
-
const session_js_1 = require("../lib/session.js");
|
|
12
|
-
const auth_js_2 = require("../lib/auth.js");
|
|
13
10
|
const index_js_1 = require("../index.js");
|
|
14
|
-
const token_js_1 = require("../lib/token.js");
|
|
15
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
16
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
13
|
function registerMaterialsCommand(program) {
|
|
18
14
|
const materialsCmd = program.command("materials");
|
|
19
15
|
materialsCmd.description("Material/resource operations");
|
|
20
|
-
//
|
|
21
|
-
async function
|
|
22
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
23
|
-
const outputFormat = (0, utils_js_1.getOutputFormat)(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
24
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
25
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
26
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
27
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
28
|
-
// Check if session exists
|
|
29
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
30
|
-
log.error("未找到登入 session。請先執行 'openape auth login' 進行登入。");
|
|
31
|
-
log.info(`Session 預期位置: ${sessionPath}`);
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
// Try to load WS token
|
|
35
|
-
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
36
|
-
if (!wsToken) {
|
|
37
|
-
log.error("未找到 WS token。請先執行 'openape auth login' 進行登入。");
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
log,
|
|
42
|
-
session: {
|
|
43
|
-
wsToken,
|
|
44
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
// Helper function to create session context (for download commands)
|
|
49
|
-
async function createSessionContext(options, command) {
|
|
50
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
51
|
-
const outputFormat = (0, utils_js_1.getOutputFormat)(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
52
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
53
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
54
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
55
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
56
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
57
|
-
log.error("未找到登入 session。請先執行 'openape auth login' 進行登入。");
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const config = {
|
|
61
|
-
username: "",
|
|
62
|
-
password: "",
|
|
63
|
-
courseUrl: "",
|
|
64
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
65
|
-
headless: !options.headed,
|
|
66
|
-
slowMo: 0,
|
|
67
|
-
authStatePath: sessionPath,
|
|
68
|
-
ollamaBaseUrl: "",
|
|
69
|
-
};
|
|
70
|
-
log.info("啟動瀏覽器...");
|
|
71
|
-
const { browser, context, page } = await (0, auth_js_1.launchAuthenticated)(config, log);
|
|
72
|
-
try {
|
|
73
|
-
const session = await (0, session_js_1.extractSessionInfo)(page, config, log);
|
|
74
|
-
return { log, page, session, browser, context };
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
await context.close();
|
|
78
|
-
await browser.close();
|
|
79
|
-
throw err;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// Helper to download a single resource
|
|
83
|
-
async function downloadResource(page, resource, outputDir, log) {
|
|
16
|
+
// Helper to download a single resource via HTTP (no browser needed)
|
|
17
|
+
async function downloadResourceHttp(resource, outputDir, log, token) {
|
|
84
18
|
try {
|
|
85
19
|
// Only download resource type (skip url)
|
|
86
20
|
if (resource.modType !== "resource") {
|
|
87
21
|
log.debug(` Skipping ${resource.modType}: ${resource.name}`);
|
|
88
22
|
return null;
|
|
89
23
|
}
|
|
90
|
-
// Create course directory
|
|
91
24
|
const courseDir = node_path_1.default.join(outputDir, (0, utils_js_1.sanitizeFilename)(resource.course_name));
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Navigate to resource page
|
|
96
|
-
log.debug(` Downloading: ${resource.name}`);
|
|
97
|
-
await page.goto(resource.url, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
98
|
-
// Try to find download link on the page
|
|
99
|
-
const downloadLinks = await page.$$eval('a[href*="forcedownload=1"]', (links) => links.map((a) => a.href));
|
|
100
|
-
if (downloadLinks.length === 0) {
|
|
101
|
-
log.warn(` No download link found for: ${resource.name}`);
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
// Download the first available file
|
|
105
|
-
const downloadUrl = downloadLinks[0];
|
|
106
|
-
// Extract filename from URL or use resource name
|
|
107
|
-
const urlObj = new URL(downloadUrl);
|
|
108
|
-
const filenameParam = urlObj.searchParams.get("filename");
|
|
109
|
-
let filename = filenameParam || (0, utils_js_1.sanitizeFilename)(resource.name);
|
|
110
|
-
// Add extension if missing
|
|
25
|
+
await node_fs_1.default.promises.mkdir(courseDir, { recursive: true });
|
|
26
|
+
// Build filename
|
|
27
|
+
let filename = (0, utils_js_1.sanitizeFilename)(resource.name);
|
|
111
28
|
if (resource.mimetype && !node_path_1.default.extname(filename)) {
|
|
112
29
|
const extMap = {
|
|
113
30
|
"application/pdf": ".pdf",
|
|
@@ -126,34 +43,61 @@ function registerMaterialsCommand(program) {
|
|
|
126
43
|
}
|
|
127
44
|
}
|
|
128
45
|
const outputPath = node_path_1.default.join(courseDir, filename);
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
46
|
+
// Skip if already exists
|
|
47
|
+
if (node_fs_1.default.existsSync(outputPath)) {
|
|
48
|
+
log.debug(` Skipping (exists): ${filename}`);
|
|
49
|
+
const stats = await node_fs_1.default.promises.stat(outputPath);
|
|
50
|
+
return { filename, path: outputPath, size: stats.size, course_id: resource.course_id, course_name: resource.course_name };
|
|
51
|
+
}
|
|
52
|
+
// Download via HTTP with WS token
|
|
53
|
+
const separator = resource.url.includes("?") ? "&" : "?";
|
|
54
|
+
const downloadUrl = `${resource.url}${separator}token=${token}`;
|
|
55
|
+
log.debug(` Downloading: ${resource.name}`);
|
|
56
|
+
const response = await fetch(downloadUrl);
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
log.warn(` Failed to download ${resource.name}: HTTP ${response.status}`);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
62
|
+
const data = new Uint8Array(arrayBuffer);
|
|
63
|
+
await node_fs_1.default.promises.writeFile(outputPath, data);
|
|
64
|
+
log.success(` Downloaded: ${filename} (${(0, utils_js_1.formatFileSize)(data.byteLength, 1)} KB)`);
|
|
65
|
+
return { filename, path: outputPath, size: data.byteLength, course_id: resource.course_id, course_name: resource.course_name };
|
|
144
66
|
}
|
|
145
67
|
catch (err) {
|
|
146
68
|
log.warn(` Failed to download ${resource.name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
147
69
|
return null;
|
|
148
70
|
}
|
|
149
71
|
}
|
|
72
|
+
// Helper to build material list from API resources
|
|
73
|
+
function buildMaterialsList(courses, apiResources) {
|
|
74
|
+
const courseMap = new Map(courses.map(c => [c.id, c]));
|
|
75
|
+
const materials = [];
|
|
76
|
+
for (const resource of apiResources) {
|
|
77
|
+
const course = courseMap.get(resource.courseId);
|
|
78
|
+
if (course) {
|
|
79
|
+
materials.push({
|
|
80
|
+
course_id: resource.courseId,
|
|
81
|
+
course_name: course.fullname,
|
|
82
|
+
cmid: resource.cmid,
|
|
83
|
+
name: resource.name,
|
|
84
|
+
url: resource.url,
|
|
85
|
+
modType: resource.modType,
|
|
86
|
+
mimetype: resource.mimetype,
|
|
87
|
+
filesize: resource.filesize,
|
|
88
|
+
modified: resource.modified,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return materials;
|
|
93
|
+
}
|
|
150
94
|
materialsCmd
|
|
151
95
|
.command("list-all")
|
|
152
96
|
.description("List all materials/resources across all courses")
|
|
153
97
|
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
154
98
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
155
99
|
.action(async (options, command) => {
|
|
156
|
-
const apiContext = await createApiContext(options, command);
|
|
100
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
157
101
|
if (!apiContext) {
|
|
158
102
|
process.exitCode = 1;
|
|
159
103
|
return;
|
|
@@ -212,150 +156,97 @@ function registerMaterialsCommand(program) {
|
|
|
212
156
|
});
|
|
213
157
|
materialsCmd
|
|
214
158
|
.command("download")
|
|
215
|
-
.description("Download all materials from a specific course
|
|
159
|
+
.description("Download all materials from a specific course")
|
|
216
160
|
.argument("<course-id>", "Course ID")
|
|
217
161
|
.option("--output-dir <path>", "Output directory", "./downloads")
|
|
218
162
|
.action(async (courseId, options, command) => {
|
|
219
|
-
const
|
|
220
|
-
if (!
|
|
163
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
164
|
+
if (!apiContext) {
|
|
221
165
|
process.exitCode = 1;
|
|
222
166
|
return;
|
|
223
167
|
}
|
|
224
|
-
const { log,
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
name: a.textContent?.trim() || "",
|
|
241
|
-
}));
|
|
242
|
-
});
|
|
243
|
-
for (const link of resourceLinks) {
|
|
244
|
-
const cmidMatch = link.url.match(/id=(\d+)/);
|
|
245
|
-
if (cmidMatch) {
|
|
246
|
-
materials.push({
|
|
247
|
-
course_id: course.id,
|
|
248
|
-
course_name: course.fullname,
|
|
249
|
-
cmid: cmidMatch[1],
|
|
250
|
-
name: link.name,
|
|
251
|
-
url: link.url,
|
|
252
|
-
modType: "resource",
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
log.info(`Found ${materials.length} materials in course: ${course.fullname}`);
|
|
257
|
-
const downloadedFiles = [];
|
|
258
|
-
for (const material of materials) {
|
|
259
|
-
const result = await downloadResource(page, material, options.outputDir, log);
|
|
260
|
-
if (result) {
|
|
261
|
-
downloadedFiles.push(result);
|
|
262
|
-
}
|
|
168
|
+
const { log, session } = apiContext;
|
|
169
|
+
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session);
|
|
170
|
+
const course = courses.find((c) => c.id === parseInt(courseId, 10));
|
|
171
|
+
if (!course) {
|
|
172
|
+
log.error(`Course not found: ${courseId}`);
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const apiResources = await (0, moodle_js_1.getResourcesByCoursesApi)(session, [course.id]);
|
|
177
|
+
const materials = buildMaterialsList(courses, apiResources);
|
|
178
|
+
log.info(`Found ${materials.length} materials in course: ${course.fullname}`);
|
|
179
|
+
const downloadedFiles = [];
|
|
180
|
+
for (const material of materials) {
|
|
181
|
+
const result = await downloadResourceHttp(material, options.outputDir, log, session.wsToken);
|
|
182
|
+
if (result) {
|
|
183
|
+
downloadedFiles.push(result);
|
|
263
184
|
}
|
|
264
|
-
|
|
185
|
+
}
|
|
186
|
+
const output = {
|
|
187
|
+
status: "success",
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
downloaded_files: downloadedFiles.map(f => ({
|
|
190
|
+
filename: f.filename,
|
|
191
|
+
path: f.path,
|
|
192
|
+
size: f.size,
|
|
193
|
+
course_id: f.course_id,
|
|
194
|
+
course_name: f.course_name,
|
|
195
|
+
})),
|
|
196
|
+
summary: {
|
|
265
197
|
total_materials: materials.length,
|
|
266
198
|
downloaded: downloadedFiles.length,
|
|
267
199
|
skipped: materials.length - downloadedFiles.length,
|
|
268
200
|
total_size: downloadedFiles.reduce((sum, f) => sum + f.size, 0),
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
timestamp: new Date().toISOString(),
|
|
273
|
-
downloaded_files: downloadedFiles.map(f => ({
|
|
274
|
-
filename: f.filename,
|
|
275
|
-
path: f.path,
|
|
276
|
-
size: f.size,
|
|
277
|
-
course_id: f.course_id,
|
|
278
|
-
course_name: f.course_name,
|
|
279
|
-
})),
|
|
280
|
-
summary,
|
|
281
|
-
};
|
|
282
|
-
console.log(JSON.stringify(output));
|
|
283
|
-
}
|
|
284
|
-
finally {
|
|
285
|
-
await (0, auth_js_2.closeBrowserSafely)(browser, browserContext);
|
|
286
|
-
}
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
console.log(JSON.stringify(output));
|
|
287
204
|
});
|
|
288
205
|
materialsCmd
|
|
289
206
|
.command("download-all")
|
|
290
|
-
.description("Download all materials from all courses
|
|
207
|
+
.description("Download all materials from all courses")
|
|
291
208
|
.option("--output-dir <path>", "Output directory", "./downloads")
|
|
292
209
|
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
293
210
|
.action(async (options, command) => {
|
|
294
|
-
const
|
|
295
|
-
if (!
|
|
211
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
212
|
+
if (!apiContext) {
|
|
296
213
|
process.exitCode = 1;
|
|
297
214
|
return;
|
|
298
215
|
}
|
|
299
|
-
const { log,
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
});
|
|
313
|
-
for (const link of resourceLinks) {
|
|
314
|
-
const cmidMatch = link.url.match(/id=(\d+)/);
|
|
315
|
-
if (cmidMatch) {
|
|
316
|
-
allMaterials.push({
|
|
317
|
-
course_id: course.id,
|
|
318
|
-
course_name: course.fullname,
|
|
319
|
-
cmid: cmidMatch[1],
|
|
320
|
-
name: link.name,
|
|
321
|
-
url: link.url,
|
|
322
|
-
modType: "resource",
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
log.info(`Found ${allMaterials.length} materials across ${courses.length} courses`);
|
|
328
|
-
const downloadedFiles = [];
|
|
329
|
-
for (const material of allMaterials) {
|
|
330
|
-
const result = await downloadResource(page, material, options.outputDir, log);
|
|
331
|
-
if (result) {
|
|
332
|
-
downloadedFiles.push(result);
|
|
333
|
-
}
|
|
216
|
+
const { log, session } = apiContext;
|
|
217
|
+
const classification = options.level === "all" ? undefined : "inprogress";
|
|
218
|
+
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session, { classification });
|
|
219
|
+
log.info(`Scanning ${courses.length} courses for materials...`);
|
|
220
|
+
const courseIds = courses.map((c) => c.id);
|
|
221
|
+
const apiResources = await (0, moodle_js_1.getResourcesByCoursesApi)(session, courseIds);
|
|
222
|
+
const allMaterials = buildMaterialsList(courses, apiResources);
|
|
223
|
+
log.info(`Found ${allMaterials.length} materials across ${courses.length} courses`);
|
|
224
|
+
const downloadedFiles = [];
|
|
225
|
+
for (const material of allMaterials) {
|
|
226
|
+
const result = await downloadResourceHttp(material, options.outputDir, log, session.wsToken);
|
|
227
|
+
if (result) {
|
|
228
|
+
downloadedFiles.push(result);
|
|
334
229
|
}
|
|
335
|
-
|
|
230
|
+
}
|
|
231
|
+
const output = {
|
|
232
|
+
status: "success",
|
|
233
|
+
timestamp: new Date().toISOString(),
|
|
234
|
+
downloaded_files: downloadedFiles.map(f => ({
|
|
235
|
+
filename: f.filename,
|
|
236
|
+
path: f.path,
|
|
237
|
+
size: f.size,
|
|
238
|
+
course_id: f.course_id,
|
|
239
|
+
course_name: f.course_name,
|
|
240
|
+
})),
|
|
241
|
+
summary: {
|
|
336
242
|
total_courses: courses.length,
|
|
337
243
|
total_materials: allMaterials.length,
|
|
338
244
|
downloaded: downloadedFiles.length,
|
|
339
245
|
skipped: allMaterials.length - downloadedFiles.length,
|
|
340
246
|
total_size: downloadedFiles.reduce((sum, f) => sum + f.size, 0),
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
timestamp: new Date().toISOString(),
|
|
345
|
-
downloaded_files: downloadedFiles.map(f => ({
|
|
346
|
-
filename: f.filename,
|
|
347
|
-
path: f.path,
|
|
348
|
-
size: f.size,
|
|
349
|
-
course_id: f.course_id,
|
|
350
|
-
course_name: f.course_name,
|
|
351
|
-
})),
|
|
352
|
-
summary,
|
|
353
|
-
};
|
|
354
|
-
console.log(JSON.stringify(output));
|
|
355
|
-
}
|
|
356
|
-
finally {
|
|
357
|
-
await (0, auth_js_2.closeBrowserSafely)(browser, browserContext);
|
|
358
|
-
}
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
console.log(JSON.stringify(output));
|
|
359
250
|
});
|
|
360
251
|
materialsCmd
|
|
361
252
|
.command("complete")
|
|
@@ -365,7 +256,7 @@ function registerMaterialsCommand(program) {
|
|
|
365
256
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
366
257
|
.action(async (courseId, options, command) => {
|
|
367
258
|
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
368
|
-
const apiContext = await createApiContext(options, command);
|
|
259
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
369
260
|
if (!apiContext) {
|
|
370
261
|
process.exitCode = 1;
|
|
371
262
|
return;
|
|
@@ -441,7 +332,7 @@ function registerMaterialsCommand(program) {
|
|
|
441
332
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
442
333
|
.action(async (options, command) => {
|
|
443
334
|
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
444
|
-
const apiContext = await createApiContext(options, command);
|
|
335
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
445
336
|
if (!apiContext) {
|
|
446
337
|
process.exitCode = 1;
|
|
447
338
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quizzes.d.ts","sourceRoot":"","sources":["../../../src/src/commands/quizzes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"quizzes.d.ts","sourceRoot":"","sources":["../../../src/src/commands/quizzes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyEpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoQ7D"}
|