tronclass-auto 1.2.5 → 1.3.1

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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +31 -11
  3. package/src/tui.js +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tronclass-auto",
3
- "version": "1.2.5",
3
+ "version": "1.3.1",
4
4
  "description": "自動觀看 eclass/TronClass 影片、填寫表單的 CLI 工具",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -26,7 +26,20 @@ async function getUncompleted(page, courseId) {
26
26
  const a = actScope.activity;
27
27
  let completeness = '';
28
28
  try { completeness = scope.getActivityCompleteness(a); } catch (e) {}
29
- if (completeness !== 'full') {
29
+ if (completeness === 'full') return;
30
+
31
+ let isUpcoming = false;
32
+ let isExpired = false;
33
+ try { isUpcoming = scope.activityUpcoming(a); } catch (e) {}
34
+ try { isExpired = scope.activityExpired(a); } catch (e) {}
35
+ if (isUpcoming) return;
36
+
37
+ let timeRange = '';
38
+ try {
39
+ const txt = actEl.textContent || '';
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];
42
+ } catch (e) {}
30
43
  let durationSec = 0;
31
44
  try {
32
45
  const durText = actEl.querySelector('.activity-attribute');
@@ -48,9 +61,9 @@ async function getUncompleted(page, courseId) {
48
61
  id: a.id,
49
62
  title: (a.title || '').substring(0, 60),
50
63
  type: a.type || '',
51
- durationSec
64
+ durationSec,
65
+ timeRange
52
66
  });
53
- }
54
67
  });
55
68
  return result;
56
69
  });
@@ -161,23 +174,30 @@ async function showStatus() {
161
174
  try {
162
175
  const uncompleted = await getUncompleted(page, courseId);
163
176
  const videos = uncompleted.filter(u => u.type === 'online_video');
177
+ const remaining = videos.filter(v => !isDone(courseId, v.id));
164
178
  const exams = uncompleted.filter(u => u.type === 'exam').length;
165
- const other = uncompleted.length - videos.length - exams;
166
179
  totalVideos += videos.length;
167
- totalRemaining += videos.length;
180
+ totalRemaining += remaining.length;
168
181
  totalExams += exams;
169
182
 
170
- const bar = generateBar(videos.length, 50);
171
- console.log(chalk.cyan('│') + ` [${courseId}] ${videos.length} videos ${exams} exams`.padEnd(50) + chalk.cyan('│'));
172
- console.log(chalk.cyan('│') + ` ${bar}`.padEnd(50) + chalk.cyan('│'));
183
+ const pct = videos.length > 0 ? Math.floor(((videos.length - remaining.length) / videos.length) * 100) : 100;
184
+ const filled = Math.floor(50 * pct / 100);
185
+ const empty = 50 - filled;
186
+ const bar = pct === 100
187
+ ? chalk.green('█'.repeat(50))
188
+ : chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
189
+
190
+ const doneCount = videos.length - remaining.length;
191
+ console.log(chalk.cyan('│') + ` [${courseId}] ${doneCount}/${videos.length} videos ${exams} exams`.padEnd(50) + chalk.cyan('│'));
192
+ console.log(chalk.cyan('│') + ` ${bar} ${pct}%`.padEnd(52) + chalk.cyan('│'));
173
193
  } catch (e) {
174
194
  console.log(chalk.cyan('│') + ` [${courseId}] ERROR`.padEnd(50) + chalk.cyan('│'));
175
195
  }
176
196
  }
177
197
 
178
198
  console.log(chalk.cyan('├' + '─'.repeat(48) + '┤'));
179
- console.log(chalk.cyan('│') + ` 總計: ${totalRemaining} videos, ${totalExams} exams`.padEnd(50) + chalk.cyan('│'));
180
- console.log(chalk.cyan('│') + ` 預估時間: ${formatTime(totalRemaining * 120)}`.padEnd(50) + chalk.cyan('│'));
199
+ console.log(chalk.cyan('│') + ` 剩餘: ${totalRemaining} videos | 考試: ${totalExams}`.padEnd(50) + chalk.cyan('│'));
200
+ console.log(chalk.cyan('│') + ` 預估: ${formatTime(totalRemaining * 120)}`.padEnd(50) + chalk.cyan('│'));
181
201
  console.log(chalk.cyan('└' + '─'.repeat(48) + '┘'));
182
202
  console.log('');
183
203
 
@@ -199,10 +219,10 @@ async function launchBrowser(headless) {
199
219
  headless,
200
220
  slowMo: 50,
201
221
  args: [
222
+ '--disable-accelerated-video-decode',
202
223
  '--disable-background-timer-throttling',
203
224
  '--disable-backgrounding-occluded-windows',
204
225
  '--disable-renderer-backgrounding',
205
- '--disable-features=MediaFoundationVideoCapture,HardwareMediaKeyHandling',
206
226
  ]
207
227
  };
208
228
  if (exePath) launchOpts.executablePath = exePath;
package/src/tui.js CHANGED
@@ -2,6 +2,7 @@ const inquirer = require('inquirer');
2
2
  const chalk = require('chalk');
3
3
  const { loadConfig, saveConfig, BASE_URL } = require('./config');
4
4
  const { loadCookies } = require('./auth');
5
+ const pkg = require('../package.json');
5
6
 
6
7
  function box(lines, width = 52) {
7
8
  const top = '╔' + '═'.repeat(width - 2) + '╗';
@@ -25,7 +26,7 @@ async function mainMenu() {
25
26
  const config = loadConfig();
26
27
 
27
28
  box([
28
- chalk.bold.white('tronclass-auto v1.1.0'),
29
+ chalk.bold.white(`tronclass-auto v${pkg.version}`),
29
30
  chalk.gray('自動觀看 eclass/TronClass 影片'),
30
31
  '',
31
32
  `課程: ${chalk.cyan(config.courses.length + ' 個')} | Cookie: ${chalk.green('✓')} | 倍速: ${chalk.yellow(config.playbackRate || 2)}x`,