tronclass-auto 1.5.0 → 1.5.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 +27 -21
  3. package/src/video.js +17 -20
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tronclass-auto",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "自動觀看 eclass/TronClass 影片、填寫表單的 CLI 工具",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -155,34 +155,40 @@ async function processCourse(page, courseId, stats) {
155
155
  }
156
156
  const success = await watchVideo(page, stats);
157
157
  if (success) {
158
- await page.waitForTimeout(10000);
159
- await page.goto(`${BASE_URL}/course/${courseId}/content#/`, {
160
- waitUntil: 'domcontentloaded', timeout: 15000
161
- }).catch(() => {});
162
- await page.waitForTimeout(5000);
163
-
164
- const serverComplete = await page.evaluate((actId) => {
165
- const el = document.querySelector('.learning-activities');
166
- if (!el) return false;
167
- const scope = angular.element(el).scope();
168
- if (!scope) return false;
169
- const activities = document.querySelectorAll('.learning-activity.sortable');
170
- for (const actEl of activities) {
171
- const actScope = angular.element(actEl).scope();
172
- if (!actScope || !actScope.activity) continue;
173
- if (actScope.activity.id == actId) {
174
- try { return scope.getActivityCompleteness(actScope.activity) === 'full'; } catch (e) {}
158
+ let serverComplete = false;
159
+ for (let attempt = 0; attempt < 5; attempt++) {
160
+ await page.waitForTimeout(10000);
161
+ await page.goto(`${BASE_URL}/course/${courseId}/content#/`, {
162
+ waitUntil: 'domcontentloaded', timeout: 15000
163
+ }).catch(() => {});
164
+ await page.waitForTimeout(5000);
165
+
166
+ serverComplete = await page.evaluate((actId) => {
167
+ const el = document.querySelector('.learning-activities');
168
+ if (!el) return false;
169
+ const scope = angular.element(el).scope();
170
+ if (!scope) return false;
171
+ const activities = document.querySelectorAll('.learning-activity.sortable');
172
+ for (const actEl of activities) {
173
+ const actScope = angular.element(actEl).scope();
174
+ if (!actScope || !actScope.activity) continue;
175
+ if (actScope.activity.id == actId) {
176
+ try { return scope.getActivityCompleteness(actScope.activity) === 'full'; } catch (e) {}
177
+ }
175
178
  }
176
- }
177
- return false;
178
- }, act.id);
179
+ return false;
180
+ }, act.id);
181
+
182
+ if (serverComplete) break;
183
+ console.log(chalk.gray(` Checking server... (${attempt + 1}/5)`));
184
+ }
179
185
 
180
186
  if (serverComplete) {
181
187
  markDone(courseId, act.id);
182
188
  done++;
183
189
  console.log(chalk.green(' ✓ Saved (server confirmed)'));
184
190
  } else {
185
- console.log(chalk.yellow(' Server not confirmed, skipping'));
191
+ console.log(chalk.yellow(' Server not confirmed after retries, skipping'));
186
192
  triedIds.add(act.id);
187
193
  fail++;
188
194
  }
package/src/video.js CHANGED
@@ -70,42 +70,39 @@ async function watchYouTube(page, stats) {
70
70
  await ytFrame.click({ timeout: 5000 }).catch(() => {});
71
71
  await page.waitForTimeout(5000);
72
72
 
73
- await page.evaluate(() => {
74
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
75
- if (!f) return;
76
- f.contentWindow.postMessage('{"event":"listening"}', '*');
77
- });
78
- await page.waitForTimeout(3000);
73
+ const postMsg = async (msg) => {
74
+ await page.evaluate((m) => {
75
+ const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
76
+ if (f) f.contentWindow.postMessage(m, '*');
77
+ }, typeof msg === 'string' ? msg : JSON.stringify(msg));
78
+ };
79
79
 
80
- await page.evaluate(() => {
81
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
82
- if (!f) return;
83
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'playVideo', args: [] }), '*');
84
- });
80
+ await postMsg('{"event":"listening"}');
81
+ await page.waitForTimeout(2000);
82
+ await postMsg({ event: 'command', func: 'mute', args: [] });
83
+ await page.waitForTimeout(1000);
84
+ await postMsg({ event: 'command', func: 'setPlaybackRate', args: [2] });
85
+ await page.waitForTimeout(2000);
86
+ await postMsg({ event: 'command', func: 'playVideo', args: [] });
85
87
  await page.waitForTimeout(5000);
86
88
 
87
89
  rate = await page.evaluate(() => {
88
90
  return new Promise((resolve) => {
89
91
  const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
90
92
  if (!f) { resolve(1); return; }
91
-
92
93
  let resolved = false;
93
94
  const handler = (e) => {
94
95
  try {
95
96
  const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
96
- if (d.event === 'infoDelivery' && d.info && !resolved) {
97
+ if (d.event === 'infoDelivery' && d.info && d.info.playbackRate !== undefined && !resolved) {
97
98
  resolved = true;
98
99
  window.removeEventListener('message', handler);
99
- resolve(d.info.playbackRate || 1);
100
+ resolve(d.info.playbackRate);
100
101
  }
101
102
  } catch(e) {}
102
103
  };
103
104
  window.addEventListener('message', handler);
104
-
105
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'mute', args: [] }), '*');
106
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'setPlaybackRate', args: [2] }), '*');
107
105
  f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getPlaybackRate', args: [] }), '*');
108
-
109
106
  setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(1); } }, 5000);
110
107
  });
111
108
  });
@@ -187,8 +184,8 @@ async function watchYouTube(page, stats) {
187
184
  return 180;
188
185
  });
189
186
 
190
- const waitTime = ytDuration + 30;
191
- log(chalk.blue(` ~${formatTime(ytDuration)} video → ~${formatTime(waitTime)} (2x playback, full wait)`));
187
+ const waitTime = (rate >= 2 ? Math.ceil(ytDuration / 2) : ytDuration) + 30;
188
+ log(chalk.blue(` ~${formatTime(ytDuration)} video → ~${formatTime(waitTime)} (${rate}x)`));
192
189
 
193
190
  if (stats) {
194
191
  stats.currentVideoDuration = ytDuration;