@chatpanel/events 0.30.0 → 0.31.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/media-transcript.js +48 -0
- package/package.json +2 -2
package/media-transcript.js
CHANGED
|
@@ -170,6 +170,54 @@ export function pickCaptionTrack(tracks, { language = '', languages = ['en'] } =
|
|
|
170
170
|
.sort((a, b) => b.s - a.s)[0].t;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
// --------------------------------------------------------------------------
|
|
174
|
+
// The InnerTube player request — how a caption URL that WORKS is obtained
|
|
175
|
+
// --------------------------------------------------------------------------
|
|
176
|
+
//
|
|
177
|
+
// The caption URLs printed into the watch page's HTML answer HTTP 200 with an EMPTY BODY —
|
|
178
|
+
// measured on every video tried, with and without session cookies, Referer and Origin. The
|
|
179
|
+
// ones returned by the InnerTube player endpoint for the ANDROID client do not.
|
|
180
|
+
//
|
|
181
|
+
// AND THE CLIENT VERSION IS THE WHOLE DIFFERENCE, which is worth stating because it is
|
|
182
|
+
// invisible and it will go stale:
|
|
183
|
+
//
|
|
184
|
+
// clientVersion 20.10.38 -> 1 track, 60,441 bytes of captions
|
|
185
|
+
// clientVersion 19.09.37 -> no captionTracks at all
|
|
186
|
+
// clientVersion 17.31.35 -> no captionTracks at all
|
|
187
|
+
//
|
|
188
|
+
// A stale version does not error. It returns a well-formed player response with the
|
|
189
|
+
// `captions` block missing, which reads exactly like "this video has no subtitles" — so the
|
|
190
|
+
// failure mode of letting this rot is a feature that quietly claims videos have no captions.
|
|
191
|
+
// tests/media-transcript.test.js pins the shape; a live check is the client's job.
|
|
192
|
+
|
|
193
|
+
/** The InnerTube client whose player response carries usable caption URLs. */
|
|
194
|
+
export const INNERTUBE_ANDROID = Object.freeze({ clientName: 'ANDROID', clientVersion: '20.10.38' });
|
|
195
|
+
|
|
196
|
+
/** The public InnerTube key is printed into every watch page; it is not a secret. */
|
|
197
|
+
export function innertubeApiKeyFromHtml(html) {
|
|
198
|
+
const m = /"INNERTUBE_API_KEY":\s*"([^"]+)"/.exec(String(html || ''))
|
|
199
|
+
|| /INNERTUBE_API_KEY\\":\\"([^\\"]+)/.exec(String(html || ''));
|
|
200
|
+
return m ? m[1] : '';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* The request to make, as data — so the caller performs it wherever its network is.
|
|
205
|
+
*
|
|
206
|
+
* Returned rather than sent for the same reason nothing else here fetches: the extension, the
|
|
207
|
+
* bridge and a mobile client each have their own idea of what "fetch" means, and this file
|
|
208
|
+
* has to run in all three.
|
|
209
|
+
*/
|
|
210
|
+
export function innertubePlayerRequest(videoId, { apiKey = '', client = INNERTUBE_ANDROID } = {}) {
|
|
211
|
+
if (!videoId) return null;
|
|
212
|
+
const query = apiKey ? `?key=${encodeURIComponent(apiKey)}` : '';
|
|
213
|
+
return {
|
|
214
|
+
url: `https://www.youtube.com/youtubei/v1/player${query}`,
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: { 'content-type': 'application/json' },
|
|
217
|
+
body: JSON.stringify({ context: { client: { ...client } }, videoId }),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
173
221
|
/**
|
|
174
222
|
* Ask a caption URL for a specific serialisation.
|
|
175
223
|
*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/events",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The canonical ChatPanel event-log and capability contracts
|
|
3
|
+
"version": "0.31.0",
|
|
4
|
+
"description": "The canonical ChatPanel event-log and capability contracts — typed durable facts, clock-free deterministic linearization, schema upcasting, and the invariants the replay harness asserts. Pure, dependency-free ESM shared by the ChatPanel extension, gateway and bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"exports": {
|