tronclass-auto 1.3.1 → 1.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tronclass-auto",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "自動觀看 eclass/TronClass 影片、填寫表單的 CLI 工具",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -32,14 +32,21 @@ async function getUncompleted(page, courseId) {
32
32
  let isExpired = false;
33
33
  try { isUpcoming = scope.activityUpcoming(a); } catch (e) {}
34
34
  try { isExpired = scope.activityExpired(a); } catch (e) {}
35
- if (isUpcoming) return;
36
35
 
37
36
  let timeRange = '';
37
+ let startTime = '';
38
38
  try {
39
39
  const txt = actEl.textContent || '';
40
40
  const tm = txt.match(/時間:\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})\s*~\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})/);
41
- if (tm) timeRange = tm[0];
41
+ if (tm) {
42
+ timeRange = tm[0];
43
+ startTime = tm[1];
44
+ }
42
45
  } catch (e) {}
46
+
47
+ const now = new Date();
48
+ const startDate = startTime ? new Date(startTime.replace(' ', 'T')) : null;
49
+ if (isUpcoming && startDate && startDate > now) return;
43
50
  let durationSec = 0;
44
51
  try {
45
52
  const durText = actEl.querySelector('.activity-attribute');
@@ -73,25 +80,30 @@ async function processCourse(page, courseId, stats) {
73
80
  let done = 0;
74
81
  let skip = 0;
75
82
  let fail = 0;
83
+ let totalProcessed = 0;
76
84
 
77
- const uncompleted = await getUncompleted(page, courseId);
78
- const videos = uncompleted.filter(u => u.type === 'online_video');
79
- const remaining = videos.filter(v => !isDone(courseId, v.id));
85
+ console.log(chalk.cyan(` [fetching course activities...]`));
80
86
 
81
- console.log(chalk.cyan(` Uncompleted: ${videos.length} videos, remaining: ${remaining.length}`));
87
+ while (true) {
88
+ const uncompleted = await getUncompleted(page, courseId);
89
+ const videos = uncompleted.filter(u => u.type === 'online_video');
90
+ const remaining = videos.filter(v => !isDone(courseId, v.id));
82
91
 
83
- const totalDuration = remaining.reduce((sum, v) => sum + (v.durationSec || 120), 0);
84
- const estWatchTime = Math.ceil(totalDuration / 2) + remaining.length * 15;
85
- console.log(chalk.cyan(` 預估時間: ${formatTime(estWatchTime)}`));
92
+ if (remaining.length === 0) break;
86
93
 
87
- if (stats) {
88
- stats.remainingVideos = remaining.length;
89
- stats.estimatedTimeForRemaining = estWatchTime;
90
- }
94
+ if (totalProcessed === 0) {
95
+ console.log(chalk.cyan(` Available: ${videos.length} videos, remaining: ${remaining.length}`));
96
+ const totalDuration = remaining.reduce((sum, v) => sum + (v.durationSec || 120), 0);
97
+ const estWatchTime = Math.ceil(totalDuration / 2) + remaining.length * 15;
98
+ console.log(chalk.cyan(` 預估時間: ${formatTime(estWatchTime)}`));
99
+ if (stats) {
100
+ stats.remainingVideos = remaining.length;
101
+ stats.estimatedTimeForRemaining = estWatchTime;
102
+ }
103
+ }
91
104
 
92
- for (let i = 0; i < remaining.length; i++) {
93
- const act = remaining[i];
94
- console.log(chalk.white(`\n [${i + 1}/${remaining.length}] ${act.title}`));
105
+ const act = remaining[0];
106
+ console.log(chalk.white(`\n [${totalProcessed + 1}] ${act.title}`));
95
107
 
96
108
  const url = `${BASE_URL}/course/${courseId}/learning-activity/full-screen#/${act.id}`;
97
109
  try {
@@ -100,15 +112,17 @@ async function processCourse(page, courseId, stats) {
100
112
  await page.waitForTimeout(5000);
101
113
 
102
114
  if (!page.url().includes('learning-activity')) {
103
- console.log(chalk.yellow(' [WARN] Failed to load activity page'));
115
+ console.log(chalk.yellow(' [WARN] Failed to load, locked or skipped'));
104
116
  skip++;
117
+ markDone(courseId, act.id);
118
+ totalProcessed++;
105
119
  continue;
106
120
  }
107
121
 
108
122
  try {
109
123
  const thisDuration = Math.ceil((act.durationSec || 120) / 2) + 15;
110
124
  if (stats) {
111
- stats.remainingVideos = remaining.length - i - 1;
125
+ stats.remainingVideos--;
112
126
  stats.estimatedTimeForRemaining -= thisDuration;
113
127
  if (stats.estimatedTimeForRemaining < 0) stats.estimatedTimeForRemaining = 0;
114
128
  }
@@ -116,15 +130,16 @@ async function processCourse(page, courseId, stats) {
116
130
  if (success) {
117
131
  markDone(courseId, act.id);
118
132
  done++;
119
- console.log(chalk.green(' ✓ Saved'));
133
+ console.log(chalk.green(' ✓ Saved'));
120
134
  } else {
121
135
  fail++;
122
136
  }
123
137
  } catch (e) {
124
- console.log(chalk.red(` [ERROR] ${e.message}`));
138
+ console.log(chalk.red(` [ERROR] ${e.message}`));
125
139
  fail++;
126
140
  }
127
141
 
142
+ totalProcessed++;
128
143
  await page.waitForTimeout(2000);
129
144
  }
130
145
 
package/src/video.js CHANGED
@@ -17,8 +17,7 @@ function progress(msg) {
17
17
  }
18
18
 
19
19
  function log(msg) {
20
- process.stdout.write('\n');
21
- progress(msg);
20
+ console.log(msg);
22
21
  }
23
22
 
24
23
  async function watchVideo(page, stats) {