roku-mcp 1.5.2 → 1.7.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/README.md +176 -26
- package/dist/assets/streamprobe/components/StreamProbe.xml +96 -0
- package/dist/assets/streamprobe/manifest +11 -0
- package/dist/assets/streamprobe/source/main.brs +29 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/stream/charles.d.ts +50 -0
- package/dist/stream/charles.d.ts.map +1 -0
- package/dist/stream/charles.js +268 -0
- package/dist/stream/charles.js.map +1 -0
- package/dist/stream/diagnose.d.ts +54 -0
- package/dist/stream/diagnose.d.ts.map +1 -0
- package/dist/stream/diagnose.js +568 -0
- package/dist/stream/diagnose.js.map +1 -0
- package/dist/stream/error-info.d.ts +50 -0
- package/dist/stream/error-info.d.ts.map +1 -0
- package/dist/stream/error-info.js +168 -0
- package/dist/stream/error-info.js.map +1 -0
- package/dist/stream/live.d.ts +43 -0
- package/dist/stream/live.d.ts.map +1 -0
- package/dist/stream/live.js +258 -0
- package/dist/stream/live.js.map +1 -0
- package/dist/stream/manifest.d.ts +90 -0
- package/dist/stream/manifest.d.ts.map +1 -0
- package/dist/stream/manifest.js +480 -0
- package/dist/stream/manifest.js.map +1 -0
- package/dist/stream/roku-specs.json +94 -0
- package/dist/tools/edit-ui.d.ts +3 -0
- package/dist/tools/edit-ui.d.ts.map +1 -0
- package/dist/tools/edit-ui.js +536 -0
- package/dist/tools/edit-ui.js.map +1 -0
- package/dist/tools/stream.d.ts +3 -0
- package/dist/tools/stream.d.ts.map +1 -0
- package/dist/tools/stream.js +194 -0
- package/dist/tools/stream.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a Charles Proxy session (or HAR capture) into normalized HTTP entries
|
|
3
|
+
* and classify each request as manifest / segment / license / key / other.
|
|
4
|
+
*
|
|
5
|
+
* Supported export formats:
|
|
6
|
+
* - Charles ".chlsj" (JSON Session File): either a single JSON array of
|
|
7
|
+
* request objects, or newline-delimited JSON (one object per line).
|
|
8
|
+
* - ".har" (HTTP Archive): the standard `{ log: { entries: [...] } }` shape
|
|
9
|
+
* produced by Charles "Export > HAR" and by browser devtools.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: the binary ".chls" format is NOT supported (it is a proprietary binary
|
|
12
|
+
* blob). Callers should instruct the user to export as ".chlsj" or ".har".
|
|
13
|
+
*
|
|
14
|
+
* Charles .chlsj entry shape (varies slightly by version) roughly looks like:
|
|
15
|
+
* {
|
|
16
|
+
* "host": "...", "method": "GET", "path": "/x.m3u8",
|
|
17
|
+
* "scheme": "https", "status": 200,
|
|
18
|
+
* "request": { "header": { "headers": [{ "name", "value" }] }, "body": {...} },
|
|
19
|
+
* "response": { "header": { "headers": [...] }, "body": { "text"|"encoded", "contentType" } },
|
|
20
|
+
* "times": { "totalDuration": 123 }
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
function lowerHeaderMap(headers) {
|
|
24
|
+
const out = {};
|
|
25
|
+
if (!Array.isArray(headers))
|
|
26
|
+
return out;
|
|
27
|
+
for (const h of headers) {
|
|
28
|
+
if (h && typeof h.name === 'string') {
|
|
29
|
+
out[h.name.toLowerCase()] = typeof h.value === 'string' ? h.value : String(h.value ?? '');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
function classify(url, method, mimeType, reqHeaders) {
|
|
35
|
+
const path = url.split('?')[0].toLowerCase();
|
|
36
|
+
const mime = (mimeType ?? '').toLowerCase();
|
|
37
|
+
if (path.endsWith('.m3u8') || path.endsWith('.mpd') || path.endsWith('.m3u') ||
|
|
38
|
+
mime.includes('mpegurl') || mime.includes('dash+xml')) {
|
|
39
|
+
return 'manifest';
|
|
40
|
+
}
|
|
41
|
+
if (path.endsWith('.ts') || path.endsWith('.m4s') || path.endsWith('.mp4') ||
|
|
42
|
+
path.endsWith('.cmfv') || path.endsWith('.cmfa') || path.endsWith('.cmft') ||
|
|
43
|
+
path.endsWith('.aac') || path.endsWith('.m4a') || path.endsWith('.m4v') ||
|
|
44
|
+
mime.includes('video/') || mime.includes('audio/') || mime.includes('iso.segment')) {
|
|
45
|
+
return 'segment';
|
|
46
|
+
}
|
|
47
|
+
if (path.endsWith('.key') || mime.includes('pskc+xml')) {
|
|
48
|
+
return 'key';
|
|
49
|
+
}
|
|
50
|
+
// License requests: typically POST to a DRM endpoint, often octet-stream or json.
|
|
51
|
+
const accept = (reqHeaders['accept'] ?? '').toLowerCase();
|
|
52
|
+
const ct = (reqHeaders['content-type'] ?? '').toLowerCase();
|
|
53
|
+
if (method.toUpperCase() === 'POST' &&
|
|
54
|
+
(path.includes('license') || path.includes('licence') || path.includes('widevine') ||
|
|
55
|
+
path.includes('playready') || path.includes('drm') || path.includes('wv/') ||
|
|
56
|
+
path.includes('/cenc') || ct.includes('octet-stream') || accept.includes('octet-stream'))) {
|
|
57
|
+
return 'license';
|
|
58
|
+
}
|
|
59
|
+
if (path.includes('license') || path.includes('widevine') || path.includes('playready') || path.includes('/drm')) {
|
|
60
|
+
return 'license';
|
|
61
|
+
}
|
|
62
|
+
return 'other';
|
|
63
|
+
}
|
|
64
|
+
function decodeBody(body) {
|
|
65
|
+
if (!body || typeof body !== 'object')
|
|
66
|
+
return {};
|
|
67
|
+
const contentType = typeof body.contentType === 'string' ? body.contentType.toLowerCase() :
|
|
68
|
+
typeof body.mimeType === 'string' ? body.mimeType.toLowerCase() : undefined;
|
|
69
|
+
// Charles uses `text` (plain), or `encoded` (base64) with `encoding: "base64"`.
|
|
70
|
+
if (typeof body.text === 'string') {
|
|
71
|
+
if ((body.encoding ?? '').toLowerCase() === 'base64') {
|
|
72
|
+
try {
|
|
73
|
+
return { text: Buffer.from(body.text, 'base64').toString('utf-8'), mimeType: contentType };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return { mimeType: contentType };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { text: body.text, mimeType: contentType };
|
|
80
|
+
}
|
|
81
|
+
if (typeof body.encoded === 'string') {
|
|
82
|
+
try {
|
|
83
|
+
return { text: Buffer.from(body.encoded, 'base64').toString('utf-8'), mimeType: contentType };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { mimeType: contentType };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { mimeType: contentType };
|
|
90
|
+
}
|
|
91
|
+
/** Normalize one Charles .chlsj entry. */
|
|
92
|
+
function normalizeChlsjEntry(raw) {
|
|
93
|
+
if (!raw || typeof raw !== 'object')
|
|
94
|
+
return undefined;
|
|
95
|
+
const scheme = raw.scheme ?? raw.protocol ?? 'https';
|
|
96
|
+
const host = raw.host ?? raw.remoteHost ?? '';
|
|
97
|
+
const path = raw.path ?? raw.query ? `${raw.path ?? ''}${raw.query ? '?' + raw.query : ''}` : raw.path ?? '/';
|
|
98
|
+
const explicitUrl = typeof raw.url === 'string' ? raw.url : undefined;
|
|
99
|
+
const url = explicitUrl ?? (host ? `${scheme}://${host}${path.startsWith('/') ? '' : '/'}${path}` : path);
|
|
100
|
+
const method = (raw.method ?? 'GET').toUpperCase();
|
|
101
|
+
const status = Number(raw.status ?? raw.responseCode ?? raw.response?.status ?? 0) || 0;
|
|
102
|
+
const reqHeaders = lowerHeaderMap(raw.request?.header?.headers ?? raw.request?.headers);
|
|
103
|
+
const respHeaders = lowerHeaderMap(raw.response?.header?.headers ?? raw.response?.headers);
|
|
104
|
+
const decoded = decodeBody(raw.response?.body);
|
|
105
|
+
const mimeType = decoded.mimeType ?? respHeaders['content-type']?.split(';')[0]?.toLowerCase();
|
|
106
|
+
const durationMs = typeof raw.times?.totalDuration === 'number' ? raw.times.totalDuration :
|
|
107
|
+
typeof raw.totalDuration === 'number' ? raw.totalDuration : undefined;
|
|
108
|
+
const kind = classify(url, method, mimeType, reqHeaders);
|
|
109
|
+
return {
|
|
110
|
+
url,
|
|
111
|
+
host: host || safeHost(url),
|
|
112
|
+
path: path || new URLSafe(url).pathname,
|
|
113
|
+
method,
|
|
114
|
+
status,
|
|
115
|
+
reqHeaders,
|
|
116
|
+
respHeaders,
|
|
117
|
+
respBody: decoded.text,
|
|
118
|
+
mimeType,
|
|
119
|
+
durationMs,
|
|
120
|
+
kind,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** Normalize one HAR entry. */
|
|
124
|
+
function normalizeHarEntry(raw) {
|
|
125
|
+
if (!raw || typeof raw !== 'object' || !raw.request)
|
|
126
|
+
return undefined;
|
|
127
|
+
const url = raw.request.url ?? '';
|
|
128
|
+
const method = (raw.request.method ?? 'GET').toUpperCase();
|
|
129
|
+
const status = Number(raw.response?.status ?? 0) || 0;
|
|
130
|
+
const reqHeaders = lowerHeaderMap(raw.request.headers);
|
|
131
|
+
const respHeaders = lowerHeaderMap(raw.response?.headers);
|
|
132
|
+
const harContent = raw.response?.content;
|
|
133
|
+
let respBody;
|
|
134
|
+
let mimeType = harContent?.mimeType?.split(';')[0]?.toLowerCase();
|
|
135
|
+
if (harContent && typeof harContent.text === 'string') {
|
|
136
|
+
if ((harContent.encoding ?? '').toLowerCase() === 'base64') {
|
|
137
|
+
try {
|
|
138
|
+
respBody = Buffer.from(harContent.text, 'base64').toString('utf-8');
|
|
139
|
+
}
|
|
140
|
+
catch { /* ignore */ }
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
respBody = harContent.text;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!mimeType)
|
|
147
|
+
mimeType = respHeaders['content-type']?.split(';')[0]?.toLowerCase();
|
|
148
|
+
const durationMs = typeof raw.time === 'number' ? raw.time : undefined;
|
|
149
|
+
const kind = classify(url, method, mimeType, reqHeaders);
|
|
150
|
+
return {
|
|
151
|
+
url,
|
|
152
|
+
host: safeHost(url),
|
|
153
|
+
path: new URLSafe(url).pathname,
|
|
154
|
+
method,
|
|
155
|
+
status,
|
|
156
|
+
reqHeaders,
|
|
157
|
+
respHeaders,
|
|
158
|
+
respBody,
|
|
159
|
+
mimeType,
|
|
160
|
+
durationMs,
|
|
161
|
+
kind,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** A tiny URL helper that never throws on malformed input. */
|
|
165
|
+
class URLSafe {
|
|
166
|
+
pathname = '/';
|
|
167
|
+
host = '';
|
|
168
|
+
constructor(url) {
|
|
169
|
+
try {
|
|
170
|
+
const u = new URL(url);
|
|
171
|
+
this.pathname = u.pathname;
|
|
172
|
+
this.host = u.host;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
const m = /^[a-z]+:\/\/([^/]+)(\/[^?#]*)?/i.exec(url);
|
|
176
|
+
if (m) {
|
|
177
|
+
this.host = m[1];
|
|
178
|
+
this.pathname = m[2] ?? '/';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function safeHost(url) {
|
|
184
|
+
return new URLSafe(url).host;
|
|
185
|
+
}
|
|
186
|
+
/** Parse the raw text of a Charles .chlsj or .har file. */
|
|
187
|
+
export function parseCharlesText(text) {
|
|
188
|
+
const notes = [];
|
|
189
|
+
const trimmed = text.trimStart();
|
|
190
|
+
// HAR detection.
|
|
191
|
+
if (trimmed.startsWith('{')) {
|
|
192
|
+
try {
|
|
193
|
+
const obj = JSON.parse(text);
|
|
194
|
+
if (obj && obj.log && Array.isArray(obj.log.entries)) {
|
|
195
|
+
const entries = obj.log.entries
|
|
196
|
+
.map(normalizeHarEntry)
|
|
197
|
+
.filter((e) => !!e);
|
|
198
|
+
return finalize(entries, 'har', notes);
|
|
199
|
+
}
|
|
200
|
+
// A single .chlsj object (rare) — treat as a one-element array.
|
|
201
|
+
const one = normalizeChlsjEntry(obj);
|
|
202
|
+
if (one)
|
|
203
|
+
return finalize([one], 'chlsj', notes);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
/* fall through to line-delimited parsing */
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// .chlsj as a JSON array.
|
|
210
|
+
if (trimmed.startsWith('[')) {
|
|
211
|
+
try {
|
|
212
|
+
const arr = JSON.parse(text);
|
|
213
|
+
if (Array.isArray(arr)) {
|
|
214
|
+
const entries = arr
|
|
215
|
+
.map(normalizeChlsjEntry)
|
|
216
|
+
.filter((e) => !!e);
|
|
217
|
+
return finalize(entries, 'chlsj', notes);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
/* fall through */
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Newline-delimited JSON objects (one Charles record per line).
|
|
225
|
+
const lineEntries = [];
|
|
226
|
+
let parsedAny = false;
|
|
227
|
+
for (const line of text.split(/\r?\n/)) {
|
|
228
|
+
const t = line.trim().replace(/,$/, '');
|
|
229
|
+
if (t === '' || t === '[' || t === ']')
|
|
230
|
+
continue;
|
|
231
|
+
try {
|
|
232
|
+
const obj = JSON.parse(t);
|
|
233
|
+
const norm = normalizeChlsjEntry(obj);
|
|
234
|
+
if (norm) {
|
|
235
|
+
lineEntries.push(norm);
|
|
236
|
+
parsedAny = true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
/* skip non-JSON lines */
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (parsedAny) {
|
|
244
|
+
notes.push('Parsed as newline-delimited JSON (one record per line).');
|
|
245
|
+
return finalize(lineEntries, 'chlsj', notes);
|
|
246
|
+
}
|
|
247
|
+
throw new Error('Could not parse the Charles session. Export it from Charles as a "JSON Session File" (.chlsj) or HAR (.har). ' +
|
|
248
|
+
'Binary ".chls" files are not supported.');
|
|
249
|
+
}
|
|
250
|
+
function finalize(entries, format, notes) {
|
|
251
|
+
const failing = entries.filter(isFailing);
|
|
252
|
+
if (entries.length === 0)
|
|
253
|
+
notes.push('No HTTP entries were found in the session.');
|
|
254
|
+
return { entries, failing, format, notes };
|
|
255
|
+
}
|
|
256
|
+
/** A request is "failing" if it has a non-2xx status, or an empty body where one is expected. */
|
|
257
|
+
export function isFailing(entry) {
|
|
258
|
+
if (entry.status === 0)
|
|
259
|
+
return false; // unknown — don't flag
|
|
260
|
+
if (entry.status >= 400)
|
|
261
|
+
return true;
|
|
262
|
+
if (entry.status >= 300 && entry.status < 400) {
|
|
263
|
+
// Redirects on license/manifest are a common Roku gotcha.
|
|
264
|
+
return entry.kind === 'license' || entry.kind === 'manifest';
|
|
265
|
+
}
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=charles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charles.js","sourceRoot":"","sources":["../../src/stream/charles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA4BH,SAAS,cAAc,CAAC,OAA6D;IACnF,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,MAAc,EAAE,QAA4B,EAAE,UAAkC;IAC7G,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAE5C,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACxE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1D,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAC1E,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACvE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACvF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACvD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,kFAAkF;IAClF,MAAM,MAAM,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM;QAC/B,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;YACjF,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC1E,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;QAC/F,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACjH,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,IAAS;IAC3B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACjD,MAAM,WAAW,GACf,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAE9E,gFAAgF;IAChF,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;YACrD,IAAI,CAAC;gBAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;YAAC,CAAC;YACnG,MAAM,CAAC;gBAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;YAAC,CAAC;QAC7C,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACrC,IAAI,CAAC;YAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,CAAC;QACtG,MAAM,CAAC;YAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAAC,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC;AAED,0CAA0C;AAC1C,SAAS,mBAAmB,CAAC,GAAQ;IACnC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAEtD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC;IACrD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC;IAC9G,MAAM,WAAW,GAAG,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,GAAG,GAAG,WAAW,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAE1G,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;IAExF,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACxF,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE3F,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,WAAW,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IAC/F,MAAM,UAAU,GACd,OAAO,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxE,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;IAExE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEzD,OAAO;QACL,GAAG;QACH,IAAI,EAAE,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC;QAC3B,IAAI,EAAE,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;QACvC,MAAM;QACN,MAAM;QACN,UAAU;QACV,WAAW;QACX,QAAQ,EAAE,OAAO,CAAC,IAAI;QACtB,QAAQ;QACR,UAAU;QACV,IAAI;KACL,CAAC;AACJ,CAAC;AAED,+BAA+B;AAC/B,SAAS,iBAAiB,CAAC,GAAQ;IACjC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtE,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;IAEtD,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE1D,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;IACzC,IAAI,QAA4B,CAAC;IACjC,IAAI,QAAQ,GAAuB,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACtF,IAAI,UAAU,IAAI,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtD,IAAI,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3D,IAAI,CAAC;gBAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QACrG,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,IAAI,CAAC,QAAQ;QAAE,QAAQ,GAAG,WAAW,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IAEpF,MAAM,UAAU,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEzD,OAAO;QACL,GAAG;QACH,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC;QACnB,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;QAC/B,MAAM;QACN,MAAM;QACN,UAAU;QACV,WAAW;QACX,QAAQ;QACR,QAAQ;QACR,UAAU;QACV,IAAI;KACL,CAAC;AACJ,CAAC;AAED,8DAA8D;AAC9D,MAAM,OAAO;IACX,QAAQ,GAAG,GAAG,CAAC;IACf,IAAI,GAAG,EAAE,CAAC;IACV,YAAY,GAAW;QACrB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;YAC3B,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,GAAG,iCAAiC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtD,IAAI,CAAC,EAAE,CAAC;gBAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;YAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AAC/B,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAEjC,iBAAiB;IACjB,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrD,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO;qBAC5B,GAAG,CAAC,iBAAiB,CAAC;qBACtB,MAAM,CAAC,CAAC,CAAkC,EAA4B,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjF,OAAO,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;YACD,gEAAgE;YAChE,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,GAAG;gBAAE,OAAO,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,GAAG;qBAChB,GAAG,CAAC,mBAAmB,CAAC;qBACxB,MAAM,CAAC,CAAC,CAAC,EAA4B,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,OAAO,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,MAAM,WAAW,GAA0B,EAAE,CAAC;IAC9C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,SAAS;QACjD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,IAAI,EAAE,CAAC;gBAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAAC,SAAS,GAAG,IAAI,CAAC;YAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IACD,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QACtE,OAAO,QAAQ,CAAC,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,IAAI,KAAK,CACb,+GAA+G;QAC/G,yCAAyC,CAC1C,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,OAA8B,EAAE,MAAuB,EAAE,KAAe;IACxF,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IACnF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC7C,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,SAAS,CAAC,KAA0B;IAClD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,uBAAuB;IAC7D,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC9C,0DAA0D;QAC1D,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The correlation engine.
|
|
3
|
+
*
|
|
4
|
+
* Given any combination of three evidence sources —
|
|
5
|
+
* 1. the device error (Video node errorCode/errorInfo), and/or
|
|
6
|
+
* 2. the parsed manifest (codecs/container/DRM/segments), and/or
|
|
7
|
+
* 3. the HTTP traffic from a Charles/HAR session —
|
|
8
|
+
* produce ranked, plain-English root-cause findings, each with the evidence it
|
|
9
|
+
* relied on, a confidence level, a suggested fix, and a doc link.
|
|
10
|
+
*
|
|
11
|
+
* Confidence rises when sources agree. For example, `errorCode -6` (DRM)
|
|
12
|
+
* corroborated by a license POST returning 403 in the Charles session is a
|
|
13
|
+
* high-confidence diagnosis; a manifest-only hint ("declares Opus audio") is
|
|
14
|
+
* low confidence because the device might still play it.
|
|
15
|
+
*/
|
|
16
|
+
import type { NormalizedRokuError } from './error-info.js';
|
|
17
|
+
import type { ManifestSummary } from './manifest.js';
|
|
18
|
+
import type { CharlesSession } from './charles.js';
|
|
19
|
+
export type Confidence = 'high' | 'medium' | 'low';
|
|
20
|
+
/**
|
|
21
|
+
* How badly the issue breaks playback:
|
|
22
|
+
* - 'blocking' : the stream will not play at all (decoder/network/DRM/empty).
|
|
23
|
+
* - 'degraded' : the stream plays but is impaired (e.g. silent audio).
|
|
24
|
+
* - 'advisory' : a hint that may or may not matter (segment length, signaling).
|
|
25
|
+
*/
|
|
26
|
+
export type Severity = 'blocking' | 'degraded' | 'advisory';
|
|
27
|
+
export interface Finding {
|
|
28
|
+
id: string;
|
|
29
|
+
cause: string;
|
|
30
|
+
/** Verbatim evidence strings that support this finding. */
|
|
31
|
+
evidence: string[];
|
|
32
|
+
confidence: Confidence;
|
|
33
|
+
/** Playback impact, used to rank a won't-play issue above a plays-but-silent one. */
|
|
34
|
+
severity: Severity;
|
|
35
|
+
fix: string;
|
|
36
|
+
docUrl: string;
|
|
37
|
+
}
|
|
38
|
+
export interface DiagnosisInput {
|
|
39
|
+
error?: NormalizedRokuError;
|
|
40
|
+
manifest?: ManifestSummary;
|
|
41
|
+
charles?: CharlesSession;
|
|
42
|
+
/** DRM hint the developer told us they configured. */
|
|
43
|
+
declaredDrm?: {
|
|
44
|
+
keySystem?: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface Diagnosis {
|
|
48
|
+
verdict: string;
|
|
49
|
+
findings: Finding[];
|
|
50
|
+
specVersion: string;
|
|
51
|
+
specDisclaimer: string;
|
|
52
|
+
}
|
|
53
|
+
export declare function diagnose(input: DiagnosisInput): Diagnosis;
|
|
54
|
+
//# sourceMappingURL=diagnose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/stream/diagnose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,cAAc,CAAC;AA6CxE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAE5D,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,qFAAqF;IACrF,QAAQ,EAAE,QAAQ,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,sDAAsD;IACtD,WAAW,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAmgBD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAwBzD"}
|