This commit is contained in:
jingyun
2025-08-22 15:13:34 +08:00
parent 961b2ae1ee
commit 1c07d576d3
44 changed files with 8049 additions and 16 deletions

View File

@@ -0,0 +1,372 @@
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<String> _getCurrentVersion() async {
if (_currentVersion == null) {
final packageInfo = await PackageInfo.fromPlatform();
_currentVersion = '${packageInfo.version}+${packageInfo.buildNumber}';
}
return _currentVersion!;
}
/// 检查是否已完成初始化(基于版本号)
static Future<bool> 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<void> 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<void> _extractAsset(String assetPath, String localPath) async {
final localFile = File(localPath);
try {
// 从assets中读取数据
final ByteData data = await rootBundle.load(assetPath);
final List<int> 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<void> 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<void> 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<void> _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<void> _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<void> _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<String> getModelsPath() async {
final supportDir = await getApplicationSupportDirectory();
return p.join(supportDir.path, 'cup', "model.gltf");
}
/// 获取3D模型文件目录路径
static Future<String> get3DModelsPath() async {
final supportDir = await getApplicationSupportDirectory();
return p.join(supportDir.path, 'cup');
}
static Future<Map<String, dynamic>> 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<String> get3DModelPath(String filename) async {
final models3dPath = await get3DModelsPath();
return p.join(models3dPath, filename);
}
/// 获取脚本文件路径 (仅适用于macOS/Linux)
static Future<String> 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<String> 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<void> 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 不需要设置执行权限
}
}