UNPKG

vaporwaver-ts

Version:

TypeScript version of the Vaporwaver Python library. Vaporwaver is a Python library for generating vaporwave image art.

142 lines (122 loc) 5.57 kB
import { spawn } from "child_process"; export class DependencyError extends Error { constructor(message: string, public readonly details: any = {}) { super(message); this.name = 'DependencyError'; } } export class DependencyChecker { private static pythonCommand = process.platform === 'win32' ? 'python' : 'python3'; private static pipCommand = process.platform === 'win32' ? 'pip' : 'pip3'; static async checkPython(minVersion = '3.7.0'): Promise<void> { try { const version = await this.getPythonVersion(); if (!this.isVersionSatisfied(version, minVersion)) { throw new DependencyError( `Python version ${minVersion} or higher is required. Found: ${version}` ); } } catch (error) { if (error instanceof DependencyError) throw error; throw new DependencyError('Python is not installed or not accessible'); } } static async checkPythonDependencies(): Promise<void> { try { const deps = await this.getInstalledPythonPackages(); const required = ['Pillow', 'glitch-this', 'opencv-python']; const missing = required.filter(dep => !deps.some(installed => installed.toLowerCase().startsWith(dep.toLowerCase()) )); if (missing.length > 0) { // Try to install missing dependencies await this.installMissingDependencies(missing); } } catch (error) { throw new DependencyError( 'Failed to verify Python dependencies', { originalError: error } ); } } private static async getPythonVersion(): Promise<string> { return new Promise((resolve, reject) => { const python = spawn(this.pythonCommand, ['--version']); let output = ''; python.stdout.on('data', (data) => { output += data; }); python.stderr.on('data', (data) => { output += data; }); python.on('close', (code) => { if (code === 0) { const version = output.match(/(\d+\.\d+\.\d+)/); if (version) { resolve(version[1]); } else { reject(new Error('Could not parse Python version')); } } else { reject(new Error(`Python check failed with code ${code}`)); } }); }); } private static async getInstalledPythonPackages(): Promise<string[]> { return new Promise((resolve, reject) => { const pip = spawn(this.pipCommand, ['freeze']); let output = ''; pip.stdout.on('data', (data) => { output += data; }); pip.stderr.on('data', () => { /* ignore stderr */ }); pip.on('close', (code) => { if (code === 0) { resolve(output.split('\n').filter(Boolean)); } else { reject(new Error('Failed to get installed packages')); } }); }); } private static async installMissingDependencies(packages: string[]): Promise<void> { // Try different installation methods const methods = [ { cmd: this.pipCommand, args: ['install', '--user'] }, { cmd: this.pipCommand, args: ['install'] }, { cmd: this.pythonCommand, args: ['-m', 'pip', 'install', '--user'] }, { cmd: this.pythonCommand, args: ['-m', 'pip', 'install'] } ]; for (const method of methods) { try { await this.tryInstallMethod(method.cmd, [...method.args, ...packages]); return; // Installation successful } catch (error) { console.warn(`Installation attempt failed with method ${method.cmd}`, error); // Continue to next method } } throw new DependencyError( 'Failed to install Python dependencies. Please install manually:\n' + `${this.pythonCommand} -m pip install ${packages.join(' ')}` ); } private static async tryInstallMethod(cmd: string, args: string[]): Promise<void> { return new Promise((resolve, reject) => { const install = spawn(cmd, args); let output = ''; install.stdout.on('data', (data) => { output += data; }); install.stderr.on('data', (data) => { output += data; }); install.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(`Installation failed with code ${code}\n${output}`)); } }); }); } private static isVersionSatisfied(current: string, required: string): boolean { const parseVersion = (v: string): number[] => v.split('.').map(Number); const [currentMajor, currentMinor, currentPatch] = parseVersion(current); const [reqMajor, reqMinor, reqPatch] = parseVersion(required); if (currentMajor !== reqMajor) return currentMajor > reqMajor; if (currentMinor !== reqMinor) return currentMinor > reqMinor; return currentPatch >= reqPatch; } }