203 lines
7.0 KiB
JavaScript
203 lines
7.0 KiB
JavaScript
import { fetch } from '@tauri-apps/plugin-http';
|
|
import { platform } from '@tauri-apps/plugin-os';
|
|
import { currentSettings } from './settings';
|
|
import { writable } from 'svelte/store';
|
|
import { get } from 'svelte/store';
|
|
|
|
/**
|
|
* @typedef {Object} FileLink
|
|
* @property {string} version - The version string (e.g., "5.0.0")
|
|
* @property {string} downloadUrl - The full download URL
|
|
* @property {string} platform - The platform identifier (e.g., 'linux', 'windows', 'macos')
|
|
* @property {string} architecture - The architecture identifier (e.g., 'x64', 'arm64')
|
|
* @property {string} fileExtension - The file extension (e.g., 'tar.xz', 'zip')
|
|
* @property {string} status - The download status ('pending', 'downloading', 'completed', 'error')
|
|
* @property {number} percent - The download progress percentage (0-100)
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} BlenderRelease
|
|
* @property {string} original - The original href from the release page
|
|
* @property {string} version - The version string (e.g., "2.80", "3.6", "4.2alpha")
|
|
* @property {string} fullPath - The full URL to the release folder
|
|
* @property {Date|null} lastModified - The last modified date (currently null)
|
|
* @property {Array<FileLink>} links - Array of download links for this release
|
|
*/
|
|
|
|
export const blenderReleases = writable([]);
|
|
|
|
export async function getBlenderReleases() {
|
|
const settings = await get(currentSettings);
|
|
const currentPlatform = settings.defaultPlatform || platform();
|
|
const currentArch = settings.defaultArch;
|
|
try {
|
|
// Fetch the HTML page
|
|
const response = await fetch('https://download.blender.org/release/');
|
|
const html = await response.text();
|
|
|
|
// Parse the HTML
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(html, 'text/html');
|
|
|
|
// Extract all links that look like version folders
|
|
const links = doc.querySelectorAll('a');
|
|
/** @type {Promise<any>[]} */
|
|
const folderPromises = [];
|
|
|
|
links.forEach((link) => {
|
|
const href = link.getAttribute('href');
|
|
// Filter for directory links (ending with '/') that are version folders
|
|
// This regex matches patterns like Blender2.80/, Blender3.6/, Blender4.2alpha/, etc.
|
|
if (href && href.endsWith('/') && href.match(/^Blender[\d.]+[a-z]*\//)) {
|
|
// Remove the trailing slash and 'Blender' prefix for cleaner version names
|
|
const versionName = href.slice(0, -1).replace('Blender', ''); // Remove trailing slash
|
|
if (parseFloat(versionName) > 2.78) {
|
|
const promise = getBlenderMinorVersionsWithDownload(
|
|
`https://download.blender.org/release/${href}`,
|
|
currentPlatform,
|
|
currentArch
|
|
).then((links) => {
|
|
// Only add to array if there are links
|
|
if (links && links.length > 0) {
|
|
return {
|
|
original: href,
|
|
version: versionName, // e.g., "2.80", "3.6", "4.2alpha"
|
|
fullPath: `https://download.blender.org/release/${href}`,
|
|
lastModified: null, // Date info is in a separate column, harder to parse
|
|
links: links
|
|
};
|
|
}
|
|
|
|
return null;
|
|
});
|
|
folderPromises.push(promise);
|
|
}
|
|
}
|
|
});
|
|
const folders = (await Promise.all(folderPromises)).filter((folder) => folder !== null);
|
|
folders.sort((a, b) => compareVersions(b.version, a.version));
|
|
blenderReleases.set(folders);
|
|
return folders;
|
|
} catch (error) {
|
|
console.error('Error fetching Blender release list:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches the minor versions (e.g., 5.0.0, 5.1.0) for a given major version folder URL,
|
|
* along with their download URLs for the current platform and architecture.
|
|
* @returns {Promise<FileLink[]>} List of minor versions with download URLs
|
|
* @param {string} majorVersionFolderUrl
|
|
* @param {string} currentPlatform
|
|
* @param {string} currentArch
|
|
*/
|
|
async function getBlenderMinorVersionsWithDownload(
|
|
majorVersionFolderUrl,
|
|
currentPlatform,
|
|
currentArch
|
|
) {
|
|
try {
|
|
// 1. Fetch the HTML of the version folder (e.g., Blender5.0/)
|
|
const response = await fetch(majorVersionFolderUrl);
|
|
const html = await response.text();
|
|
|
|
// 2. Parse the HTML
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(html, 'text/html');
|
|
|
|
// 3. Map your platform/arch to the naming convention used in filenames
|
|
/** @type {{ linux: string; windows: string; darwin: string; }} */
|
|
const platformMap = {
|
|
linux: 'linux',
|
|
windows: 'windows',
|
|
darwin: 'macos' // macOS is called 'darwin' in some contexts, but files use 'macos'
|
|
};
|
|
/** @type {{ x86_64: string; arm64: string; }} */
|
|
const archMap = {
|
|
x86_64: 'x64',
|
|
arm64: 'arm64'
|
|
};
|
|
|
|
const targetPlatform =
|
|
platformMap[/** @type {keyof platformMap} */ (currentPlatform)] || currentPlatform;
|
|
const targetArch = archMap[/** @type {keyof archMap} */ (currentArch)] || currentArch;
|
|
|
|
// 4. Get all file links and process them
|
|
const links = doc.querySelectorAll('a');
|
|
/** @type {Map<string, { [key: string]: { url: string; extension: string; platform: string; arch: string } }>} */
|
|
const versionsMap = new Map(); // Key: version string, Value: object with files
|
|
|
|
links.forEach((link) => {
|
|
const href = link.getAttribute('href');
|
|
if (!href) return;
|
|
|
|
// Match pattern: blender-5.0.0-linux-x64.tar.xz
|
|
// Regex captures: version, platform, arch, extension
|
|
const match = href.match(/^blender-([\d.]+)-([a-z]+)-([a-z0-9]+)\.([a-z.]+)$/);
|
|
if (match) {
|
|
const [_, version, platform, arch, ext] =
|
|
/** @type {[string, string, string, string, string]} */ (match);
|
|
|
|
// Ignore checksum files (.md5, .sha256) if you don't need them
|
|
if (ext === 'md5' || ext === 'sha256') return;
|
|
|
|
// Store file info grouped by version
|
|
if (!versionsMap.has(version)) {
|
|
versionsMap.set(version, {});
|
|
}
|
|
const versionFiles = versionsMap.get(version);
|
|
if (!versionFiles) return;
|
|
const key = `${platform}-${arch}`;
|
|
versionFiles[key] = {
|
|
url: new URL(href, majorVersionFolderUrl).href,
|
|
extension: ext,
|
|
platform,
|
|
arch
|
|
};
|
|
}
|
|
});
|
|
|
|
// 5. For each version, pick the correct download link based on target platform/arch
|
|
const results = [];
|
|
const targetKey = `${targetPlatform}-${targetArch}`;
|
|
|
|
for (const [version, files] of versionsMap.entries()) {
|
|
const fileInfo = files[targetKey];
|
|
if (fileInfo) {
|
|
results.push({
|
|
version: version,
|
|
downloadUrl: fileInfo.url,
|
|
platform: targetPlatform,
|
|
architecture: targetArch,
|
|
fileExtension: fileInfo.extension,
|
|
status: 'pending',
|
|
percent: 0
|
|
});
|
|
} else {
|
|
console.warn(`No matching file found for ${version} with ${targetKey}`);
|
|
}
|
|
}
|
|
|
|
return results.sort((a, b) => compareVersions(a.version, b.version)); // Optional: sort versions
|
|
} catch (error) {
|
|
console.error('Error fetching Blender versions:', error);
|
|
return [];
|
|
}
|
|
}
|
|
// Helper function to compare version strings (e.g., "5.0.0", "5.0.1")
|
|
/**
|
|
* @param {string} v1
|
|
* @param {string} v2
|
|
*/
|
|
function compareVersions(v1, v2) {
|
|
const parts1 = v1.split('.').map(Number);
|
|
const parts2 = v2.split('.').map(Number);
|
|
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
const num1 = parts1[i] || 0;
|
|
const num2 = parts2[i] || 0;
|
|
if (num1 !== num2) return num1 - num2;
|
|
}
|
|
return 0;
|
|
}
|