mirror of
https://github.com/hyzendust/Sharepoint-Downloader.git
synced 2026-06-30 20:32:20 +02:00
Refactor background logic and enhance popup interface for SharePoint Video Downloader
This commit is contained in:
209
background.js
209
background.js
@@ -1,75 +1,170 @@
|
||||
console.log('Background script loaded');
|
||||
console.log("Background script loaded");
|
||||
|
||||
// Classify and clean a URL, returning { cleanedUrl, type, label } or null
|
||||
function classifyUrl(url) {
|
||||
try {
|
||||
const u = new URL(url);
|
||||
const path = u.pathname.toLowerCase();
|
||||
const host = u.hostname.toLowerCase();
|
||||
|
||||
// 1. SharePoint videomanifest (DASH / HLS / smooth)
|
||||
if (path.includes("/transform/videomanifest")) {
|
||||
let type = "dash";
|
||||
if (url.includes("format=hls")) type = "hls";
|
||||
return { cleanedUrl: url, type, label: "Video Manifest" };
|
||||
}
|
||||
|
||||
// 2. Media proxy service (mediap.svc.ms) — actual stream chunks/manifests
|
||||
if (host.includes("mediap.svc.ms")) {
|
||||
if (path.endsWith(".m3u8") || url.includes(".m3u8")) {
|
||||
return {
|
||||
cleanedUrl: url.split("?")[0],
|
||||
type: "hls",
|
||||
label: "HLS Manifest (mediap)",
|
||||
};
|
||||
}
|
||||
if (path.endsWith(".mpd") || url.includes(".mpd")) {
|
||||
return {
|
||||
cleanedUrl: url.split("?")[0],
|
||||
type: "dash",
|
||||
label: "DASH Manifest (mediap)",
|
||||
};
|
||||
}
|
||||
// Catch-all for other mediap requests that look like manifests
|
||||
if (path.includes("manifest") || path.includes("Manifest")) {
|
||||
return {
|
||||
cleanedUrl: url,
|
||||
type: "dash",
|
||||
label: "Stream Manifest (mediap)",
|
||||
};
|
||||
}
|
||||
return null; // Ignore individual segment fetches
|
||||
}
|
||||
|
||||
// 3. Standalone .m3u8 / .mpd files
|
||||
if (path.endsWith(".m3u8") || path.match(/\.m3u8($|\?)/)) {
|
||||
return {
|
||||
cleanedUrl: url.split("?")[0],
|
||||
type: "hls",
|
||||
label: "HLS Manifest",
|
||||
};
|
||||
}
|
||||
if (path.endsWith(".mpd") || path.match(/\.mpd($|\?)/)) {
|
||||
return {
|
||||
cleanedUrl: url.split("?")[0],
|
||||
type: "dash",
|
||||
label: "DASH Manifest",
|
||||
};
|
||||
}
|
||||
|
||||
// 4. SharePoint direct-download links
|
||||
if (host.includes("sharepoint.com") && path.includes("download.aspx")) {
|
||||
return { cleanedUrl: url, type: "direct", label: "Direct Download" };
|
||||
}
|
||||
|
||||
// 5. SharePoint OneDrive transcode API segments (mid-playback encrypted segments)
|
||||
// Strip per-segment params so all segments of the same video deduplicate to one entry.
|
||||
if (
|
||||
host.includes("sharepoint.com") &&
|
||||
path.includes("/onedrive.transcode")
|
||||
) {
|
||||
const u2 = new URL(url);
|
||||
// Keep only identity/auth params; drop segment-specific ones
|
||||
const dropParams = new Set([
|
||||
"part",
|
||||
"track",
|
||||
"quality",
|
||||
"segmentTime",
|
||||
"wsd",
|
||||
"ppd",
|
||||
"ppst",
|
||||
"headerOffset",
|
||||
"headerSize",
|
||||
"correlationid",
|
||||
"psi",
|
||||
"ccat",
|
||||
"PlaybackSessionData",
|
||||
]);
|
||||
for (const k of dropParams) u2.searchParams.delete(k);
|
||||
return {
|
||||
cleanedUrl: u2.toString(),
|
||||
type: "direct",
|
||||
label: "SP Transcode (use Current Page tab for yt-dlp)",
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
chrome.webRequest.onBeforeRequest.addListener(
|
||||
(details) => {
|
||||
try {
|
||||
const originalUrl = details.url;
|
||||
if (
|
||||
(originalUrl.includes('/transform/videomanifest') && originalUrl.includes('format=dash')) ||
|
||||
originalUrl.endsWith('.m3u8') ||
|
||||
originalUrl.endsWith('.mpd')
|
||||
) {
|
||||
console.log('Video manifest detected:', originalUrl);
|
||||
const result = classifyUrl(details.url);
|
||||
if (!result) return;
|
||||
|
||||
let cleanedUrl = originalUrl;
|
||||
let type = 'unknown';
|
||||
if (originalUrl.includes('/transform/videomanifest') && originalUrl.includes('format=dash')) {
|
||||
const index = originalUrl.indexOf('format=dash');
|
||||
cleanedUrl = originalUrl.substring(0, index + 'format=dash'.length);
|
||||
type = 'dash';
|
||||
} else if (originalUrl.endsWith('.mpd')) {
|
||||
const index = originalUrl.indexOf('.mpd') + '.mpd'.length;
|
||||
cleanedUrl = originalUrl.substring(0, index);
|
||||
type = 'dash';
|
||||
} else if (originalUrl.endsWith('.m3u8')) {
|
||||
const index = originalUrl.indexOf('.m3u8') + '.m3u8'.length;
|
||||
cleanedUrl = originalUrl.substring(0, index);
|
||||
type = 'hls';
|
||||
const manifestData = {
|
||||
originalUrl: details.url,
|
||||
cleanedUrl: result.cleanedUrl,
|
||||
type: result.type,
|
||||
label: result.label,
|
||||
timestamp: Date.now(),
|
||||
tabId: details.tabId,
|
||||
};
|
||||
|
||||
// Store per-tab list (keep last 20 per tab)
|
||||
const storageKey = `manifests_${details.tabId}`;
|
||||
chrome.storage.local.get(storageKey, (stored) => {
|
||||
const list = stored[storageKey] || [];
|
||||
// Avoid duplicates by cleanedUrl
|
||||
if (!list.some((m) => m.cleanedUrl === manifestData.cleanedUrl)) {
|
||||
list.push(manifestData);
|
||||
if (list.length > 20) list.shift();
|
||||
chrome.storage.local.set({ [storageKey]: list });
|
||||
}
|
||||
});
|
||||
|
||||
const manifestData = {
|
||||
originalUrl: originalUrl,
|
||||
cleanedUrl: cleanedUrl,
|
||||
type: type
|
||||
};
|
||||
chrome.storage.local.set({ lastManifest: manifestData }, (result) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error('Error saving manifest:', chrome.runtime.lastError.message);
|
||||
} else {
|
||||
console.log('Manifest saved:', manifestData);
|
||||
}
|
||||
});
|
||||
// Also keep a global "lastManifest" for backward compat
|
||||
chrome.storage.local.set({ lastManifest: manifestData });
|
||||
|
||||
chrome.runtime.sendMessage({ type: 'manifestDetected', data: manifestData }, (response) => {
|
||||
chrome.runtime.sendMessage(
|
||||
{ type: "manifestDetected", data: manifestData },
|
||||
() => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error('Error sending message:', chrome.runtime.lastError.message);
|
||||
/* popup closed, ignore */
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error in webRequest listener:', error);
|
||||
console.error("Error in webRequest listener:", error);
|
||||
}
|
||||
},
|
||||
{ urls: ["<all_urls>"] },
|
||||
["requestBody"]
|
||||
);
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
console.log('Message received:', message);
|
||||
if (message.type === 'getLastManifest') {
|
||||
try {
|
||||
chrome.storage.local.get('lastManifest', (result) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error('Error getting manifest:', chrome.runtime.lastError.message);
|
||||
sendResponse({ data: null });
|
||||
} else {
|
||||
sendResponse({ data: result.lastManifest || null });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error in message listener:', error);
|
||||
sendResponse({ data: null });
|
||||
}
|
||||
return true; // Keep the channel open for async response
|
||||
if (message.type === "getManifests") {
|
||||
const tabId = message.tabId;
|
||||
const storageKey = `manifests_${tabId}`;
|
||||
chrome.storage.local.get([storageKey, "lastManifest"], (result) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
sendResponse({ manifests: [], lastManifest: null });
|
||||
} else {
|
||||
sendResponse({
|
||||
manifests: result[storageKey] || [],
|
||||
lastManifest: result.lastManifest || null,
|
||||
});
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (message.type === "clearManifests") {
|
||||
const tabId = message.tabId;
|
||||
chrome.storage.local.remove(`manifests_${tabId}`, () => {
|
||||
sendResponse({ ok: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user