tronclass-auto 1.4.6 → 1.5.0
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 +1 -1
- package/src/index.js +22 -29
- package/src/video.js +68 -48
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -81,15 +81,14 @@ async function processCourse(page, courseId, stats) {
|
|
|
81
81
|
let skip = 0;
|
|
82
82
|
let fail = 0;
|
|
83
83
|
let totalProcessed = 0;
|
|
84
|
-
let
|
|
85
|
-
let consecutiveLocked = 0;
|
|
84
|
+
let triedIds = new Set();
|
|
86
85
|
|
|
87
86
|
console.log(chalk.cyan(` [fetching course activities...]`));
|
|
88
87
|
|
|
89
88
|
while (true) {
|
|
90
89
|
const uncompleted = await getUncompleted(page, courseId);
|
|
91
90
|
const videos = uncompleted.filter(u => u.type === 'online_video');
|
|
92
|
-
const remaining = videos.filter(v => !isDone(courseId, v.id));
|
|
91
|
+
const remaining = videos.filter(v => !isDone(courseId, v.id) && !triedIds.has(v.id));
|
|
93
92
|
|
|
94
93
|
if (remaining.length === 0) break;
|
|
95
94
|
|
|
@@ -115,8 +114,9 @@ async function processCourse(page, courseId, stats) {
|
|
|
115
114
|
|
|
116
115
|
if (!page.url().includes('learning-activity')) {
|
|
117
116
|
console.log(chalk.yellow(' Page redirect, skipping'));
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
triedIds.add(act.id);
|
|
118
|
+
totalProcessed++;
|
|
119
|
+
continue;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const playerType = await page.evaluate(() => {
|
|
@@ -128,25 +128,22 @@ async function processCourse(page, courseId, stats) {
|
|
|
128
128
|
}
|
|
129
129
|
return 'none';
|
|
130
130
|
});
|
|
131
|
+
|
|
131
132
|
if (playerType === 'none') {
|
|
132
|
-
console.log(chalk.yellow(' No
|
|
133
|
+
console.log(chalk.yellow(' No player, waiting more...'));
|
|
133
134
|
await page.waitForTimeout(10000);
|
|
134
135
|
const hasVideoNow = await page.evaluate(() => {
|
|
135
|
-
if (document.querySelector('video')) return
|
|
136
|
+
if (document.querySelector('video')) return 'html5';
|
|
136
137
|
const yt = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"], iframe[src*="vimeo"]');
|
|
137
|
-
if (yt) return
|
|
138
|
-
|
|
139
|
-
try { if (f.contentDocument && f.contentDocument.querySelector('video')) return true; } catch (e) {}
|
|
140
|
-
}
|
|
141
|
-
return false;
|
|
138
|
+
if (yt) return 'iframe';
|
|
139
|
+
return 'none';
|
|
142
140
|
});
|
|
143
|
-
if (
|
|
144
|
-
console.log(chalk.yellow('
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
if (hasVideoNow === 'none') {
|
|
142
|
+
console.log(chalk.yellow(' No player found, skipping'));
|
|
143
|
+
triedIds.add(act.id);
|
|
144
|
+
totalProcessed++;
|
|
145
|
+
continue;
|
|
147
146
|
}
|
|
148
|
-
} else {
|
|
149
|
-
consecutiveLocked = 0;
|
|
150
147
|
}
|
|
151
148
|
|
|
152
149
|
try {
|
|
@@ -158,11 +155,11 @@ async function processCourse(page, courseId, stats) {
|
|
|
158
155
|
}
|
|
159
156
|
const success = await watchVideo(page, stats);
|
|
160
157
|
if (success) {
|
|
161
|
-
await page.waitForTimeout(
|
|
158
|
+
await page.waitForTimeout(10000);
|
|
162
159
|
await page.goto(`${BASE_URL}/course/${courseId}/content#/`, {
|
|
163
160
|
waitUntil: 'domcontentloaded', timeout: 15000
|
|
164
161
|
}).catch(() => {});
|
|
165
|
-
await page.waitForTimeout(
|
|
162
|
+
await page.waitForTimeout(5000);
|
|
166
163
|
|
|
167
164
|
const serverComplete = await page.evaluate((actId) => {
|
|
168
165
|
const el = document.querySelector('.learning-activities');
|
|
@@ -185,21 +182,17 @@ async function processCourse(page, courseId, stats) {
|
|
|
185
182
|
done++;
|
|
186
183
|
console.log(chalk.green(' ✓ Saved (server confirmed)'));
|
|
187
184
|
} else {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
markDone(courseId, act.id);
|
|
192
|
-
done++;
|
|
193
|
-
} else {
|
|
194
|
-
console.log(chalk.yellow(' Server not confirmed, will retry...'));
|
|
195
|
-
fail++;
|
|
196
|
-
}
|
|
185
|
+
console.log(chalk.yellow(' Server not confirmed, skipping'));
|
|
186
|
+
triedIds.add(act.id);
|
|
187
|
+
fail++;
|
|
197
188
|
}
|
|
198
189
|
} else {
|
|
190
|
+
triedIds.add(act.id);
|
|
199
191
|
fail++;
|
|
200
192
|
}
|
|
201
193
|
} catch (e) {
|
|
202
194
|
console.log(chalk.red(` [ERROR] ${e.message}`));
|
|
195
|
+
triedIds.add(act.id);
|
|
203
196
|
fail++;
|
|
204
197
|
}
|
|
205
198
|
|
package/src/video.js
CHANGED
|
@@ -60,6 +60,12 @@ async function watchYouTube(page, stats) {
|
|
|
60
60
|
let rate = 1;
|
|
61
61
|
|
|
62
62
|
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);
|
|
68
|
+
|
|
63
69
|
const ytFrame = page.locator('iframe[src*="youtube"], iframe[src*="youtu.be"]');
|
|
64
70
|
await ytFrame.click({ timeout: 5000 }).catch(() => {});
|
|
65
71
|
await page.waitForTimeout(5000);
|
|
@@ -76,16 +82,45 @@ async function watchYouTube(page, stats) {
|
|
|
76
82
|
if (!f) return;
|
|
77
83
|
f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'playVideo', args: [] }), '*');
|
|
78
84
|
});
|
|
79
|
-
await page.waitForTimeout(
|
|
85
|
+
await page.waitForTimeout(5000);
|
|
86
|
+
|
|
87
|
+
rate = await page.evaluate(() => {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
|
|
90
|
+
if (!f) { resolve(1); return; }
|
|
91
|
+
|
|
92
|
+
let resolved = false;
|
|
93
|
+
const handler = (e) => {
|
|
94
|
+
try {
|
|
95
|
+
const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
|
|
96
|
+
if (d.event === 'infoDelivery' && d.info && !resolved) {
|
|
97
|
+
resolved = true;
|
|
98
|
+
window.removeEventListener('message', handler);
|
|
99
|
+
resolve(d.info.playbackRate || 1);
|
|
100
|
+
}
|
|
101
|
+
} catch(e) {}
|
|
102
|
+
};
|
|
103
|
+
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
|
+
f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getPlaybackRate', args: [] }), '*');
|
|
108
|
+
|
|
109
|
+
setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(1); } }, 5000);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
log(chalk.blue(` Speed: ${rate}x`));
|
|
80
113
|
|
|
81
114
|
const isPlaying = await page.evaluate(() => {
|
|
82
115
|
return new Promise((resolve) => {
|
|
83
116
|
const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
|
|
84
117
|
if (!f) { resolve(false); return; }
|
|
118
|
+
let resolved = false;
|
|
85
119
|
const handler = (e) => {
|
|
86
120
|
try {
|
|
87
121
|
const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
|
|
88
|
-
if (d.event === 'infoDelivery' && d.info) {
|
|
122
|
+
if (d.event === 'infoDelivery' && d.info && !resolved) {
|
|
123
|
+
resolved = true;
|
|
89
124
|
window.removeEventListener('message', handler);
|
|
90
125
|
resolve(d.info.currentTime > 0);
|
|
91
126
|
}
|
|
@@ -93,67 +128,52 @@ async function watchYouTube(page, stats) {
|
|
|
93
128
|
};
|
|
94
129
|
window.addEventListener('message', handler);
|
|
95
130
|
f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getCurrentTime', args: [] }), '*');
|
|
96
|
-
setTimeout(() => { window.removeEventListener('message', handler); resolve(false); }, 5000);
|
|
131
|
+
setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(false); } }, 5000);
|
|
97
132
|
});
|
|
98
133
|
});
|
|
99
134
|
|
|
100
|
-
if (
|
|
101
|
-
log(chalk.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
await frame.locator('.ytp-large-play-button, .ytp-play-button').first().click({ timeout: 5000 }).catch(() => {});
|
|
105
|
-
} catch(e) {}
|
|
106
|
-
await page.waitForTimeout(3000);
|
|
107
|
-
|
|
135
|
+
if (isPlaying) {
|
|
136
|
+
log(chalk.green(' Playing ✓'));
|
|
137
|
+
} else {
|
|
138
|
+
log(chalk.yellow(' Not playing, retrying...'));
|
|
108
139
|
await page.evaluate(() => {
|
|
109
140
|
const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
|
|
110
141
|
if (!f) return;
|
|
111
142
|
f.contentWindow.postMessage('{"event":"listening"}', '*');
|
|
112
143
|
});
|
|
113
|
-
await page.waitForTimeout(
|
|
144
|
+
await page.waitForTimeout(3000);
|
|
114
145
|
await page.evaluate(() => {
|
|
115
146
|
const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
|
|
116
147
|
if (!f) return;
|
|
117
148
|
f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'playVideo', args: [] }), '*');
|
|
118
149
|
});
|
|
119
|
-
await page.waitForTimeout(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const handler = (e) => {
|
|
141
|
-
try {
|
|
142
|
-
const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
|
|
143
|
-
if (d.event === 'infoDelivery' && d.info && d.info.playbackRate) {
|
|
144
|
-
window.removeEventListener('message', handler);
|
|
145
|
-
resolve(d.info.playbackRate);
|
|
146
|
-
}
|
|
147
|
-
} catch(e) {}
|
|
148
|
-
};
|
|
149
|
-
window.addEventListener('message', handler);
|
|
150
|
-
f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getPlaybackRate', args: [] }), '*');
|
|
151
|
-
setTimeout(() => { window.removeEventListener('message', handler); resolve(1); }, 3000);
|
|
150
|
+
await page.waitForTimeout(5000);
|
|
151
|
+
|
|
152
|
+
const retry = await page.evaluate(() => {
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
const f = document.querySelector('iframe[src*="youtube"], iframe[src*="youtu.be"]');
|
|
155
|
+
if (!f) { resolve(false); return; }
|
|
156
|
+
let resolved = false;
|
|
157
|
+
const handler = (e) => {
|
|
158
|
+
try {
|
|
159
|
+
const d = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
|
|
160
|
+
if (d.event === 'infoDelivery' && d.info && !resolved) {
|
|
161
|
+
resolved = true;
|
|
162
|
+
window.removeEventListener('message', handler);
|
|
163
|
+
resolve(d.info.currentTime > 0);
|
|
164
|
+
}
|
|
165
|
+
} catch(e) {}
|
|
166
|
+
};
|
|
167
|
+
window.addEventListener('message', handler);
|
|
168
|
+
f.contentWindow.postMessage(JSON.stringify({ event: 'command', func: 'getCurrentTime', args: [] }), '*');
|
|
169
|
+
setTimeout(() => { if (!resolved) { resolved = true; window.removeEventListener('message', handler); resolve(false); } }, 5000);
|
|
170
|
+
});
|
|
152
171
|
});
|
|
153
|
-
|
|
154
|
-
|
|
172
|
+
if (retry) log(chalk.green(' Playing ✓'));
|
|
173
|
+
else log(chalk.yellow(' Could not verify playback'));
|
|
174
|
+
}
|
|
155
175
|
} catch (e) {
|
|
156
|
-
log(chalk.yellow(` YouTube
|
|
176
|
+
log(chalk.yellow(` YouTube error: ${e.message}`));
|
|
157
177
|
}
|
|
158
178
|
|
|
159
179
|
await page.waitForTimeout(2000);
|