Capturing pack and cover
This commit is contained in:
@@ -3,10 +3,7 @@
|
||||
const TARGET_SELECTOR = ".playbar-playback-info-bar.playback-row-section.svelte-11gjrk3";
|
||||
const INJECTED_CONTAINER_ID = "s3-audio-capture-controls";
|
||||
const SPLICE_DOMAIN_RE = /^https?:\/\/([a-z0-9-]+\.)*splice\.com(\/|$)/i;
|
||||
const GRAPHQL_URL_FRAGMENT = "surfaces-graphql.splice.com/graphql";
|
||||
const PROBE_EVENT_SOURCE = "S3_CAPTURE_PROBE";
|
||||
const DEBUG_CAPTURE = true;
|
||||
const DEBUG_PLAYBACK_META_MARKER = "S3DBG_PLAYBAR_META_V2";
|
||||
let knownUrls = new Set();
|
||||
let knownPerformanceUrls = new Set();
|
||||
let observer = null;
|
||||
@@ -178,15 +175,20 @@ function extractCoverImageFromGraphqlItem(item) {
|
||||
return "";
|
||||
}
|
||||
|
||||
function logDebug(message, extra) {
|
||||
if (!DEBUG_CAPTURE) {
|
||||
return;
|
||||
function extractPackNameFromGraphqlItem(item) {
|
||||
if (!item || typeof item !== "object" || !item.parents || !Array.isArray(item.parents.items)) {
|
||||
return "";
|
||||
}
|
||||
if (typeof extra === "undefined") {
|
||||
console.debug("[S3 capture]", message);
|
||||
return;
|
||||
for (const parent of item.parents.items) {
|
||||
if (!parent || typeof parent !== "object") {
|
||||
continue;
|
||||
}
|
||||
const name = typeof parent.name === "string" ? parent.name.trim() : "";
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
console.debug("[S3 capture]", message, extra);
|
||||
return "";
|
||||
}
|
||||
|
||||
function collectProbeCandidateUrls(payload) {
|
||||
@@ -272,6 +274,7 @@ function extractPreviewContextsFromGraphql(json) {
|
||||
? Array.from(new Set(item.tags.map((tag) => (tag && tag.label ? String(tag.label).trim() : "")).filter(Boolean)))
|
||||
: [];
|
||||
const itemCoverImageUrl = extractCoverImageFromGraphqlItem(item);
|
||||
const itemPackName = extractPackNameFromGraphqlItem(item);
|
||||
const files = Array.isArray(item.files) ? item.files : [];
|
||||
|
||||
for (const file of files) {
|
||||
@@ -290,7 +293,8 @@ function extractPreviewContextsFromGraphql(json) {
|
||||
key: itemKey,
|
||||
bpm: itemBpm,
|
||||
tags: itemTags,
|
||||
coverImageUrl: itemCoverImageUrl
|
||||
coverImageUrl: itemCoverImageUrl,
|
||||
packName: itemPackName
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -308,30 +312,6 @@ function sendPreviewContexts(contexts) {
|
||||
}).catch(() => {
|
||||
// Ignore transient extension messaging failures.
|
||||
});
|
||||
logDebug("Captured preview contexts", contexts);
|
||||
}
|
||||
|
||||
function inspectGraphqlResponse(candidateUrl, response) {
|
||||
if (typeof candidateUrl !== "string" || !candidateUrl.includes(GRAPHQL_URL_FRAGMENT)) {
|
||||
return;
|
||||
}
|
||||
if (!response || typeof response.clone !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
response.clone().json().then((json) => {
|
||||
const contexts = extractPreviewContextsFromGraphql(json);
|
||||
if (contexts.length) {
|
||||
sendPreviewContexts(contexts);
|
||||
}
|
||||
const urls = new Set();
|
||||
recursivelyCollectAudioUrls(json, urls);
|
||||
if (urls.size) {
|
||||
captureUrls(Array.from(urls));
|
||||
}
|
||||
}).catch(() => {
|
||||
// Some GraphQL responses may not be parseable JSON.
|
||||
});
|
||||
}
|
||||
|
||||
function captureUrls(inputUrls) {
|
||||
@@ -361,7 +341,6 @@ function captureUrls(inputUrls) {
|
||||
}).catch(() => {
|
||||
// Ignore transient extension context failures.
|
||||
});
|
||||
logDebug("Captured S3 URLs", newUrls);
|
||||
}
|
||||
|
||||
function scanNodeForUrls(root) {
|
||||
@@ -408,12 +387,7 @@ function patchFetch() {
|
||||
if (init && typeof init === "object" && typeof init.url === "string") {
|
||||
captureUrls([init.url]);
|
||||
}
|
||||
return originalFetch(input, init).then((response) => {
|
||||
if (candidate) {
|
||||
inspectGraphqlResponse(candidate, response);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
return originalFetch(input, init);
|
||||
};
|
||||
fetchPatched = true;
|
||||
} catch (error) {
|
||||
@@ -429,6 +403,7 @@ function patchXhr() {
|
||||
try {
|
||||
const originalOpen = window.XMLHttpRequest.prototype.open;
|
||||
window.XMLHttpRequest.prototype.open = function patchedOpen(method, url) {
|
||||
this.__s3CaptureRequestUrl = typeof url === "string" ? url : "";
|
||||
if (typeof url === "string") {
|
||||
captureUrls([url]);
|
||||
}
|
||||
@@ -708,15 +683,6 @@ function syncPlaybackMeta() {
|
||||
tags: currentPlaybackTags,
|
||||
coverImageUrl: currentPlaybackCoverImageUrl
|
||||
};
|
||||
if (DEBUG_CAPTURE) {
|
||||
console.debug(DEBUG_PLAYBACK_META_MARKER, {
|
||||
fileName: payload.fileName,
|
||||
key: payload.key,
|
||||
bpm: payload.bpm,
|
||||
tagsCount: Array.isArray(payload.tags) ? payload.tags.length : 0,
|
||||
coverImageUrl: payload.coverImageUrl
|
||||
});
|
||||
}
|
||||
const signature = JSON.stringify(payload);
|
||||
lastPlaybackMetaSignature = signature;
|
||||
lastPlaybackMetaApplied = false;
|
||||
@@ -745,11 +711,17 @@ function installPageProbeListener() {
|
||||
|
||||
const kind = typeof data.kind === "string" ? data.kind : "unknown";
|
||||
const payload = data.payload && typeof data.payload === "object" ? data.payload : {};
|
||||
if (kind === "graphql-contexts") {
|
||||
const contexts = Array.isArray(payload.contexts) ? payload.contexts : [];
|
||||
if (contexts.length) {
|
||||
sendPreviewContexts(contexts);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const candidateUrls = collectProbeCandidateUrls(payload);
|
||||
if (candidateUrls.length) {
|
||||
captureUrls(candidateUrls);
|
||||
}
|
||||
logDebug("Probe event: " + kind, payload);
|
||||
});
|
||||
|
||||
probeListenerInstalled = true;
|
||||
@@ -771,10 +743,6 @@ function injectPageProbeScript() {
|
||||
script.async = false;
|
||||
script.onload = () => {
|
||||
script.remove();
|
||||
logDebug("Injected page probe.");
|
||||
};
|
||||
script.onerror = () => {
|
||||
logDebug("Failed to inject page probe.");
|
||||
};
|
||||
root.appendChild(script);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user