mirror of
https://github.com/EmulatorJS/EmulatorJS.git
synced 2026-02-06 11:17:36 +00:00
Enhance EJS_Download to handle non-http(s) URLs and delegate URL-based downloads in EmulatorJS
This commit is contained in:
parent
9408a61c35
commit
2abe5243d0
@ -20,8 +20,72 @@ class EJS_Download {
|
||||
this.EJS = EJS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles downloading non-http(s) URLs (blob:, data:, file:, etc.)
|
||||
* @param {string} url - The non-http(s) URL to fetch
|
||||
* @param {string} type - The type of the file
|
||||
* @param {string} method - The HTTP method (HEAD returns empty, others fetch)
|
||||
* @param {string} responseType - The response type ("arraybuffer" or "text")
|
||||
* @returns {Promise<EJS_CacheItem|null>} - The fetched data as a cache item, or null for HEAD requests
|
||||
*/
|
||||
async handleNonHttpUrl(url, type, method = "GET", responseType = "arraybuffer") {
|
||||
console.log("[EJS Download] Handling non-http(s) URL:", url);
|
||||
|
||||
if (method === "HEAD") {
|
||||
// HEAD requests just return empty for non-http URLs
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
let res = await fetch(url);
|
||||
let data;
|
||||
|
||||
if (responseType === "arraybuffer" || !responseType) {
|
||||
data = await res.arrayBuffer();
|
||||
data = new Uint8Array(data);
|
||||
} else {
|
||||
data = await res.text();
|
||||
// Try to parse as JSON if it looks like JSON
|
||||
try { data = JSON.parse(data) } catch(e) {}
|
||||
}
|
||||
|
||||
// Clean up blob URLs to free memory
|
||||
if (url.startsWith("blob:")) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// Create a cache item for consistency
|
||||
const filename = url.split("/").pop() || "downloaded.bin";
|
||||
const now = Date.now();
|
||||
|
||||
// Ensure data is Uint8Array for file item
|
||||
let fileData;
|
||||
if (data instanceof Uint8Array) {
|
||||
fileData = data;
|
||||
} else if (typeof data === "string") {
|
||||
const encoder = new TextEncoder();
|
||||
fileData = encoder.encode(data);
|
||||
} else if (data instanceof ArrayBuffer) {
|
||||
fileData = new Uint8Array(data);
|
||||
} else {
|
||||
const encoder = new TextEncoder();
|
||||
fileData = encoder.encode(String(data));
|
||||
}
|
||||
|
||||
const files = [new EJS_FileItem(filename, fileData)];
|
||||
const key = this.storageCache ? this.storageCache.generateCacheKey(fileData) : "temp-" + Date.now();
|
||||
|
||||
// Don't cache non-http URLs (they're typically temporary or special)
|
||||
return new EJS_CacheItem(key, files, now, type, responseType, filename, url, null);
|
||||
} catch(e) {
|
||||
console.error("[EJS Download] Failed to fetch non-http URL:", url, e);
|
||||
throw new Error(`Failed to fetch non-http URL: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from the given URL with the specified options.
|
||||
* Automatically detects and handles both http(s) and non-http(s) URLs (blob:, data:, etc.)
|
||||
* @param {string} url - The URL to download the file from.
|
||||
* @param {string} type - The type of the file to download (e.g. "ROM", "CORE", "BIOS", etc).
|
||||
* @param {string} method - The HTTP method to use (default is "GET").
|
||||
@ -43,6 +107,17 @@ class EJS_Download {
|
||||
console.log("[EJS Download] Downloading " + responseType + " file: " + url + cacheActiveText);
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// Check if this is a non-http(s) URL (blob:, data:, file:, etc.)
|
||||
let urlObj;
|
||||
try { urlObj = new URL(url) } catch(e) {};
|
||||
|
||||
if (urlObj && !["http:", "https:"].includes(urlObj.protocol)) {
|
||||
// Handle non-http(s) URLs directly
|
||||
const result = await this.handleNonHttpUrl(url, type, method, responseType);
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the provided storageCache or create a temporary one
|
||||
if (!this.storageCache) {
|
||||
console.warn("No storageCache provided to EJS_Download, downloads will not be cached");
|
||||
|
||||
@ -91,6 +91,8 @@ class EmulatorJS {
|
||||
}
|
||||
/**
|
||||
* Downloads a file from the specified path.
|
||||
* Helper method that delegates to EJS_Download system for all URL-based downloads.
|
||||
* Handles direct data objects (ArrayBuffer, Uint8Array, Blob) and constructs proper paths.
|
||||
* @param {*} path The path to the file to download.
|
||||
* @param {*} type The expected type of the file.
|
||||
* @param {*} progress A callback function for progress updates.
|
||||
@ -103,7 +105,7 @@ class EmulatorJS {
|
||||
downloadFile(path, type, progress, notWithPath, opts, forceExtract = false, dontCache = false) {
|
||||
if (this.debug) console.log("[EJS " + type + "] Downloading " + path);
|
||||
return new Promise(async (resolve) => {
|
||||
// Handle data types (ArrayBuffer, Uint8Array, Blob)
|
||||
// Handle direct data objects (ArrayBuffer, Uint8Array, Blob)
|
||||
const data = this.toData(path);
|
||||
if (data) {
|
||||
data.then((game) => {
|
||||
@ -116,40 +118,14 @@ class EmulatorJS {
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct the full path
|
||||
// Construct the full path/URL
|
||||
const basePath = notWithPath ? "" : this.config.dataPath;
|
||||
let fullPath = basePath + path;
|
||||
if (!notWithPath && this.config.filePaths && typeof this.config.filePaths[path.split("/").pop()] === "string") {
|
||||
fullPath = this.config.filePaths[path.split("/").pop()];
|
||||
}
|
||||
|
||||
// Check if it's a URL
|
||||
let url;
|
||||
try { url = new URL(fullPath) } catch(e) {};
|
||||
|
||||
// Handle non-http(s) URLs (blob:, data:, etc.)
|
||||
if (url && !["http:", "https:"].includes(url.protocol)) {
|
||||
if (opts.method === "HEAD") {
|
||||
resolve({ headers: {} });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let res = await fetch(fullPath);
|
||||
if ((opts.type && opts.type.toLowerCase() === "arraybuffer") || !opts.type) {
|
||||
res = await res.arrayBuffer();
|
||||
} else {
|
||||
res = await res.text();
|
||||
try { res = JSON.parse(res) } catch(e) {}
|
||||
}
|
||||
if (fullPath.startsWith("blob:")) URL.revokeObjectURL(fullPath);
|
||||
resolve({ data: res, headers: {} });
|
||||
} catch(e) {
|
||||
resolve(-1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Use EJS_Download for http(s) downloads
|
||||
// Delegate all URL downloads (http, https, blob, data, etc.) to EJS_Download
|
||||
try {
|
||||
const onProgress = progress instanceof Function ? (status, percentage, loaded, total) => {
|
||||
if (status === "downloading") {
|
||||
@ -183,8 +159,14 @@ class EmulatorJS {
|
||||
dontCache
|
||||
);
|
||||
|
||||
// Handle HEAD requests (returns null)
|
||||
if (!cacheItem) {
|
||||
resolve({ headers: {} });
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract the data from the cache item
|
||||
if (cacheItem && cacheItem.files && cacheItem.files.length > 0) {
|
||||
if (cacheItem.files && cacheItem.files.length > 0) {
|
||||
// If there are files, return the entire cache item
|
||||
// so the caller can access all extracted files
|
||||
if (cacheItem.files.length > 0) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user