mixvideo-v2/apps/desktop/src/services/imageDownloadService.ts

193 lines
4.5 KiB
TypeScript

import { invoke } from '@tauri-apps/api/core';
import { save } from '@tauri-apps/plugin-dialog';
import { GroundingSource } from '../types/ragGrounding';
/**
* 图片下载选项
*/
export interface ImageDownloadOptions {
/** 建议的文件名 */
suggestedName?: string;
/** 文件扩展名 */
extension?: string;
/** 是否显示保存对话框 */
showSaveDialog?: boolean;
}
/**
* 图片下载结果
*/
export interface ImageDownloadResult {
/** 是否成功 */
success: boolean;
/** 保存的文件路径 */
filePath?: string;
/** 错误信息 */
error?: string;
}
/**
* 图片下载服务
* 支持从URI下载图片到本地
*/
export class ImageDownloadService {
/**
* 下载图片到本地
*/
static async downloadImage(
source: GroundingSource,
options: ImageDownloadOptions = {}
): Promise<ImageDownloadResult> {
try {
const { suggestedName, extension = 'jpg', showSaveDialog = true } = options;
// 检查图片URI
if (!source.uri) {
return {
success: false,
error: '图片URI不存在'
};
}
// 生成建议的文件名
const defaultName = suggestedName || this.generateFileName(source);
const fileName = `${defaultName}.${extension}`;
let filePath: string | null = null;
if (showSaveDialog) {
// 显示保存对话框
filePath = await save({
defaultPath: fileName,
filters: [
{
name: '图片文件',
extensions: ['jpg', 'jpeg', 'png', 'webp', 'gif']
},
{
name: '所有文件',
extensions: ['*']
}
]
});
if (!filePath) {
return {
success: false,
error: '用户取消了保存操作'
};
}
} else {
// 使用默认下载目录
filePath = await this.getDefaultDownloadPath(fileName);
}
// 调用后端下载命令
await invoke('download_image_from_uri', {
uri: source.uri,
filePath: filePath
});
return {
success: true,
filePath: filePath
};
} catch (error) {
console.error('图片下载失败:', error);
return {
success: false,
error: error instanceof Error ? error.message : '下载失败'
};
}
}
/**
* 批量下载图片
*/
static async downloadImages(
sources: GroundingSource[],
options: ImageDownloadOptions = {}
): Promise<ImageDownloadResult[]> {
const results: ImageDownloadResult[] = [];
for (const source of sources) {
const result = await this.downloadImage(source, {
...options,
showSaveDialog: false // 批量下载时不显示对话框
});
results.push(result);
}
return results;
}
/**
* 生成文件名
*/
private static generateFileName(source: GroundingSource): string {
const title = source.title || 'fashion_image';
const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, '');
// 清理文件名中的特殊字符
const cleanTitle = title
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '_')
.toLowerCase();
return `${cleanTitle}_${timestamp}`;
}
/**
* 获取默认下载路径
*/
private static async getDefaultDownloadPath(fileName: string): Promise<string> {
try {
// 调用后端获取默认下载目录
const downloadDir = await invoke<string>('get_default_download_directory');
return `${downloadDir}/${fileName}`;
} catch (error) {
// 如果获取失败,使用当前目录
return fileName;
}
}
/**
* 检查图片是否可下载
*/
static isDownloadable(source: GroundingSource): boolean {
return !!(source.uri && source.uri.startsWith('http'));
}
/**
* 获取图片信息
*/
static getImageInfo(source: GroundingSource): {
title: string;
description: string;
size?: string;
format?: string;
} {
const imageData = source.content?.text || source.content;
return {
title: source.title || '时尚图片',
description: imageData?.description || '',
size: imageData?.size,
format: this.extractImageFormat(source.uri)
};
}
/**
* 从URI提取图片格式
*/
private static extractImageFormat(uri?: string): string | undefined {
if (!uri) return undefined;
const match = uri.match(/\.([a-zA-Z0-9]+)(?:\?|$)/);
return match ? match[1].toLowerCase() : undefined;
}
}
export default ImageDownloadService;