"use strict";
/**
 * 视频生成模块 - 业务逻辑
 * 处理视频项目的增删改查和生成逻辑
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createVideoProject = createVideoProject;
exports.getVideoProjects = getVideoProjects;
exports.getVideoProject = getVideoProject;
exports.updateVideoProject = updateVideoProject;
exports.deleteVideoProject = deleteVideoProject;
exports.generateVideoForProject = generateVideoForProject;
exports.getGenerateProgress = getGenerateProgress;
exports.getMaterials = getMaterials;
exports.uploadMaterial = uploadMaterial;
exports.deleteMaterial = deleteMaterial;
exports.createVideoProjectFromBook = createVideoProjectFromBook;
const client_1 = require("@prisma/client");
const path_1 = __importDefault(require("path"));
const uuid_1 = require("uuid");
const video_generator_types_1 = require("./video-generator.types");
const video_generator_ffmpeg_1 = require("./video-generator.ffmpeg");
const prisma = new client_1.PrismaClient();
// ============ 视频项目管理 ============
/**
 * 创建视频项目
 */
async function createVideoProject(data, userId) {
    const config = data.config || video_generator_types_1.PRESET_VIDEO_CONFIGS.portrait;
    const project = await prisma.videoProject.create({
        data: {
            userId: userId,
            title: data.title,
            description: data.description,
            coverUrl: data.coverUrl,
            configJson: (0, video_generator_types_1.serializeConfig)(config),
            bookId: data.bookId,
            chapterId: data.chapterId, // 直接关联章节
            status: 'draft',
            progress: 0,
        },
    });
    return {
        ...project,
        config,
    };
}
/**
 * 获取视频项目列表
 */
async function getVideoProjects(query) {
    const page = query.page || 1;
    const pageSize = query.pageSize || 10;
    const skip = (page - 1) * pageSize;
    const where = {};
    if (query.userId)
        where.userId = query.userId;
    if (query.status)
        where.status = query.status;
    const [items, total] = await Promise.all([
        prisma.videoProject.findMany({
            where,
            orderBy: { createdAt: 'desc' },
            skip,
            take: pageSize,
        }),
        prisma.videoProject.count({ where }),
    ]);
    return {
        items: items.map((item) => ({
            ...item,
            config: (0, video_generator_types_1.parseConfig)(item.configJson),
        })),
        total,
        page,
        pageSize,
    };
}
/**
 * 获取视频项目详情
 */
async function getVideoProject(id) {
    const project = await prisma.videoProject.findUnique({
        where: { id },
    });
    if (!project)
        return null;
    return {
        ...project,
        config: (0, video_generator_types_1.parseConfig)(project.configJson),
    };
}
/**
 * 更新视频项目
 */
async function updateVideoProject(id, data) {
    const updateData = {};
    if (data.title !== undefined)
        updateData.title = data.title;
    if (data.description !== undefined)
        updateData.description = data.description;
    if (data.coverUrl !== undefined)
        updateData.coverUrl = data.coverUrl;
    if (data.config !== undefined)
        updateData.configJson = (0, video_generator_types_1.serializeConfig)(data.config);
    const project = await prisma.videoProject.update({
        where: { id },
        data: updateData,
    });
    return {
        ...project,
        config: (0, video_generator_types_1.parseConfig)(project.configJson),
    };
}
/**
 * 删除视频项目
 */
async function deleteVideoProject(id) {
    try {
        await prisma.videoProject.delete({
            where: { id },
        });
        return true;
    }
    catch {
        return false;
    }
}
// ============ 视频生成 ============
/**
 * 生成视频
 */
