diff --git a/Models/VideoModel.cs b/Models/VideoModel.cs index e37b457..81856ef 100644 --- a/Models/VideoModel.cs +++ b/Models/VideoModel.cs @@ -18,26 +18,49 @@ namespace VideoConcat.Models public class VideoModel : INotifyPropertyChanged { private int _num; + private int _maxNum; private string _folderPath = ""; + private string _auditImagePath = ""; private bool _canStart = false; private ObservableCollection _FolderInfos = []; + public int Num { - get { return _num; } + get => _num; set { _num = value; OnPropertyChanged(); + UpdateSum(); + } + } + + public string AuditImagePath + { + get => _auditImagePath; + set + { + _auditImagePath = value; + OnPropertyChanged(); + } + } + + public int MaxNum + { + get => _maxNum; + set + { + _maxNum = value; } } public struct FolderInfo { - public DirectoryInfo DirectoryInfo{set;get;} + public DirectoryInfo DirectoryInfo { set; get; } public int Num { set; get; } - public List VideoPaths { set; get; } + public List VideoPaths { set; get; } } public ObservableCollection FolderInfos @@ -81,11 +104,12 @@ namespace VideoConcat.Models public ICommand? BtnOpenFolderCommand { get; set; } public ICommand? BtnStartVideoConcatCommand { get; set; } + public ICommand? BtnChooseAuditImageCommand { get; set; } public event PropertyChangedEventHandler? PropertyChanged; - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } @@ -98,17 +122,35 @@ namespace VideoConcat.Models public void UpdateSum() { int _temp = 1; - foreach (var item in FolderInfos) + if (FolderInfos.Count > 0) { - if (item.Num > 0) + foreach (FolderInfo item in FolderInfos) { - _temp *= item.Num; + if (item.Num > 0) + { + _temp *= item.Num; + } } + MaxNum = _temp; + SetCanStart(); } - Num = _temp; - if(Num > 0){ + else + { + MaxNum = 0; + SetCanStart(); + } + } + + public void SetCanStart() + { + if (Num > 0 && Num <= MaxNum) + { CanStart = true; } + else + { + CanStart = false; + } } } } diff --git a/ViewModels/VideoViewModel.cs b/ViewModels/VideoViewModel.cs index f8d62a2..1c35ba1 100644 --- a/ViewModels/VideoViewModel.cs +++ b/ViewModels/VideoViewModel.cs @@ -1,18 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Input; +using System.Windows.Input; using VideoConcat.Models; -using System.Windows; -using System.Windows.Forms; using MessageBox = System.Windows.MessageBox; -using System.ComponentModel; using VideoConcat.Common.Tools; using System.IO; using Microsoft.Expression.Drawing.Core; using FFMpegCore; +using FFMpegCore.Arguments; namespace VideoConcat.ViewModels { @@ -32,54 +25,159 @@ namespace VideoConcat.ViewModels public VideoViewModel() { - VideoModel = new VideoModel(); - VideoModel.BtnOpenFolderCommand = new BtnOpenFolderCommand() + VideoModel = new VideoModel { - DoExcue = obj => + FolderInfos = [], + MaxNum = 0, + CanStart = false, + BtnOpenFolderCommand = new Command() { - FolderBrowserDialog folderBrowserDialog = new(); - if (folderBrowserDialog.ShowDialog() == DialogResult.OK) + DoExcue = obj => { - VideoModel.FolderPath = folderBrowserDialog.SelectedPath; - LogUtils.Info($"获取视频文件夹,视频路径:{VideoModel.FolderPath}"); - ListFolder(VideoModel.FolderPath); - } - } - }; - - VideoModel.BtnStartVideoConcatCommand = new BtnStartVideoConcatCommand() - { - DoExcue = obj => - { - LogUtils.Info("开始合并视频"); - List> combinations = []; - List currentCombination = []; - List> videoLists = []; - - - VideoModel.FolderInfos.ForEach(folderInfo => - { - videoLists.Add(folderInfo.VideoPaths); - }); - - VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations); - - int combinationIndex = 1; - foreach (List combination in combinations) - { - Console.Write($"组合 {combinationIndex++}: "); - foreach (string video in combination) + FolderBrowserDialog folderBrowserDialog = new(); + if (folderBrowserDialog.ShowDialog() == DialogResult.OK) { - Console.Write(video + " "); + VideoModel.FolderPath = folderBrowserDialog.SelectedPath; + LogUtils.Info($"获取视频文件夹,视频路径:{VideoModel.FolderPath}"); + ListFolder(VideoModel.FolderPath); } - Console.WriteLine(); - FFMpeg.Join("./1.mp4",combination.ToArray()); } + }, + BtnChooseAuditImageCommand = new Command() + { + DoExcue = obj => + { + // 创建一个 OpenFileDialog 实例 + OpenFileDialog openFileDialog = new() + { + // 设置文件对话框的标题 + Title = "选择广审图片", + // 设置文件筛选器,只允许选择文本文件和图像文件 + Filter = "图片文件[*.png;*.jpg;*.jpeg;*.bmp]|*.png;*.jpg;*.jpeg;*.bmp", + }; - Console.WriteLine($"总共有 {combinations.Count} 种拼接方案。"); - + // 显示文件对话框并获取结果 + DialogResult result = openFileDialog.ShowDialog(); + + + // 检查用户是否点击了打开按钮 + if (result == DialogResult.OK) + { + // 获取用户选择的文件路径列表 + VideoModel.AuditImagePath = openFileDialog.FileName; + } + } + }, + + BtnStartVideoConcatCommand = new Command() + { + DoExcue = obj => + { + Task.Run(action: () => + { + //开始时间 + DateTime startTime = DateTime.Now; + + LogUtils.Info("开始合并视频"); + List> combinations = []; + List currentCombination = []; + List> videoLists = []; + + + List taskList = []; + + VideoModel.FolderInfos.ForEach(folderInfo => + { + videoLists.Add(folderInfo.VideoPaths); + }); + + VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations); + + + List> result = []; + Random random = new(); + + + // 复制原列表,避免修改原列表 + List> tempList = new(combinations); + + + for (int i = 0; i < VideoModel.Num && tempList.Count > 0; i++) + { + int index = random.Next(tempList.Count); + result.Add(tempList[index]); + tempList.RemoveAt(index); + } + + + foreach (List combination in result) + { + taskList.Add(Task.Run(() => + { + try + { + if (Directory.Exists($"{VideoModel.FolderPath}/output") == false) + { + Directory.CreateDirectory($"{VideoModel.FolderPath}/output"); + } + string _outPutName = $"{VideoModel.FolderPath}/output/{random.Next(100000, 999999)}.mp4"; + + bool _isSuccess = FFMpeg.Join(_outPutName, [.. combination]); + + + if (_isSuccess && VideoModel.AuditImagePath != "") + { + // 使用 FFMpegCore 执行添加图片到视频的操作 + try + { + // 配置 FFmpeg 二进制文件位置(如果 FFmpeg 不在系统路径中) + // GlobalFFOptions.Configure(new FFOptions { BinaryFolder = "path/to/ffmpeg/bin" }); + string _outPutNameImg = $"{VideoModel.FolderPath}/output/{random.Next(100000, 999999)}.mp4"; + + + // 使用 FFMpegArguments 构建命令 + bool _isCoverSuccess = FFMpegArguments + .FromFileInput(_outPutName) + .AddFileInput(VideoModel.AuditImagePath) + .OutputToFile( + _outPutNameImg, + true, + options => options.WithCustomArgument("-filter_complex \"[0:v][1:v] overlay=0:0\" ") + ) + .ProcessSynchronously(); + + + LogUtils.Info($"图片已成功添加到视频中,输出文件:{_outPutName}"); + } + catch (Exception ex) + { + LogUtils.Error($"图片添加到视频中失败", ex); + } + } + + + LogUtils.Info($"当前视频{string.Join(",", combination)}合并成功"); + } + catch (Exception ex) + { + LogUtils.Error($"视频{string.Join(",", combination)}合并失败", ex); + } + + })); + + } + + + Task.WhenAll(taskList).ContinueWith((s) => + { + //结束时间 + DateTime endTime = DateTime.Now; + LogUtils.Info($"所有视频拼接完成,用时{(endTime - startTime).TotalSeconds}秒"); + }); + }); + } } }; } @@ -95,10 +193,13 @@ namespace VideoConcat.ViewModels //获取文件夹下所有视频文件 foreach (DirectoryInfo Folder in folders) { - string[] files = Directory.GetFiles(Folder.FullName, "*.mp4"); - LogUtils.Info($"{Folder.Name}下有{files.Length}个视频文件"); + if (Folder.Name != "output") + { + string[] files = Directory.GetFiles(Folder.FullName, "*.mp4"); + LogUtils.Info($"{Folder.Name}下有{files.Length}个视频文件"); - VideoModel.FolderInfos.Add(new VideoModel.FolderInfo { DirectoryInfo = Folder, Num = files.Length, VideoPaths = new List(files) }); + VideoModel.FolderInfos.Add(new VideoModel.FolderInfo { DirectoryInfo = Folder, Num = files.Length, VideoPaths = new List(files) }); + } } VideoModel.UpdateSum(); } @@ -110,20 +211,7 @@ namespace VideoConcat.ViewModels } } - class BtnOpenFolderCommand : ICommand - { - public event EventHandler? CanExecuteChanged; - - public bool CanExecute(object? parameter) => true; - - public void Execute(object? parameter) - { - DoExcue?.Invoke(parameter); - } - - public Action? DoExcue { get; set; } - } - class BtnStartVideoConcatCommand : ICommand + class Command : ICommand { public event EventHandler? CanExecuteChanged; diff --git a/Views/MainWindow.xaml.cs b/Views/MainWindow.xaml.cs index 9b28704..ac92a59 100644 --- a/Views/MainWindow.xaml.cs +++ b/Views/MainWindow.xaml.cs @@ -21,9 +21,16 @@ namespace VideoConcat.Views { if (Username.Text == "admin" && Password.Password == "123456") { - + new Video().Show(); + Close(); } - new Video().Show(); + else + { + System.Windows.MessageBox.Show("用户名或者密码错误!"); + Username.Clear(); + Password.Clear(); + } + } } } diff --git a/Views/Video.xaml b/Views/Video.xaml index 92d02f2..0a696bb 100644 --- a/Views/Video.xaml +++ b/Views/Video.xaml @@ -7,7 +7,7 @@ xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers" xmlns:local="clr-namespace:VideoConcat.Views" mc:Ignorable="d" - Title="视频拼接" Height="600" Width="800" WindowStartupLocation="CenterScreen" ResizeMode="NoResize"> + Title="视频拼接" Height="900" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"> @@ -22,7 +22,10 @@ - + + + + + + + +