mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 05:12:33 +00:00
- Renamed all instances of "AlphaHuman" to "OpenHuman" in various components, pages, and files. - Updated links and email addresses to reflect the new OpenHuman branding. - Ensured consistency in messaging and branding throughout the application.
361 lines
10 KiB
TypeScript
361 lines
10 KiB
TypeScript
/**
|
|
* Device Detection Utility
|
|
*
|
|
* Detects the user's platform/device type for showing appropriate download links
|
|
*/
|
|
|
|
export type Platform = 'windows' | 'macos' | 'linux' | 'android' | 'ios' | 'unknown';
|
|
|
|
export type Architecture = 'x64' | 'aarch64' | 'amd64' | 'x86_64' | 'arm64' | 'unknown';
|
|
|
|
export interface PlatformInfo {
|
|
platform: Platform;
|
|
architecture: Architecture;
|
|
isMobile: boolean;
|
|
isDesktop: boolean;
|
|
}
|
|
|
|
export interface GitHubReleaseAsset {
|
|
name: string;
|
|
browser_download_url: string;
|
|
content_type: string;
|
|
size: number;
|
|
}
|
|
|
|
export interface GitHubRelease {
|
|
tag_name: string;
|
|
name: string;
|
|
assets: GitHubReleaseAsset[];
|
|
}
|
|
|
|
export interface PlatformDownloadLinks {
|
|
windows?: string;
|
|
macos?: string;
|
|
linux?: string;
|
|
android?: string;
|
|
ios?: string;
|
|
}
|
|
|
|
export interface ArchitectureDownloadLink {
|
|
architecture: Architecture;
|
|
displayName: string;
|
|
url: string;
|
|
fileName: string;
|
|
}
|
|
|
|
export interface PlatformArchitectureLinks {
|
|
windows?: ArchitectureDownloadLink[];
|
|
macos?: ArchitectureDownloadLink[];
|
|
linux?: ArchitectureDownloadLink[];
|
|
android?: ArchitectureDownloadLink[];
|
|
}
|
|
|
|
/**
|
|
* Detect the user's architecture based on user agent and platform
|
|
*/
|
|
export function detectArchitecture(): Architecture {
|
|
if (typeof window === 'undefined') {
|
|
return 'unknown';
|
|
}
|
|
|
|
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
const platform = window.navigator.platform.toLowerCase();
|
|
|
|
// Check for ARM architecture (Apple Silicon, ARM64)
|
|
if (/arm64|aarch64/i.test(userAgent) || /arm64|aarch64/i.test(platform)) {
|
|
return 'aarch64';
|
|
}
|
|
|
|
// Check for x64/amd64/x86_64
|
|
if (/x64|amd64|x86_64|win64|wow64/i.test(userAgent) || /x64|amd64|x86_64/i.test(platform)) {
|
|
return 'x64';
|
|
}
|
|
|
|
// macOS on Apple Silicon
|
|
if (/mac/i.test(platform) && /cpu os \d+_\d+_0 like mac os x/i.test(userAgent)) {
|
|
const match = userAgent.match(/cpu os (\d+)_(\d+)_/);
|
|
if (match) {
|
|
const major = parseInt(match[1], 10);
|
|
if (major >= 11) {
|
|
// Rely on user agent check above for ARM
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default to x64 for desktop platforms
|
|
if (/win|mac|linux/i.test(platform)) {
|
|
return 'x64';
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* Detect the user's platform based on user agent
|
|
*/
|
|
export function detectPlatform(): PlatformInfo {
|
|
if (typeof window === 'undefined') {
|
|
return { platform: 'unknown', architecture: 'unknown', isMobile: false, isDesktop: false };
|
|
}
|
|
|
|
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
const platform = window.navigator.platform.toLowerCase();
|
|
const architecture = detectArchitecture();
|
|
|
|
const isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
|
|
|
if (/iphone|ipad|ipod/i.test(userAgent)) {
|
|
return { platform: 'ios', architecture: 'aarch64', isMobile: true, isDesktop: false };
|
|
}
|
|
|
|
if (/android/i.test(userAgent)) {
|
|
return { platform: 'android', architecture, isMobile: true, isDesktop: false };
|
|
}
|
|
|
|
if (/win/i.test(platform) || /windows/i.test(userAgent)) {
|
|
return { platform: 'windows', architecture, isMobile: false, isDesktop: true };
|
|
}
|
|
|
|
if (/mac/i.test(platform) || /macintosh/i.test(userAgent)) {
|
|
return { platform: 'macos', architecture, isMobile: false, isDesktop: true };
|
|
}
|
|
|
|
if (/linux/i.test(platform) && !/android/i.test(userAgent)) {
|
|
return { platform: 'linux', architecture, isMobile: false, isDesktop: true };
|
|
}
|
|
|
|
return { platform: 'unknown', architecture, isMobile, isDesktop: !isMobile };
|
|
}
|
|
|
|
/**
|
|
* Fetch the latest release from GitHub
|
|
*/
|
|
export async function fetchLatestRelease(): Promise<GitHubRelease> {
|
|
const response = await fetch(
|
|
'https://api.github.com/repos/openhumanxyz/openhuman/releases/latest'
|
|
);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch release: ${response.statusText}`);
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
function extractArchitecture(name: string): Architecture {
|
|
const lowerName = name.toLowerCase();
|
|
|
|
if (lowerName.includes('aarch64') || lowerName.includes('arm64')) {
|
|
return 'aarch64';
|
|
}
|
|
if (lowerName.includes('amd64') || lowerName.includes('x86_64') || lowerName.includes('x64')) {
|
|
return 'x64';
|
|
}
|
|
if (lowerName.includes('x86') || lowerName.includes('i386')) {
|
|
return 'x64';
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* Get architecture display name
|
|
*/
|
|
export function getArchitectureDisplayName(arch: Architecture): string {
|
|
const names: Record<Architecture, string> = {
|
|
x64: 'Intel (x64)',
|
|
aarch64: 'Apple Silicon (ARM64)',
|
|
amd64: 'AMD64',
|
|
x86_64: 'x86_64',
|
|
arm64: 'ARM64',
|
|
unknown: 'Unknown',
|
|
};
|
|
return names[arch] || arch;
|
|
}
|
|
|
|
/**
|
|
* Parse GitHub release assets and map them to platforms with architecture support
|
|
*/
|
|
export function parseReleaseAssetsByArchitecture(
|
|
assets: GitHubReleaseAsset[]
|
|
): PlatformArchitectureLinks {
|
|
const links: PlatformArchitectureLinks = {};
|
|
|
|
const windowsMap = new Map<Architecture, ArchitectureDownloadLink>();
|
|
const macosMap = new Map<Architecture, ArchitectureDownloadLink>();
|
|
const linuxMap = new Map<Architecture, ArchitectureDownloadLink>();
|
|
const androidMap = new Map<Architecture, ArchitectureDownloadLink>();
|
|
|
|
for (const asset of assets) {
|
|
const name = asset.name.toLowerCase();
|
|
|
|
if (name.endsWith('.sig')) {
|
|
continue;
|
|
}
|
|
|
|
const architecture = extractArchitecture(asset.name);
|
|
const displayName = getArchitectureDisplayName(architecture);
|
|
const link: ArchitectureDownloadLink = {
|
|
architecture,
|
|
displayName,
|
|
url: asset.browser_download_url,
|
|
fileName: asset.name,
|
|
};
|
|
|
|
if (
|
|
name.includes('windows') ||
|
|
name.includes('.exe') ||
|
|
name.includes('.msi') ||
|
|
(name.includes('.zip') && !name.includes('macos'))
|
|
) {
|
|
if (
|
|
!windowsMap.has(architecture) ||
|
|
(name.includes('windows') &&
|
|
!windowsMap.get(architecture)?.fileName.toLowerCase().includes('windows'))
|
|
) {
|
|
windowsMap.set(architecture, link);
|
|
}
|
|
} else if (name.includes('macos') || name.includes('.dmg') || name.includes('darwin')) {
|
|
if (
|
|
!macosMap.has(architecture) ||
|
|
(name.includes('macos') &&
|
|
!macosMap.get(architecture)?.fileName.toLowerCase().includes('macos'))
|
|
) {
|
|
macosMap.set(architecture, link);
|
|
}
|
|
} else if (
|
|
name.includes('linux') ||
|
|
name.includes('.appimage') ||
|
|
name.includes('.deb') ||
|
|
name.includes('.rpm')
|
|
) {
|
|
const existing = linuxMap.get(architecture);
|
|
if (!existing) {
|
|
linuxMap.set(architecture, link);
|
|
} else {
|
|
const existingName = existing.fileName.toLowerCase();
|
|
if (name.includes('.appimage') && !existingName.includes('.appimage')) {
|
|
linuxMap.set(architecture, link);
|
|
} else if (name.includes('.deb') && existingName.includes('.rpm')) {
|
|
linuxMap.set(architecture, link);
|
|
}
|
|
}
|
|
} else if (name.includes('android') || name.includes('.apk')) {
|
|
if (!androidMap.has(architecture)) {
|
|
androidMap.set(architecture, link);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (windowsMap.size > 0) {
|
|
links.windows = Array.from(windowsMap.values());
|
|
}
|
|
if (macosMap.size > 0) {
|
|
links.macos = Array.from(macosMap.values());
|
|
}
|
|
if (linuxMap.size > 0) {
|
|
links.linux = Array.from(linuxMap.values());
|
|
}
|
|
if (androidMap.size > 0) {
|
|
links.android = Array.from(androidMap.values());
|
|
}
|
|
|
|
const sortArchitectures = (
|
|
archLinks: ArchitectureDownloadLink[],
|
|
preferredArch?: Architecture
|
|
) => {
|
|
return archLinks.sort((a, b) => {
|
|
if (preferredArch && a.architecture === preferredArch) return -1;
|
|
if (preferredArch && b.architecture === preferredArch) return 1;
|
|
if (a.architecture === 'x64') return -1;
|
|
if (b.architecture === 'x64') return 1;
|
|
if (a.architecture === 'aarch64') return -1;
|
|
if (b.architecture === 'aarch64') return 1;
|
|
return 0;
|
|
});
|
|
};
|
|
|
|
if (links.windows) {
|
|
links.windows = sortArchitectures(links.windows);
|
|
}
|
|
if (links.macos) {
|
|
links.macos = sortArchitectures(links.macos);
|
|
}
|
|
if (links.linux) {
|
|
links.linux = sortArchitectures(links.linux);
|
|
}
|
|
if (links.android) {
|
|
links.android = sortArchitectures(links.android);
|
|
}
|
|
|
|
return links;
|
|
}
|
|
|
|
/**
|
|
* Parse GitHub release assets and map them to platforms (legacy)
|
|
*/
|
|
export function parseReleaseAssets(assets: GitHubReleaseAsset[]): PlatformDownloadLinks {
|
|
const archLinks = parseReleaseAssetsByArchitecture(assets);
|
|
const links: PlatformDownloadLinks = {};
|
|
|
|
if (archLinks.windows && archLinks.windows.length > 0) {
|
|
links.windows = archLinks.windows[0].url;
|
|
}
|
|
if (archLinks.macos && archLinks.macos.length > 0) {
|
|
links.macos = archLinks.macos[0].url;
|
|
}
|
|
if (archLinks.linux && archLinks.linux.length > 0) {
|
|
links.linux = archLinks.linux[0].url;
|
|
}
|
|
if (archLinks.android && archLinks.android.length > 0) {
|
|
links.android = archLinks.android[0].url;
|
|
}
|
|
|
|
return links;
|
|
}
|
|
|
|
/**
|
|
* Get download link for a specific platform (fallback to dummy links)
|
|
*/
|
|
export function getDownloadLink(platform: Platform, releaseLinks?: PlatformDownloadLinks): string {
|
|
if (releaseLinks && platform !== 'unknown' && platform !== 'ios') {
|
|
if (platform === 'windows' && releaseLinks.windows) {
|
|
return releaseLinks.windows;
|
|
}
|
|
if (platform === 'macos' && releaseLinks.macos) {
|
|
return releaseLinks.macos;
|
|
}
|
|
if (platform === 'linux' && releaseLinks.linux) {
|
|
return releaseLinks.linux;
|
|
}
|
|
if (platform === 'android' && releaseLinks.android) {
|
|
return releaseLinks.android;
|
|
}
|
|
}
|
|
|
|
const links: Record<Platform, string> = {
|
|
windows: 'https://example.com/download/openhuman-windows.exe',
|
|
macos: 'https://example.com/download/openhuman-macos.dmg',
|
|
linux: 'https://example.com/download/openhuman-linux.AppImage',
|
|
android: 'https://example.com/download/openhuman-android.apk',
|
|
ios: 'https://apps.apple.com/app/openhuman',
|
|
unknown: 'https://example.com/download',
|
|
};
|
|
|
|
return links[platform];
|
|
}
|
|
|
|
/**
|
|
* Get platform display name
|
|
*/
|
|
export function getPlatformDisplayName(platform: Platform): string {
|
|
const names: Record<Platform, string> = {
|
|
windows: 'Windows',
|
|
macos: 'macOS',
|
|
linux: 'Linux',
|
|
android: 'Android',
|
|
ios: 'iOS',
|
|
unknown: 'Your Device',
|
|
};
|
|
|
|
return names[platform];
|
|
}
|