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 不需要设置执行权限
}
}

View File

@@ -0,0 +1,204 @@
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 ModelManager {
/// 当前应用版本(用于初始化检查)
static String? _currentVersion;
/// 获取当前应用版本
static Future<String> _getCurrentVersion() async {
if (_currentVersion == null) {
final packageInfo = await PackageInfo.fromPlatform();
_currentVersion = '${packageInfo.version}+${packageInfo.buildNumber}';
}
return _currentVersion!;
}
/// 检查3D模型资源是否已完成初始化基于版本号
static Future<bool> isInitialized() async {
final supportDir = await getApplicationSupportDirectory();
final currentVersion = await _getCurrentVersion();
final initMarker = File(
p.join(supportDir.path, '3d_models', '.initialized_$currentVersion'),
);
return await initMarker.exists();
}
/// 标记3D模型资源初始化完成
static Future<void> markInitialized() async {
final supportDir = await getApplicationSupportDirectory();
final currentVersion = await _getCurrentVersion();
final modelsDir = Directory(p.join(supportDir.path, '3d_models'));
if (!await modelsDir.exists()) {
await modelsDir.create(recursive: true);
}
final initMarker = File(
p.join(modelsDir.path, '.initialized_$currentVersion'),
);
await initMarker.writeAsString('${DateTime.now().toIso8601String()}\n');
}
/// 从assets目录提取文件到指定路径
static Future<void> _extractAsset(String assetPath, String localPath) async {
final localFile = File(localPath);
final localDir = Directory(p.dirname(localPath));
// 确保目录存在
if (!await localDir.exists()) {
await localDir.create(recursive: true);
}
try {
// 从assets中读取数据
final ByteData data = await rootBundle.load(assetPath);
final List<int> bytes = data.buffer.asUint8List();
// 写入本地文件
await localFile.writeAsBytes(bytes);
print('成功提取3D模型文件: $assetPath -> $localPath');
} catch (e) {
print('提取3D模型资源文件失败 $assetPath: $e');
rethrow;
}
}
/// 初始化3D模型资源
static Future<void> initializeAppResources() async {
final currentVersion = await _getCurrentVersion();
if (await isInitialized()) {
print('3D模型资源已初始化当前版本: $currentVersion');
return;
}
try {
// 迁移模型文件
await _migrateModels();
// 标记初始化完成
await markInitialized();
print('3D模型资源初始化完成');
} catch (e) {
print('3D模型资源初始化失败: $e');
rethrow;
}
}
/// 迁移3D模型文件
/// 迁移3D模型文件
static Future<void> _migrateModels() async {
print('开始迁移3D模型文件...');
final supportDir = await getApplicationSupportDirectory();
final modelsDir = Directory(p.join(supportDir.path, 'models'));
// 创建3D模型目录
if (!await modelsDir.exists()) {
await modelsDir.create(recursive: true);
}
// 创建cup子目录
final cupDir = Directory(p.join(modelsDir.path, 'cup'));
if (!await cupDir.exists()) {
await cupDir.create(recursive: true);
}
// 3D模型文件列表 - 修正路径
final modelFiles = [
'cup/model.gltf',
'cup/model.bin',
'cup/texture.jpg',
];
// 迁移所有3D模型文件
for (final modelFile in modelFiles) {
try {
// 确保源文件路径正确
final assetPath = 'assets/$modelFile';
// 检查资源是否存在
try {
await rootBundle.load(assetPath);
} catch (e) {
print('资源文件不存在: $assetPath');
continue;
}
await _extractAsset(
assetPath,
p.join(modelsDir.path, modelFile),
);
} catch (e) {
print('迁移3D模型文件失败 $modelFile: $e');
// 继续迁移其他模型文件
}
}
print('3D模型文件迁移完成');
}
/// 获取3D模型文件目录路径
static Future<String> getModelsPath() async {
final supportDir = await getApplicationSupportDirectory();
return p.join(supportDir.path, 'models/cup/');
}
/// 获取特定3D模型文件的路径
static Future<String> getModelFilePath(String relativePath) async {
final modelsPath = await getModelsPath();
final filePath = p.join(modelsPath, relativePath);
final file = File(filePath);
if (!await file.exists()) {
throw FileSystemException(
'3D模型文件不存在: $filePath\n请确保应用程序已正确初始化3D模型资源',
filePath,
);
}
return filePath;
}
/// 获取GLTF模型文件路径
static Future<String> getGltfModelPath() async {
return getModelFilePath('cup/model.gltf');
}
/// 获取BIN模型文件路径
static Future<String> getBinModelPath() async {
return getModelFilePath('cup/model.bin');
}
/// 获取纹理文件路径
static Future<String> getTexturePath() async {
return getModelFilePath('cup/texture.jpg');
}
/// 更新纹理文件
static Future<void> updateTexture(File newTextureFile) async {
try {
final texturePath = await getTexturePath();
final textureFile = File(texturePath);
// 删除旧纹理文件
if (await textureFile.exists()) {
await textureFile.delete();
}
// 复制新纹理文件
await newTextureFile.copy(texturePath);
print('纹理文件更新成功: $texturePath');
} catch (e) {
print('更新纹理文件失败: $e');
rethrow;
}
}
}