async function generateVideoForProject(projectId) {
    // 1. 获取项目
    const project = await getVideoProject(projectId);
    if (!project) {
        return { success: false, error: '项目不存在' };
    }
    // 2. 检查状态
    if (project.status === 'processing') {
        return { success: false, error: '项目正在生成中' };
    }
    // 3. 更新状态为处理中
    await prisma.videoProject.update({
        where: { id: projectId },
        data: { status: 'processing', progress: 0 },
    });
    try {
        // 4. 获取配置
        const config = project.config;
        if (!config) {
            throw new Error('缺少视频配置');
        }
        // 5. 如果有 chapterId，优先从章节获取音频
        let audioPath = config.audio?.url;
        let textContent = '';
        if (project.chapterId) {
            const chapter = await prisma.bookChapter.findUnique({
                where: { id: project.chapterId },
            });
            if (chapter?.audioUrl) {
                audioPath = chapter.audioUrl;
            }
            if (chapter?.content) {
                textContent = chapter.content;
            }
        }
        if (!audioPath || !config.images?.[0]?.url) {
            throw new Error('缺少必要的素材：音频或图片');
        }
        // 转换URL为本地文件路径
        const fs = await import('fs');
        // 获取 server 目录路径
        const serverDir = process.cwd();
        // 处理图片路径
        let imagePath = config.images[0].url;
        // 统一处理：所有非绝对路径都加上 server 目录和 public 前缀
        if (!imagePath.match(/^[A-Za-z]:/)) {
            // Windows 或 Unix 相对路径
            imagePath = path_1.default.join(serverDir, 'public', imagePath.replace(/^\//, ''));
        }
        // 处理音频路径
        if (!audioPath.match(/^[A-Za-z]:/)) {
            audioPath = path_1.default.join(serverDir, audioPath.replace(/^\//, ''));
        }
        // 检查文件是否存在
        if (!fs.existsSync(imagePath)) {
            throw new Error('图片文件不存在: ' + imagePath);
        }
        if (!fs.existsSync(audioPath)) {
            throw new Error('音频文件不存在: ' + audioPath);
        }
        // 6. 生成输出路径
        const outputFileName = `video_${projectId}_${(0, uuid_1.v4)()}.mp4`;
        const outputPath = path_1.default.join(process.cwd(), 'public', 'videos', outputFileName);
        // 7. 更新进度
        await prisma.videoProject.update({
            where: { id: projectId },
            data: { progress: 30 },
        });
        // 8. 生成视频
        let result;
        if (config.bgm?.url) {
            // 带背景音乐
            const bgmPath = await downloadFile(config.bgm.url, 'audio');
            result = await (0, video_generator_ffmpeg_1.generateVideoWithBgm)(imagePath, audioPath, bgmPath, outputPath, config);
        }
        else {
            // 不带背景音乐
            result = await (0, video_generator_ffmpeg_1.generateVideo)(imagePath, audioPath, outputPath, config);
        }
        // 9. 更新项目状态
        const outputUrl = `/videos/${outputFileName}`;
        await prisma.videoProject.update({
            where: { id: projectId },
            data: {
                status: 'completed',
                progress: 100,
                outputUrl,
                duration: result.duration,
                fileSize: result.fileSize,
            },
        });
        // 10. 如果有章节关联，更新章节的视频URL
        if (project.chapterId) {
            await prisma.bookChapter.update({
                where: { id: project.chapterId },
                data: {
                    videoUrl: outputUrl,
                    videoDuration: result.duration,
                },
            });
        }
        return {
            success: true,
            outputUrl,
            duration: result.duration,
            fileSize: result.fileSize,
        };
    }
    catch (error) {
        // 生成失败，更新状态
        await prisma.videoProject.update({
            where: { id: projectId },
            data: {
                status: 'failed',
                errorMsg: error.message,
            },
        });
        return {
            success: false,
            error: error.message,
        };
    }
}
/**
 * 获取生成进度
 */
async function getGenerateProgress(projectId) {
    const project = await prisma.videoProject.findUnique({
        where: { id: projectId },
        select: {
            status: true,
            progress: true,
            outputUrl: true,
            duration: true,
            fileSize: true,
            errorMsg: true,
        },
    });
    if (!project) {
        return { status: 'failed', progress: 0, errorMsg: '项目不存在' };
    }
    return project;
}
// ============ 素材管理 ============
/**
 * 获取素材列表
 */
async function getMaterials(query) {
    const page = query.page || 1;
    const pageSize = query.pageSize || 20;
    const skip = (page - 1) * pageSize;
    const where = {
        OR: [{ userId: null }, { userId: query.userId || 0 }],
    };
    if (query.type)
        where.type = query.type;
    if (query.category)
        where.category = query.category;
    const [items, total] = await Promise.all([
        prisma.videoMaterial.findMany({
            where,
            orderBy: { createdAt: 'desc' },
            skip,
            take: pageSize,
        }),
        prisma.videoMaterial.count({ where }),
    ]);
    return {
        items: items.map((item) => ({
            ...item,
            tags: (0, video_generator_types_1.parseTags)(item.tags),
        })),
        total,
    };
}
/**
 * 上传素材
 */
async function uploadMaterial(data, userId) {
    const material = await prisma.videoMaterial.create({
        data: {
            userId: userId,
            type: data.type,
            name: data.name,
            url: data.url,
            thumbnail: data.thumbnail,
            tags: (0, video_generator_types_1.serializeTags)(data.tags || []),
            category: data.category,
            duration: data.duration,
            size: data.size,
            width: data.width,
            height: data.height,
        },
    });
    return {
        ...material,
        tags: (0, video_generator_types_1.parseTags)(material.tags),
    };
}
/**
 * 删除素材
 */
async function deleteMaterial(id) {
    try {
        await prisma.videoMaterial.delete({
            where: { id },
        });
        return true;
    }
    catch {
        return false;
    }
}
// ============ 辅助函数 ============
/**
 * 下载文件到临时目录
 */
async function downloadFile(url, type) {
    const https = await import('https');
    const http = await import('http');
    const fs = await import('fs');
    const tempDir = path_1.default.join(process.cwd(), 'temp', type);
    if (!fs.existsSync(tempDir)) {
        fs.mkdirSync(tempDir, { recursive: true });
    }
    const ext = path_1.default.extname(url) || (type === 'images' ? '.jpg' : '.mp3');
    const filePath = path_1.default.join(tempDir, `${(0, uuid_1.v4)()}${ext}`);
    return new Promise((resolve, reject) => {
        const protocol = url.startsWith('https') ? https : http;
        const file = fs.createWriteStream(filePath);
        protocol.get(url, (response) => {
            if (response.statusCode === 301 || response.statusCode === 302) {
                // 重定向
                const redirectUrl = response.headers.location;
                if (!redirectUrl) {
                    file.close();
                    reject(new Error('重定向但没有 location header'));
                    return;
                }
                const redirectProtocol = redirectUrl.startsWith('https') ? https : http;
                redirectProtocol.get(redirectUrl, (redirectResponse) => {
                    redirectResponse.pipe(file);
                    file.on('finish', () => resolve(filePath));
                });
            }
            else {
                response.pipe(file);
                file.on('finish', () => resolve(filePath));
            }
        }).on('error', reject);
    });
}
/**
 * 清理临时文件
 */
async function cleanupTempFiles(...filePaths) {
    const fs = await import('fs');
    for (const filePath of filePaths) {
        try {
            if (fs.existsSync(filePath)) {
                fs.unlinkSync(filePath);
            }
        }
        catch (error) {
            console.error(`清理文件失败: ${filePath}`, error);
        }
    }
}
/**
 * 从书籍生成视频项目（基于指定章节）
 */
async function createVideoProjectFromBook(bookId, chapterId, userId) {
    // 获取书籍信息
    const book = await prisma.book.findUnique({
        where: { id: bookId },
        include: { chapters: { orderBy: { number: 'asc' } } },
    });
    if (!book)
        return null;
    // 如果没有指定章节，使用第一个有音频的章节
    let targetChapter = book.chapters.find(c => c.audioUrl) || book.chapters[0];
    if (chapterId) {
        const found = book.chapters.find(c => c.id === chapterId);
        if (found)
            targetChapter = found;
    }
    if (!targetChapter)
        return null;
    // 如果章节没有音频，返回错误
    if (!targetChapter.audioUrl) {
        console.error(`章节 ${targetChapter.number} 没有音频`);
        return null;
    }
    // 创建视频项目，直接关联到章节
    const project = await createVideoProject({
        title: `《${book.title}》第${targetChapter.number}章 视频`,
        description: targetChapter.summary || book.description,
        bookId: book.id,
        chapterId: targetChapter.id,
        config: {
            ...video_generator_types_1.PRESET_VIDEO_CONFIGS.portrait,
            audio: {
                url: targetChapter.audioUrl,
                volume: 1,
            },
            images: [
                {
                    url: '/images/default-cover.jpg',
                    duration: 5,
                    transition: 'fade',
                },
            ],
            subtitle: {
                text: targetChapter.title,
                position: 'bottom',
                fontSize: 28,
                fontColor: 'white',
            },
        },
    }, userId);
    return project;
}
