import 'dart:convert'; import 'dart:io'; import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; class BinaryManager { /// 当前应用版本(用于初始化检查) static String? _currentVersion; /// 获取当前应用版本 static Future _getCurrentVersion() async { if (_currentVersion == null) { final packageInfo = await PackageInfo.fromPlatform(); _currentVersion = '${packageInfo.version}+${packageInfo.buildNumber}'; } return _currentVersion!; } /// 检查是否已完成初始化(基于版本号) static Future isInitialized() async { final supportDir = await getApplicationSupportDirectory(); final currentVersion = await _getCurrentVersion(); final initMarker = File( p.join(supportDir.path, '.initialized_$currentVersion'), ); return await initMarker.exists(); } /// 标记初始化完成(文件名包含版本号) static Future markInitialized() async { final supportDir = await getApplicationSupportDirectory(); final currentVersion = await _getCurrentVersion(); final initMarker = File( p.join(supportDir.path, '.initialized_$currentVersion'), ); await initMarker.writeAsString('${DateTime.now().toIso8601String()}\n'); } /// 从assets目录提取文件到指定路径 static Future _extractAsset(String assetPath, String localPath) async { final localFile = File(localPath); try { // 从assets中读取数据 final ByteData data = await rootBundle.load(assetPath); final List bytes = data.buffer.asUint8List(); // 确保目录存在 final dir = Directory(p.dirname(localPath)); if (!await dir.exists()) { await dir.create(recursive: true); } // 写入本地文件 await localFile.writeAsBytes(bytes); print('成功提取文件: $assetPath -> $localPath'); } catch (e) { print('提取资源文件失败 $assetPath: $e'); rethrow; } } /// 初始化应用程序资源 - 将所有必要的assets迁移到系统文件夹 static Future initializeAppResources() async { final currentVersion = await _getCurrentVersion(); if (await isInitialized()) { print('应用程序资源已初始化,当前版本: $currentVersion'); return; } try { // 迁移shell脚本 await _migrateShellScript(); // 迁移二进制文件 await _migrateBinaries(); // 迁移模型文件 await _migrateModels(); // 迁移3D模型文件 await migrate3DModels(); // 标记初始化完成 await markInitialized(); print('应用程序资源初始化完成'); } catch (e) { print('应用程序资源初始化失败: $e'); rethrow; } } /// 迁移3D模型文件 static Future migrate3DModels() async { print('开始迁移3D模型文件...'); final supportDir = await getApplicationSupportDirectory(); final models3dDir = Directory(p.join(supportDir.path, 'cup')); // 创建3D模型目录 if (!await models3dDir.exists()) { await models3dDir.create(recursive: true); } // 3D模型文件列表 final model3dFiles = ['model.gltf', 'model.bin', 'texture.jpg']; // 迁移所有3D模型文件 for (final modelFile in model3dFiles) { try { await _extractAsset( 'assets/cup/$modelFile', p.join(models3dDir.path, modelFile), ); } catch (e) { print('迁移3D模型文件失败 $modelFile: $e'); // 继续迁移其他模型文件 } } print('3D模型文件迁移完成'); } static Future _migrateShellScript() async { print('开始迁移脚本文件...'); final supportDir = await getApplicationSupportDirectory(); // 迁移 Shell 脚本 (macOS/Linux) final shellScriptPath = p.join(supportDir.path, 'upscale_to_8k_single.sh'); await _extractAsset('assets/upscale_to_8k_single.sh', shellScriptPath); // 设置脚本执行权限(仅限 macOS/Linux) if (Platform.isMacOS || Platform.isLinux) { try { final result = await Process.run('chmod', ['+x', shellScriptPath]); if (result.exitCode == 0) { print('成功设置Shell脚本执行权限: $shellScriptPath'); } else { print('设置Shell脚本执行权限失败: ${result.stderr}'); } } catch (e) { print('设置Shell脚本权限异常: $e'); } } print('脚本文件迁移完成'); } /// 迁移二进制文件 static Future _migrateBinaries() async { print('开始迁移二进制文件...'); final supportDir = await getApplicationSupportDirectory(); final binDir = Directory(p.join(supportDir.path, 'bin')); // 创建bin目录 if (!await binDir.exists()) { await binDir.create(recursive: true); } // macOS 二进制文件 final macBinDir = Directory(p.join(binDir.path, 'macos')); if (!await macBinDir.exists()) { await macBinDir.create(recursive: true); } await _extractAsset( 'assets/bin/mac/upscayl-bin', p.join(macBinDir.path, 'upscayl-bin'), ); // Windows 二进制文件 final winBinDir = Directory(p.join(binDir.path, 'windows')); if (!await winBinDir.exists()) { await winBinDir.create(recursive: true); } await _extractAsset( 'assets/bin/windows/upscayl-bin.exe', p.join(winBinDir.path, 'upscayl-bin.exe'), ); await _extractAsset( 'assets/bin/windows/vcomp140.dll', p.join(winBinDir.path, 'vcomp140.dll'), ); await _extractAsset( 'assets/bin/windows/vcomp140d.dll', p.join(winBinDir.path, 'vcomp140d.dll'), ); // 设置 macOS/Linux 二进制文件执行权限 if (Platform.isMacOS || Platform.isLinux) { final binPath = p.join(macBinDir.path, 'upscayl-bin'); try { final result = await Process.run('chmod', ['+x', binPath]); if (result.exitCode == 0) { print('成功设置执行权限: $binPath'); } else { print('设置执行权限失败: ${result.stderr}'); } } catch (e) { print('设置权限异常: $e'); } } print('二进制文件迁移完成'); } /// 迁移模型文件 static Future _migrateModels() async { print('开始迁移模型文件...'); final supportDir = await getApplicationSupportDirectory(); final modelsDir = Directory(p.join(supportDir.path, 'models')); // 创建models目录 if (!await modelsDir.exists()) { await modelsDir.create(recursive: true); } // 模型文件列表 final modelFiles = [ 'high-fidelity-4x.bin', 'high-fidelity-4x.param', 'digital-art-4x.bin', 'digital-art-4x.param', 'remacri-4x.bin', 'remacri-4x.param', 'ultrasharp-4x.bin', 'ultrasharp-4x.param', 'upscayl-standard-4x.bin', 'upscayl-standard-4x.param', 'ultramix-balanced-4x.bin', 'ultramix-balanced-4x.param', 'upscayl-lite-4x.bin', 'upscayl-lite-4x.param', ]; // 迁移所有模型文件 for (final modelFile in modelFiles) { try { await _extractAsset( 'assets/models/$modelFile', p.join(modelsDir.path, modelFile), ); } catch (e) { print('迁移模型文件失败 $modelFile: $e'); // 继续迁移其他模型文件 } } print('模型文件迁移完成'); } /// 获取模型文件目录路径 static Future getModelsPath() async { final supportDir = await getApplicationSupportDirectory(); return p.join(supportDir.path, 'cup', "model.gltf"); } /// 获取3D模型文件目录路径 static Future get3DModelsPath() async { final supportDir = await getApplicationSupportDirectory(); return p.join(supportDir.path, 'cup'); } static Future> readLocalJson() async { // 获取应用支持目录(默认在 ~/Library/Application Support/) final appSupportDir = await getApplicationSupportDirectory(); final targetDir = Directory('${appSupportDir.path}/cup'); // 检查目录是否存在,若不存在则创建 if (!await targetDir.exists()) { await targetDir.create(recursive: true); } print('file=${targetDir.path}/model.gltf'); // 构建 JSON 文件路径 final file = File('${targetDir.path}/model.gltf'); if (await file.exists()) { final content = await file.readAsString(); return jsonDecode(content); } else { throw Exception('JSON 文件不存在'); } } /// 获取特定的3D模型文件路径 static Future get3DModelPath(String filename) async { final models3dPath = await get3DModelsPath(); return p.join(models3dPath, filename); } /// 获取脚本文件路径 (仅适用于macOS/Linux) static Future getScriptPath() async { if (Platform.isWindows) { throw UnsupportedError('Windows平台已使用专门的UpscaleWindowsService,不再需要脚本文件'); } final supportDir = await getApplicationSupportDirectory(); final scriptName = 'upscale_to_8k_single.sh'; final scriptPath = p.join(supportDir.path, scriptName); final scriptFile = File(scriptPath); if (!await scriptFile.exists()) { throw FileSystemException( '脚本文件不存在: $scriptPath\n请确保应用程序已正确初始化资源', scriptPath, ); } return scriptPath; } /// 获取二进制文件路径 static Future getBinaryPath() async { final supportDir = await getApplicationSupportDirectory(); String platformDir; String binaryName; if (Platform.isMacOS) { platformDir = 'macos'; binaryName = 'upscayl-bin'; } else if (Platform.isWindows) { platformDir = 'windows'; binaryName = 'upscayl-bin.exe'; } else if (Platform.isLinux) { platformDir = 'macos'; // Linux 使用 macOS 的二进制文件 binaryName = 'upscayl-bin'; } else { throw UnsupportedError('不支持的操作系统: ${Platform.operatingSystem}'); } final binPath = p.join(supportDir.path, 'bin', platformDir, binaryName); // 检查文件是否存在 final binFile = File(binPath); if (!await binFile.exists()) { throw FileSystemException('二进制文件不存在: $binPath\n请确保应用程序已正确初始化资源', binPath); } return binPath; } /// 设置脚本执行权限 static Future setScriptExecutable(String scriptPath) async { // 设置脚本执行权限(仅限 macOS/Linux) if (Platform.isMacOS || Platform.isLinux) { try { final result = await Process.run('chmod', ['+x', scriptPath]); if (result.exitCode == 0) { print('成功设置脚本执行权限: $scriptPath'); } else { print('设置脚本执行权限失败: ${result.stderr}'); } } catch (e) { print('设置脚本权限异常: $e'); } } // Windows 不需要设置执行权限 } }