@caplin/streamlink-devtools
Version:
StreamLink wrapper for Caplin Developer Tools
1,611 lines (1,422 loc) • 52.1 kB
JavaScript
/* eslint-disable */
// @ts-nocheck
let __caplin_devtools_show_gui_in_browser_tab__ = false;
let __caplin_devtools_webworker__ = undefined;
export function withDevTools(streamlink, webWorkerUrl = undefined) {
if (webWorkerUrl && !__caplin_devtools_webworker__) {
__caplin_devtools_show_gui_in_browser_tab__ = true;
// start content script in webworker
if (window.Worker) {
try {
__caplin_devtools_webworker__ = new Worker(webWorkerUrl);
console.log("Caplin dev tools script loaded in web worker");
} catch (e) {
console.error("Error starting Caplin dev tools script in web worker", e);
return undefined;
}
} else {
console.error("Web workers not supported, unable to start Caplin dev tools script");
return undefined;
}
}
const streamLinkInterceptor = new StreamLinkInterceptor(streamlink);
const proxy = intercept(streamlink, streamLinkInterceptor);
streamLinkInterceptor.proxyStreamLink = proxy;
return proxy;
}
class BaseInterceptor {
__other__(method) {
this.__post__({
method: method.name,
params: method.stringArgs,
});
}
}
class StreamLinkInterceptor extends BaseInterceptor {
constructor(streamlink) {
super();
this.streamlink = streamlink;
this.streamlinkId = randomUUID();
this.connectionListeners = new Map();
this.apiLineNumber = 1;
this.logLineNumber = 1;
this.toolsConfig = null;
this.userSubscriptions = new Map();
this.proxyStreamLink = null;
this.messageListener = (event) => {
// console.log("window message:", event);
if (__caplin_devtools_show_gui_in_browser_tab__ || (event.source instanceof Window && event.source === window)) {
if (event.data && event.data.source === "__caplin_streamlink_devtools__") {
// console.log("Message from content script:", event.data);
switch (event.data.type) {
case "setToolsConfig":
this.toolsConfig = event.data.toolsConfig;
localStorage.setItem("__caplin_streamlink_devtools__", JSON.stringify(this.toolsConfig));
this.applySubscriptions();
break;
}
}
}
};
const jsonConfig = localStorage.getItem("__caplin_streamlink_devtools__");
let savedToolsConfig = {};
if (jsonConfig) {
try {
savedToolsConfig = JSON.parse(jsonConfig);
} catch (e) {
console.log("Unable to parse saved Devtools JSON config: ", jsonConfig);
}
}
if (__caplin_devtools_show_gui_in_browser_tab__) {
__caplin_devtools_webworker__.onmessage = this.messageListener;
} else {
window.addEventListener('message', this.messageListener);
}
windowPost({type: "toolsConfig", toolsConfig: savedToolsConfig});
try {
streamlink.getLogger().addListener(
{
onLog: (info) => {
windowPost({
type: "log",
lineNumber: this.logLineNumber++,
level: info.getLevel().getValue(),
line: info.toString(),
});
},
},
{getValue: () => 9, getPaddedName: () => "FINEST"}
);
} catch (e) {
console.log(e);
}
}
applySubscriptions() {
const sentSubs = this.toolsConfig.subscriptions;
const sentIds = new Map();
for (const sub of sentSubs) {
sentIds.set(sub.id, null);
const currentSub = this.userSubscriptions.get(sub.id);
if (currentSub) { // existing sub
if (currentSub.subject !== sub.subject || currentSub.enabled !== sub.enabled) { // it's changed
if (currentSub.enabled && currentSub.subscribed) {
currentSub.subscription.unsubscribe();
}
if (sub.enabled) {
currentSub.subject = sub.subject;
currentSub.enabled = sub.enabled;
currentSub.subscription = this.proxyStreamLink.subscribe(sub.subject, currentSub);
currentSub.subscribed = true;
} else {
this.userSubscriptions.delete(sub.id);
}
}
} else { // new sub
if (sub.enabled) {
const newSub = {
id: sub.id, subject: sub.subject, enabled: sub.enabled, subscribed: false, subscription: null,
onSubscriptionError: (subscription, evt) => {
newSub.subscribed = false;
},
onSubscriptionStatus: (subscription, evt) => {
},
onStoryUpdate: function (subscription, event) {
},
onJsonUpdate: function (subscription, event) {
},
onRecordUpdate: function (subscription, event) {
},
onRecordType2Update: function (subscription, event) {
},
onRecordType3Update: function (subscription, event) {
},
onPermissionUpdate: function (subscription, event) {
},
onNewsUpdate: function (subscription, event) {
},
onDirectoryUpdate: function (subscription, event) {
},
onContainerUpdate: function (subscription, event) {
},
onChatUpdate: function (subscription, event) {
},
onPageUpdate: function (subscription, event) {
}
};
this.userSubscriptions.set(newSub.id, newSub);
newSub.subscription = this.proxyStreamLink.subscribe(newSub.subject, newSub);
newSub.subscribed = true;
}
}
}
// find entries to remove
const toRemove = [];
for (const [id, sub] of this.userSubscriptions) {
if (!sentIds.has(id)) {
toRemove.push(id);
}
}
// remove and unsubscribe
for (const id of toRemove) {
const sub = this.userSubscriptions.get(id);
if (sub.enabled && sub.subscribed) {
sub.subscription.unsubscribe();
}
this.userSubscriptions.delete(id);
}
}
applyLogging(json) {
if (json.method && json.params && json.params.subject && json.type) {
if (this.toolsConfig && this.toolsConfig.logMessages && this.toolsConfig.logMessages.some((lm) => {
return lm.enabled && lm.subject == json.params.subject
})) {
const prefix = json.timestamp + (json.type === "callback" ? " Incoming: " : " Outgoing: ") + json.params.subject;
const image = json.params.image ? json.params.image : false;
if (json.params.fields) {
console.log(prefix, json.params.fields, "image: " + image);
} else if (json.params.json) {
console.log(prefix, json.params.json, "image: " + image);
} else {
console.log(prefix, json.method, json.params);
}
}
}
}
__basePost__(json) {
json.timestamp = formatDateStamp(new Date());
this.applyLogging(json);
json.streamlinkId = this.streamlinkId;
json.lineNumber = this.apiLineNumber++;
windowPost(json);
}
__post__(json) {
json.type = "call";
json.classType = "StreamLink";
json.caller = callingFunction(5);
this.__basePost__(json);
}
connect(method) {
this.__post__({method: method.name});
}
disconnect(method) {
this.__post__({method: method.name});
window.removeEventListener('message', this.messageListener);
}
addConnectionListener(method, connectionListener) {
const subscriptionId = randomUUID();
const wrappedListener = intercept(
connectionListener,
new ConnectionListenerInterceptor(subscriptionId, this)
);
this.connectionListeners.set(connectionListener, wrappedListener);
this.__post__({
method: method.name,
subscriptionId,
});
method.args[0] = wrappedListener;
method.apply(method.args);
}
removeConnectionListener(method, connectionListener) {
const wrappedListener = this.connectionListeners.get(connectionListener);
if (wrappedListener) {
this.connectionListeners.delete(connectionListener);
method.args[0] = wrappedListener;
method.apply(method.args);
}
this.__post__({method: method.name});
}
subscribe(method, subject, subscriptionListener, subscriptionParameters) {
const subscriptionId = randomUUID();
method.args[1] = intercept(
subscriptionListener,
new SubscriptionListenerInterceptor(subscriptionId, this)
);
const subscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, subscriptionParameters},
});
return intercept(
subscription,
new SubscriptionInterceptor(subscriptionId, subject, this)
);
}
snapshot(method, subject, subscriptionListener, subscriptionParameters) {
const subscriptionId = randomUUID();
method.args[1] = intercept(
subscriptionListener,
new SubscriptionListenerInterceptor(subscriptionId, this)
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, subscriptionParameters},
});
}
createSubject(
method,
subject,
subjectType,
commandListener,
commandParameters
) {
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {
subject,
subjectType: subjectType.toString(),
commandParameters,
},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
deleteSubject(method, subject, commandListener) {
const subscriptionId = randomUUID();
method.args[1] = intercept(
commandListener,
new CommandListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({method: method.name, subscriptionId, params: {subject}});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
publishToSubject(
method,
subject,
fieldData,
commandListener,
commandParameters
) {
if (!Array.isArray(fieldData)) { // not an array, just a map of fieldname:fieldvalue
this.applyToolsIntercept("output", subject, fieldData);
} else { // an array of maps
this.applyToolsInterceptForArray("output", subject, fieldData);
}
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, fieldData, commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
publishToJsonSubject(
method,
subject,
jsonObject,
commandListener,
commandParameters
) {
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, jsonObject, commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
create(method, subject, commandParameters, commandListener) {
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandResultListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
delete(method, subject, commandParameters, commandListener) {
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandResultListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
publish(method, subject, commandParameters, commandListener) {
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandResultListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
throttleSubject(
method,
subject,
throttleCommand,
commandListener,
commandParameters
) {
const subscriptionId = randomUUID();
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
throttleEverything(
method,
throttleCommand,
commandListener,
commandParameters
) {
const subscriptionId = randomUUID();
method.args[1] = intercept(
commandListener,
new CommandListenerInterceptor(subscriptionId, this)
);
const commandSubscription = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {commandParameters},
});
return intercept(
commandSubscription,
new CommandSubscriptionInterceptor(subscriptionId, this)
);
}
getLogger(method) {
this.__post__({method: method.name});
}
getVersion(method) {
// this.__post__({method: "getVersion"});
}
networkAvailable(method) {
this.__post__({method: method.name});
}
networkUnavailable(method) {
this.__post__({method: method.name});
}
pause(method) {
this.__post__({method: method.name});
}
resume(method) {
this.__post__({method: method.name});
}
createWebRequestParameters(method, moduleName, options) {
this.__post__({
method: method.name,
params: {moduleName, options},
});
}
getLastLog(method) {
this.__post__({method: method.name});
}
createChannel(method, subject, channelListener, commandParameters) {
const subscriptionId = randomUUID();
method.args[1] = intercept(
channelListener,
new ChannelListenerInterceptor(subscriptionId, this)
);
const channel = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, commandParameters},
});
return intercept(channel, new ChannelInterceptor(subject, subscriptionId, this));
}
createJsonChannel(method, subject, channelListener, commandParameters) {
const subscriptionId = randomUUID();
method.args[1] = intercept(
channelListener,
new JsonChannelListenerInterceptor(subscriptionId, this)
);
const channel = method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId,
params: {subject, commandParameters},
});
return intercept(channel, new JsonChannelInterceptor(subscriptionId, this));
}
getConnectionCurrentState(method) {
this.__post__({
method: method.name,
});
}
registerRecordProvider(method, pattern, provider, commandListener) {
const providerId = randomUUID();
method.args[1] = intercept(
provider,
new RecordProviderInterceptor(providerId, this)
);
if (commandListener) {
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(providerId, this)
);
}
const providerHandle = method.apply(method.args);
this.__post__({
method: method.name,
providerId,
params: {pattern},
});
return providerHandle;
}
registerJsonProvider(method, pattern, provider, commandListener) {
const providerId = randomUUID();
method.args[1] = intercept(
provider,
new JsonProviderInterceptor(providerId, this)
);
if (commandListener) {
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(providerId, this)
);
}
const providerHandle = method.apply(method.args);
this.__post__({
method: method.name,
providerId,
params: {pattern},
});
return providerHandle;
}
applyToolsIntercept(type, subject, fieldsMap) {
let logMessage = "";
if (this.toolsConfig && this.toolsConfig.intercepts) {
for (const intercept of this.toolsConfig.intercepts) {
if (intercept.enabled && intercept.type === type &&
((intercept.isRegEx === false && subject === intercept.subject) ||
(intercept.isRegEx === true && subject.match(new RegExp(intercept.subject))))) {
let allowIntercept = true;
if (intercept.where) {
for (const field of intercept.where) {
if (!fieldsMap[field.name]) {
allowIntercept = false;
break;
}
if (fieldsMap[field.name] !== field.value) {
allowIntercept = false;
break;
}
}
}
if (allowIntercept) {
for (const field of intercept.fields) {
logMessage += field.name + ": " + fieldsMap[field.name] + " -> " + field.value + ", ";
fieldsMap[field.name] = field.value;
}
}
}
}
}
if (logMessage !== "") {
this.__post__({
method: "--Message Intercept--",
params: {subject: subject, changes: logMessage},
})
}
}
applyToolsInterceptForArray(type, subject, fieldsArray) {
let logMessage = "";
if (this.toolsConfig && this.toolsConfig.intercepts) {
for (const intercept of this.toolsConfig.intercepts) {
if (intercept.enabled && intercept.type === type &&
((intercept.isRegEx === false && subject === intercept.subject) ||
(intercept.isRegEx === true && subject.match(new RegExp(intercept.subject))))) {
let allowIntercept = true;
if (intercept.where) {
for (const field of intercept.where) {
const found = fieldsArray.find((m) => m[field.name] !== undefined);
if (!found) {
allowIntercept = false;
break;
}
if (found[field.name] !== field.value) {
allowIntercept = false;
break;
}
}
}
if (allowIntercept) {
for (const field of intercept.fields) {
const found = fieldsArray.find((m) => m[field.name] !== undefined)
logMessage += field.name + ": " + found[field.name] + " -> " + field.value + ", ";
found[field.name] = field.value;
}
}
}
}
}
if (logMessage !== "") {
this.__post__({
method: "--Message Intercept--",
params: {subject: subject, changes: logMessage},
})
}
}
}
// ===================================================
class SubscriptionInterceptor extends BaseInterceptor {
constructor(subscriptionId, subject, streamlinkInterceptor) {
super();
this.subscriptionId = subscriptionId;
this.subject = subject;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "call";
json.classType = "Subscription";
json.caller = callingFunction(5);
this.streamlinkInterceptor.__basePost__(json);
}
getSubject(method) {
}
getSubscriptionListener(method) {
}
unsubscribe(method) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {subject: this.subject},
});
}
setContainerWindow(method, start, size) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {start, size},
});
}
}
class SubscriptionListenerInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.streamlinkInterceptor = streamlinkInterceptor;
this.subscriptionId = subscriptionId;
}
__post__(json) {
json.type = "callback";
json.classType = "SubscriptionListener";
this.streamlinkInterceptor.__basePost__(json);
}
onStoryUpdate(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
image: evt.isImage(),
},
});
}
onDirectoryUpdate(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
image: evt.isImage(),
},
});
}
onNewsUpdate(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
image: evt.isImage(),
},
});
}
onPageUpdate(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
image: evt.isImage(),
},
});
}
onChatUpdate(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
image: evt.isImage(),
},
});
}
onRecordUpdate(method, subscription, evt) {
this.streamlinkInterceptor.applyToolsIntercept("input", evt.getSubject(), evt.getFields());
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
fields: sortFields(evt.getFields()),
image: evt.isImage(),
},
});
}
onRecordType2Update(method, subscription, evt) {
this.streamlinkInterceptor.applyToolsIntercept("input", evt.getSubject(), evt.getFields());
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
fields: sortFields(evt.getFields()),
image: evt.isImage(),
indexField: evt.getType2IndexField(),
deleteRow: evt.deleteRow(),
deleteAllRows: evt.deleteAllRows(),
},
});
}
onRecordType3Update(method, subscription, evt) {
this.streamlinkInterceptor.applyToolsIntercept("input", evt.getSubject(), evt.getFields());
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
fields: sortFields(evt.getFields()),
image: evt.isImage(),
deleteAllEntries: evt.deleteAllEntries(),
},
});
}
onPermissionUpdate(method, subscription, evt) {
this.streamlinkInterceptor.applyToolsIntercept("input", evt.getSubject(), evt.getFields());
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
fields: sortFields(evt.getFields()),
image: evt.isImage(),
indexField: evt.getKey(),
deleteRow: evt.deleteKey(),
deleteAllRows: evt.deleteAllKeys(),
},
});
}
onJsonUpdate(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
json: evt.getJson(),
jsonString: evt.getJsonString(),
image: evt.isImage(),
},
});
}
onContainerUpdate(method, subscription, evt) {
const ops = [];
evt.updateModel({
clear: () => {
ops.push({op: "clear"});
},
insert: (index, element) => {
ops.push({op: "insert", index: index, subject: element.getSubject()});
},
remove: (index, element) => {
ops.push({op: "remove", index: index});
},
move: (from, to, element) => {
ops.push({op: "move", from, to});
},
});
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
size: evt.getSize(),
windowStart: evt.getWindowStart(),
windowEnd: evt.getWindowEnd(),
ops: ops,
image: evt.isImage(),
},
});
}
onSubscriptionStatus(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
status: evt.getStatus().toString(),
message: evt.getStatusMessage(),
},
});
}
onSubscriptionError(method, subscription, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
error: evt.getError().toString(),
errorReason: evt.getErrorReason(),
},
});
}
}
class ChannelInterceptor extends BaseInterceptor {
constructor(subject, subscriptionId, streamlinkInterceptor) {
super();
this.subject = subject;
this.subscriptionId = subscriptionId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "call";
json.classType = "Channel";
json.caller = callingFunction(5);
this.streamlinkInterceptor.__basePost__(json);
}
getSubject(method) {
}
closeChannel(method) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {},
});
}
send(method, fields, commandListener) {
this.streamlinkInterceptor.applyToolsIntercept("output", this.subject, fields);
method.args[1] = intercept(
commandListener,
new CommandListenerInterceptor(this.subscriptionId, this.streamlinkInterceptor)
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {fields},
});
}
sendWithFieldList(method, fieldlist, commandListener) {
method.args[1] = intercept(
commandListener,
new CommandListenerInterceptor(this.subscriptionId, this.streamlinkInterceptor)
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {fieldlist},
});
}
}
class ChannelListenerInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.streamlinkInterceptor = streamlinkInterceptor;
this.subscriptionId = subscriptionId;
}
__post__(json) {
json.type = "callback";
json.classType = "ChannelListener";
this.streamlinkInterceptor.__basePost__(json);
}
onChannelData(method, channel, evt) {
this.streamlinkInterceptor.applyToolsIntercept("input", evt.getSubject(), evt.getFields());
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
image: evt.isImage(),
fields: sortFields(evt.getFields())
},
});
}
onChannelStatus(method, channel, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
status: evt.getStatus().toString(),
message: evt.getStatusMessage(),
},
});
}
onChannelError(method, channel, evt) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: evt.getSubject(),
error: evt.getError().toString(),
errorReason: evt.getErrorReason(),
},
});
}
}
class ConnectionListenerInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.streamlinkInterceptor = streamlinkInterceptor;
this.subscriptionId = subscriptionId;
}
__post__(json) {
json.type = "callback";
json.classType = "ConnectionListener";
this.streamlinkInterceptor.__basePost__(json);
}
onConnectionStatusChange(method, connectionStatusEvent) {
const params = {
connectionState: connectionStatusEvent.getConnectionState().toString(),
liberatorUrl: connectionStatusEvent.getLiberatorUrl(),
};
if (connectionStatusEvent.getUsername() !== null) {
params.userName = connectionStatusEvent.getUsername();
}
if (connectionStatusEvent.getLoginFailReason() !== "UNDEFINED") {
params.loginFailedReason = connectionStatusEvent.getLoginFailReason();
}
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params,
});
}
onSourceStatusChange(method, sourceStatusEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
sourceName: sourceStatusEvent.getSourceName(),
sourceStatus: sourceStatusEvent.getSourceStatus().toString(),
},
});
}
onServiceStatusChange(method, serviceStatusEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
serviceName: serviceStatusEvent.getServiceName(),
serviceStatus: serviceStatusEvent.getServiceStatus().toString(),
},
});
}
onStatisticsChange(method, statisticsEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
latency: statisticsEvent.getLatency(),
averageLatency: statisticsEvent.getAverageLatency(),
clockOffset: statisticsEvent.getClockOffset(),
},
});
}
}
class JsonChannelInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.subscriptionId = subscriptionId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "call";
json.classType = "JsonChannel";
json.caller = callingFunction(5);
this.streamlinkInterceptor.__basePost__(json);
}
getSubject(method) {
}
closeChannel(method) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
});
}
send(method, json, commandListener) {
method.args[1] = intercept(
commandListener,
new CommandListenerInterceptor(this.subscriptionId, this.streamlinkInterceptor)
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
json: json,
},
});
}
}
class JsonChannelListenerInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.subscriptionId = subscriptionId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "callback";
json.classType = "JsonChannelListener";
this.streamlinkInterceptor.__basePost__(json);
}
onChannelData(method, channel, jsonEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: jsonEvent.getSubject(),
json: jsonEvent.getJson(),
jsonString: jsonEvent.getJsonString(),
},
});
}
onChannelStatus(method, channel, subscriptionStatusEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: subscriptionStatusEvent.getSubject(),
status: subscriptionStatusEvent.getStatus().toString(),
message: subscriptionStatusEvent.getStatusMessage(),
},
});
}
onChannelError(method, channel, subscriptionErrorEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: subscriptionErrorEvent.getSubject(),
error: subscriptionErrorEvent.getError().toString(),
errorReason: subscriptionErrorEvent.getErrorReason(),
},
});
}
}
class CommandSubscriptionInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.subscriptionId = subscriptionId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "call";
json.classType = "CommandSubscription";
json.caller = callingFunction(5);
this.streamlinkInterceptor.__basePost__(json);
}
getSubject(method) {
}
getCommandListener(method) {
}
unPersist(method) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
});
}
}
class CommandListenerInterceptor extends BaseInterceptor {
constructor(subscriptionId, streamlinkInterceptor) {
super();
this.subscriptionId = subscriptionId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "callback";
json.classType = "CommandListener";
this.streamlinkInterceptor.__basePost__(json);
}
onCommandError(method, subject, commandErrorEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: subject,
error: commandErrorEvent.getError().toString(),
reason: commandErrorEvent.getErrorReason().toString(),
},
});
}
onCommandOk(method, subject) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: subject,
},
});
}
}
class CommandResultListenerInterceptor extends CommandListenerInterceptor {
__post__(json) {
json.type = "callback";
json.classType = "CommandResultListener";
this.streamlinkInterceptor.__basePost__(json);
}
onCommandResult(method, subject, commandEvent) {
this.__post__({
method: method.name,
subscriptionId: this.subscriptionId,
params: {
subject: subject,
payload: commandEvent.getPayload(),
},
});
}
}
// ===================================================
class RecordProviderInterceptor extends BaseInterceptor {
constructor(providerId, streamlinkInterceptor) {
super();
this.providerId = providerId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "callback";
json.classType = "RecordProvider";
this.streamlinkInterceptor.__basePost__(json);
}
onRequest(method, publisher) {
method.args[0] = intercept(
publisher,
new RecordPublisherInterceptor(this.providerId, this.streamlinkInterceptor, publisher.getSubject())
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: publisher.getSubject()
},
});
}
}
// ===================================================
class RecordPublisherInterceptor extends BaseInterceptor {
constructor(providerId, streamlinkInterceptor, subject) {
super();
this.providerId = providerId;
this.streamlinkInterceptor = streamlinkInterceptor;
this.subject = subject;
}
__post__(json) {
json.type = "call";
json.classType = "RecordPublisher";
this.streamlinkInterceptor.__basePost__(json);
}
send(method, fields, image, commandListener) {
this.streamlinkInterceptor.applyToolsIntercept("output", this.subject, fields);
if (commandListener) {
method.args[2] = intercept(
commandListener,
new CommandListenerInterceptor(this.providerId, this.streamlinkInterceptor)
);
}
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: this.subject,
fields: fields,
image: image
},
});
}
setDiscardHandler(method, discardHandler) {
method.args[0] = intercept(
discardHandler,
new RecordDiscardHandlerInterceptor(this.providerId, this.streamlinkInterceptor, this.subject)
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: this.subject
},
});
}
}
// ===================================================
class RecordDiscardHandlerInterceptor extends BaseInterceptor {
constructor(providerId, streamlinkInterceptor, subject) {
super();
this.providerId = providerId;
this.streamlinkInterceptor = streamlinkInterceptor;
this.subject = subject;
}
__post__(json) {
json.type = "callback";
json.classType = "RecordDiscardHandler";
this.streamlinkInterceptor.__basePost__(json);
}
onDiscard(method, publisher) {
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: this.subject
},
});
}
}
// ===================================================
class JsonProviderInterceptor extends BaseInterceptor {
constructor(providerId, streamlinkInterceptor) {
super();
this.providerId = providerId;
this.streamlinkInterceptor = streamlinkInterceptor;
}
__post__(json) {
json.type = "callback";
json.classType = "JsonProvider";
this.streamlinkInterceptor.__basePost__(json);
}
onRequest(method, publisher) {
method.args[0] = intercept(
publisher,
new JsonPublisherInterceptor(this.providerId, this.streamlinkInterceptor, publisher.getSubject())
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: publisher.getSubject()
},
});
}
}
// ===================================================
class JsonPublisherInterceptor extends BaseInterceptor {
constructor(providerId, streamlinkInterceptor, subject) {
super();
this.providerId = providerId;
this.streamlinkInterceptor = streamlinkInterceptor;
this.subject = subject;
}
__post__(json) {
json.type = "call";
json.classType = "JsonPublisher";
this.streamlinkInterceptor.__basePost__(json);
}
send(method, jsonObject, commandListener) {
if (commandListener) {
method.args[1] = intercept(
commandListener,
new CommandListenerInterceptor(this.providerId, this.streamlinkInterceptor)
);
}
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: this.subject,
jsonObject: jsonObject,
},
});
}
setDiscardHandler(method, discardHandler) {
method.args[0] = intercept(
discardHandler,
new JsonDiscardHandlerInterceptor(this.providerId, this.streamlinkInterceptor, this.subject)
);
method.apply(method.args);
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: this.subject
},
});
}
}
// ===================================================
class JsonDiscardHandlerInterceptor extends BaseInterceptor {
constructor(providerId, streamlinkInterceptor, subject) {
super();
this.providerId = providerId;
this.streamlinkInterceptor = streamlinkInterceptor;
this.subject = subject;
}
__post__(json) {
json.type = "callback";
json.classType = "JsonDiscardHandler";
this.streamlinkInterceptor.__basePost__(json);
}
onDiscard(method, publisher) {
this.__post__({
method: method.name,
subscriptionId: this.providerId,
params: {
subject: this.subject
},
});
}
}
// ===================================================
function formatDateStamp(dt) {
var timeZoneOffset = dt.getTimezoneOffset(),
timezoneOffsetAbsolute = Math.abs(timeZoneOffset),
offsetSign = timeZoneOffset <= 0 ? "+" : "-",
offsetHours = padZeros(
Math.floor(Math.abs(timezoneOffsetAbsolute) / 60),
2
),
offsetMinutes = padZeros(timezoneOffsetAbsolute % 60, 2),
paddedMonth = padZeros(dt.getMonth() + 1, 2),
paddedDay = padZeros(dt.getDate(), 2),
paddedHours = padZeros(dt.getHours(), 2),
paddedMinutes = padZeros(dt.getMinutes(), 2),
paddedSeconds = padZeros(dt.getSeconds(), 2),
paddedMillis = padZeros(dt.getMilliseconds(), 3);
return (
dt.getFullYear() +
"/" +
paddedMonth +
"/" +
paddedDay +
"-" +
paddedHours +
":" +
paddedMinutes +
":" +
paddedSeconds +
"." +
paddedMillis +
" " +
offsetSign +
offsetHours +
offsetMinutes
);
}
const zeroPadding = ["", "0", "00", "000"];
function padZeros(val, paddedLength) {
var valLen = (val + "").length,
diff = paddedLength - valLen;
return zeroPadding[diff] + val;
}
function sortFields(fields) {
const names = Object.keys(fields);
names.sort();
const map = {};
for (const name of names) {
map[name] = fields[name];
}
return map;
}
function windowPost(json) {
json.source = "__caplin_streamlink__";
// console.log("post " + JSON.stringify(json));
try {
if (__caplin_devtools_show_gui_in_browser_tab__) {
__caplin_devtools_webworker__.postMessage(json);
} else {
window.postMessage(json, "*");
}
} catch (e) {
console.log(e);
}
}
function callingFunction(level) {
let caller = null;
try {
throw new Error();
} catch (e) {
// Inspect the call stack
try {
const stack = e.stack.split("\n");
const callerLine = stack[level]; // Line representing the calling function
caller = callerLine.match(/at\s+(.*)\s+\(/)[1];
} catch (e2) {
}
}
return caller;
}
function randomUUID() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
);
}
function intercept(obj, interceptor) {
try {
return new Proxy(obj, {
get(target, prop, receiver) {
if (typeof