tronclass-auto 1.5.1 → 1.5.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/video.js +40 -105
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tronclass-auto",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "自動觀看 eclass/TronClass 影片、填寫表單的 CLI 工具",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/video.js CHANGED
@@ -25,18 +25,22 @@ async function detectPlayerType(page) {
25
25
  const v = document.querySelector('video');
26
26
  if (v) return 'html5';
27
27
 
28
+ const yt = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"], iframe[src*="vimeo"]');
29
+ if (yt) return 'youtube';
30
+
28
31
  for (const f of document.querySelectorAll('iframe')) {
29
- const src = (f.src || f.getAttribute('data-src') || '').toLowerCase();
30
- if (src.includes('youtube') || src.includes('youtu.be')) return 'youtube';
31
- if (src.includes('vimeo')) return 'vimeo';
32
32
  try {
33
- if (f.contentDocument) {
34
- const fv = f.contentDocument.querySelector('video');
35
- if (fv) return 'html5';
36
- }
33
+ if (f.contentDocument && f.contentDocument.querySelector('video')) return 'html5';
37
34
  } catch (e) {}
38
35
  }
39
36
  return 'none';
37
+ }).then(async (type) => {
38
+ if (type === 'none') {
39
+ for (const f of page.frames) {
40
+ if (f.url && (f.url.includes('youtube') || f.url.includes('youtu.be'))) return 'youtube';
41
+ }
42
+ }
43
+ return type;
40
44
  });
41
45
  }
42
46
 
@@ -60,114 +64,45 @@ async function watchYouTube(page, stats) {
60
64
  let rate = 1;
61
65
 
62
66
  try {
63
- await page.evaluate(() => {
64
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
65
- if (f) f.scrollIntoView({ block: 'center' });
66
- });
67
- await page.waitForTimeout(2000);
67
+ let ytFrame = null;
68
+ for (const f of page.frames) {
69
+ if (f.url && (f.url.includes('youtube') || f.url.includes('youtu.be'))) {
70
+ ytFrame = f;
71
+ break;
72
+ }
73
+ }
68
74
 
69
- const ytFrame = page.locator('iframe[src*="youtube"], iframe[src*="youtu.be"]');
70
- await ytFrame.click({ timeout: 5000 }).catch(() => {});
71
- await page.waitForTimeout(5000);
75
+ if (!ytFrame) {
76
+ log(chalk.yellow(' YouTube frame not found'));
77
+ return false;
78
+ }
72
79
 
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));
80
+ const evalYT = async (code) => {
81
+ return await ytFrame.evaluate(code).catch(() => null);
78
82
  };
79
83
 
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] });
84
+ log(chalk.blue(' Starting video...'));
85
+ await evalYT("() => { const p = document.querySelector('#movie_player'); if (p) p.playVideo(); }");
86
+ await page.waitForTimeout(3000);
87
+
88
+ const state1 = await evalYT("() => { const p = document.querySelector('#movie_player'); return p ? p.getPlayerState() : -1; }");
89
+ if (state1 !== 1) {
90
+ log(chalk.yellow(' Retrying play...'));
91
+ await evalYT("() => { const p = document.querySelector('#movie_player'); if (p) p.playVideo(); }");
92
+ await page.waitForTimeout(3000);
93
+ }
94
+
95
+ await evalYT("() => { const p = document.querySelector('#movie_player'); if (p) { p.mute(); p.setPlaybackRate(2); } }");
85
96
  await page.waitForTimeout(2000);
86
- await postMsg({ event: 'command', func: 'playVideo', args: [] });
87
- await page.waitForTimeout(5000);
88
97
 
89
- rate = await page.evaluate(() => {
90
- return new Promise((resolve) => {
91
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
92
- if (!f) { resolve(1); return; }
93
- let resolved = false;
94
- const handler = (e) => {
95
- try {
96
- const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
97
- if (d.event === 'infoDelivery' && d.info && d.info.playbackRate !== undefined && !resolved) {
98
- resolved = true;
99
- window.removeEventListener('message', handler);
100
- resolve(d.info.playbackRate);
101
- }
102
- } catch(e) {}
103
- };
104
- window.addEventListener('message', handler);
105
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getPlaybackRate', args: [] }), '*');
106
- setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(1); } }, 5000);
107
- });
108
- });
98
+ rate = await evalYT("() => { const p = document.querySelector('#movie_player'); return p ? p.getPlaybackRate() : 1; }") || 1;
109
99
  log(chalk.blue(` Speed: ${rate}x`));
110
100
 
111
- const isPlaying = await page.evaluate(() => {
112
- return new Promise((resolve) => {
113
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
114
- if (!f) { resolve(false); return; }
115
- let resolved = false;
116
- const handler = (e) => {
117
- try {
118
- const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
119
- if (d.event === 'infoDelivery' && d.info && !resolved) {
120
- resolved = true;
121
- window.removeEventListener('message', handler);
122
- resolve(d.info.currentTime > 0);
123
- }
124
- } catch(e) {}
125
- };
126
- window.addEventListener('message', handler);
127
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getCurrentTime', args: [] }), '*');
128
- setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(false); } }, 5000);
129
- });
130
- });
131
-
132
- if (isPlaying) {
101
+ const state2 = await evalYT("() => { const p = document.querySelector('#movie_player'); return p ? p.getPlayerState() : -1; }");
102
+ if (state2 === 1) {
133
103
  log(chalk.green(' Playing ✓'));
134
104
  } else {
135
- log(chalk.yellow(' Not playing, retrying...'));
136
- await page.evaluate(() => {
137
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
138
- if (!f) return;
139
- f.contentWindow.postMessage('{"event":"listening"}', '*');
140
- });
141
- await page.waitForTimeout(3000);
142
- await page.evaluate(() => {
143
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
144
- if (!f) return;
145
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'playVideo', args: [] }), '*');
146
- });
147
- await page.waitForTimeout(5000);
148
-
149
- const retry = await page.evaluate(() => {
150
- return new Promise((resolve) => {
151
- const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
152
- if (!f) { resolve(false); return; }
153
- let resolved = false;
154
- const handler = (e) => {
155
- try {
156
- const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
157
- if (d.event === 'infoDelivery' && d.info && !resolved) {
158
- resolved = true;
159
- window.removeEventListener('message', handler);
160
- resolve(d.info.currentTime > 0);
161
- }
162
- } catch(e) {}
163
- };
164
- window.addEventListener('message', handler);
165
- f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getCurrentTime', args: [] }), '*');
166
- setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(false); } }, 5000);
167
- });
168
- });
169
- if (retry) log(chalk.green(' Playing ✓'));
170
- else log(chalk.yellow(' Could not verify playback'));
105
+ log(chalk.yellow(` State: ${state2} (may still be loading)`));
171
106
  }
172
107
  } catch (e) {
173
108
  log(chalk.yellow(` YouTube error: ${e.message}`));