Compare commits
10 Commits
d1787dc7e6
...
0786589239
| Author | SHA1 | Date | |
|---|---|---|---|
| 0786589239 | |||
| ba41c6dad4 | |||
| d3fc2368c8 | |||
| 3154a9f948 | |||
| c93a2ce892 | |||
| fefb3b8990 | |||
| 637e5b2969 | |||
| 6fa231b010 | |||
|
|
baf1592fc5 | ||
|
|
ec0ac61133 |
127
Common/Api/Base/LoginResponse.cs
Normal file
127
Common/Api/Base/LoginResponse.cs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using System.Globalization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
using VideoConcat.Common.Api.Common;
|
||||||
|
|
||||||
|
namespace VideoConcat.Common.Api.Base
|
||||||
|
{
|
||||||
|
|
||||||
|
public partial class UserLoginResponse
|
||||||
|
{
|
||||||
|
[JsonProperty("user")]
|
||||||
|
public User User { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("token")]
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("expiresAt")]
|
||||||
|
public long ExpiresAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class User
|
||||||
|
{
|
||||||
|
[JsonProperty("ID")]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("CreatedAt")]
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("UpdatedAt")]
|
||||||
|
public DateTimeOffset UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("uuid")]
|
||||||
|
public Guid Uuid { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("userName")]
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("nickName")]
|
||||||
|
public string NickName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("headerImg")]
|
||||||
|
public Uri HeaderImg { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("authorityId")]
|
||||||
|
public long AuthorityId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("authority")]
|
||||||
|
public Authority Authority { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("authorities")]
|
||||||
|
public List<Authority> Authorities { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("phone")]
|
||||||
|
public string Phone { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("enable")]
|
||||||
|
public long Enable { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("originSetting")]
|
||||||
|
public object OriginSetting { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Authority
|
||||||
|
{
|
||||||
|
[JsonProperty("CreatedAt")]
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("UpdatedAt")]
|
||||||
|
public DateTimeOffset UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("DeletedAt")]
|
||||||
|
public object DeletedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("authorityId")]
|
||||||
|
public long AuthorityId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("authorityName")]
|
||||||
|
public string AuthorityName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("parentId")]
|
||||||
|
public long ParentId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("dataAuthorityId")]
|
||||||
|
public object DataAuthorityId { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("children")]
|
||||||
|
public object Children { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("menus")]
|
||||||
|
public object Menus { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("defaultRouter")]
|
||||||
|
public string DefaultRouter { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class UserLoginResponse
|
||||||
|
{
|
||||||
|
public static UserLoginResponse FromJson(string json)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<UserLoginResponse>(json, Converter.Settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Serialize
|
||||||
|
{
|
||||||
|
public static string ToJson(this UserLoginResponse self) => JsonConvert.SerializeObject(self, Converter.Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Converter
|
||||||
|
{
|
||||||
|
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
|
||||||
|
DateParseHandling = DateParseHandling.None,
|
||||||
|
Converters =
|
||||||
|
{
|
||||||
|
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Common/Api/Base/SystemApi.cs
Normal file
16
Common/Api/Base/SystemApi.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
using VideoConcat.Common.Tools;
|
||||||
|
|
||||||
|
namespace VideoConcat.Common.Api.Base
|
||||||
|
{
|
||||||
|
public class SystemApi
|
||||||
|
{
|
||||||
|
public static async Task<ApiResponse<UserLoginResponse>> LoginAsync<UserLoginResponse>(string username, string password)
|
||||||
|
{
|
||||||
|
HttpHelper Http = new();
|
||||||
|
ApiResponse<UserLoginResponse> res = await Http.PostAsync<UserLoginResponse>("/api/base/login", new { Username = username, Password = password, Platform = "pc" });
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Common/Api/Common/Common.cs
Normal file
22
Common/Api/Common/Common.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VideoConcat.Common.Api.Common
|
||||||
|
{
|
||||||
|
public class CommonResponese<T>
|
||||||
|
{
|
||||||
|
|
||||||
|
[JsonProperty("code")]
|
||||||
|
public int Code { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("data")]
|
||||||
|
public T Data { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("msg")]
|
||||||
|
public string Msg { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Common/Tools/HttpUtils.cs
Normal file
80
Common/Tools/HttpUtils.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace VideoConcat.Common.Tools
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// HTTP 请求封装类
|
||||||
|
public class HttpHelper
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
public HttpHelper()
|
||||||
|
{
|
||||||
|
|
||||||
|
_httpClient = new HttpClient
|
||||||
|
{
|
||||||
|
BaseAddress = new Uri("https://admin.xiangbing.vip")
|
||||||
|
};
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送 GET 请求
|
||||||
|
public async Task<ApiResponse<T>> GetAsync<T>(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<ApiResponse<T>>(content);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new ApiResponse<T>
|
||||||
|
{
|
||||||
|
Code = 500,
|
||||||
|
Msg = $"请求出错: {ex.Message}",
|
||||||
|
Data = default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送 POST 请求
|
||||||
|
public async Task<ApiResponse<T>> PostAsync<T>(string url, object data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(data);
|
||||||
|
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
|
var response = await _httpClient.PostAsync(url, content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<ApiResponse<T>>(responseContent);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new ApiResponse<T>
|
||||||
|
{
|
||||||
|
Code = 500,
|
||||||
|
Msg = $"请求出错: {ex.Message}",
|
||||||
|
Data = default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ApiResponse<T>
|
||||||
|
{
|
||||||
|
public int Code { get; set; }
|
||||||
|
public T Data { get; set; }
|
||||||
|
public string Msg { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,15 @@
|
|||||||
using System;
|
using FFMpegCore.Enums;
|
||||||
using System.Collections.Generic;
|
using FFMpegCore.Helpers;
|
||||||
using System.Linq;
|
using FFMpegCore;
|
||||||
using System.Text;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace VideoConcat.Common.Tools
|
namespace VideoConcat.Common.Tools
|
||||||
{
|
{
|
||||||
internal class VideoCombine
|
internal class VideoCombine
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 生成所有组合
|
||||||
|
/// </summary>
|
||||||
public static void GenerateCombinations(List<List<string>> videoLists, int index, List<string> currentCombination, List<List<string>> result)
|
public static void GenerateCombinations(List<List<string>> videoLists, int index, List<string> currentCombination, List<List<string>> result)
|
||||||
{
|
{
|
||||||
if (index == videoLists.Count)
|
if (index == videoLists.Count)
|
||||||
@ -24,5 +26,37 @@ namespace VideoConcat.Common.Tools
|
|||||||
currentCombination.RemoveAt(currentCombination.Count - 1);
|
currentCombination.RemoveAt(currentCombination.Count - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理临时文件
|
||||||
|
/// </summary>
|
||||||
|
public static void Cleanup(List<string> pathList)
|
||||||
|
{
|
||||||
|
foreach (var path in pathList)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将视频文件转换为ts格式
|
||||||
|
/// </summary>
|
||||||
|
public static string ConvertVideos(string videoPath)
|
||||||
|
{
|
||||||
|
|
||||||
|
var video = FFProbe.Analyse(videoPath);
|
||||||
|
FFMpegHelper.ConversionSizeExceptionCheck(video);
|
||||||
|
|
||||||
|
string _tempPath = Path.GetDirectoryName(videoPath) ?? "";
|
||||||
|
|
||||||
|
//GlobalFFOptions.Current.TemporaryFilesFolder
|
||||||
|
var destinationPath = Path.Combine(_tempPath, $"{Path.GetFileNameWithoutExtension(videoPath)}{FileExtension.Ts}");
|
||||||
|
//Directory.CreateDirectory(GlobalFFOptions.Current.TemporaryFilesFolder);
|
||||||
|
FFMpeg.Convert(videoPath, destinationPath, VideoType.Ts);
|
||||||
|
return destinationPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Threading;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Header;
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Header;
|
||||||
|
|
||||||
@ -18,28 +19,98 @@ namespace VideoConcat.Models
|
|||||||
public class VideoModel : INotifyPropertyChanged
|
public class VideoModel : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private int _num;
|
private int _num;
|
||||||
|
private int _maxNum;
|
||||||
private string _folderPath = "";
|
private string _folderPath = "";
|
||||||
|
private string _auditImagePath = "";
|
||||||
private bool _canStart = false;
|
private bool _canStart = false;
|
||||||
|
private bool _isCanOperate=false;
|
||||||
|
private bool _isStart = false;
|
||||||
private ObservableCollection<FolderInfo> _FolderInfos = [];
|
private ObservableCollection<FolderInfo> _FolderInfos = [];
|
||||||
|
private ObservableCollection<ConcatVideo> _concatVideos = [];
|
||||||
|
private Dispatcher _dispatcher;
|
||||||
|
|
||||||
|
public static int Index = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public bool IsCanOperate
|
||||||
|
{
|
||||||
|
get => _isCanOperate;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isCanOperate = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsStart
|
||||||
|
{
|
||||||
|
get => _isStart;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isStart = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
SetCanStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dispatcher Dispatcher
|
||||||
|
{
|
||||||
|
get => _dispatcher;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_dispatcher = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int Num
|
public int Num
|
||||||
{
|
{
|
||||||
get { return _num; }
|
get => _num;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_num = value;
|
_num = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
|
UpdateSum();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AuditImagePath
|
||||||
|
{
|
||||||
|
get => _auditImagePath;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_auditImagePath = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MaxNum
|
||||||
|
{
|
||||||
|
get => _maxNum;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_maxNum = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct FolderInfo
|
public struct FolderInfo
|
||||||
{
|
{
|
||||||
public DirectoryInfo DirectoryInfo{set;get;}
|
public DirectoryInfo DirectoryInfo { set; get; }
|
||||||
public int Num { set; get; }
|
public int Num { set; get; }
|
||||||
|
|
||||||
public List<string> VideoPaths { set; get; }
|
public List<string> VideoPaths { set; get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ConcatVideo
|
||||||
|
{
|
||||||
|
public int Index { set; get; }
|
||||||
|
public string FileName { set; get; } = "";
|
||||||
|
public string Size { set; get; } = "";
|
||||||
|
public int Seconds { set; get; }
|
||||||
|
public string Status { set; get; } = "";
|
||||||
|
public string Progress { set; get; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<FolderInfo> FolderInfos
|
public ObservableCollection<FolderInfo> FolderInfos
|
||||||
{
|
{
|
||||||
get { return _FolderInfos; }
|
get { return _FolderInfos; }
|
||||||
@ -59,6 +130,26 @@ namespace VideoConcat.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ObservableCollection<ConcatVideo> ConcatVideos
|
||||||
|
{
|
||||||
|
get { return _concatVideos; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_concatVideos != null)
|
||||||
|
{
|
||||||
|
_concatVideos.CollectionChanged -= ItemList_CollectionChanged;
|
||||||
|
}
|
||||||
|
_concatVideos = value;
|
||||||
|
if (_concatVideos != null)
|
||||||
|
{
|
||||||
|
_concatVideos.CollectionChanged += ItemList_CollectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string FolderPath
|
public string FolderPath
|
||||||
{
|
{
|
||||||
get { return _folderPath; }
|
get { return _folderPath; }
|
||||||
@ -81,11 +172,12 @@ namespace VideoConcat.Models
|
|||||||
|
|
||||||
public ICommand? BtnOpenFolderCommand { get; set; }
|
public ICommand? BtnOpenFolderCommand { get; set; }
|
||||||
public ICommand? BtnStartVideoConcatCommand { get; set; }
|
public ICommand? BtnStartVideoConcatCommand { get; set; }
|
||||||
|
public ICommand? BtnChooseAuditImageCommand { get; set; }
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
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));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
}
|
}
|
||||||
@ -98,17 +190,42 @@ namespace VideoConcat.Models
|
|||||||
public void UpdateSum()
|
public void UpdateSum()
|
||||||
{
|
{
|
||||||
int _temp = 1;
|
int _temp = 1;
|
||||||
foreach (var item in FolderInfos)
|
if (FolderInfos.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (FolderInfo item in FolderInfos)
|
||||||
{
|
{
|
||||||
if (item.Num > 0)
|
if (item.Num > 0)
|
||||||
{
|
{
|
||||||
_temp *= item.Num;
|
_temp *= item.Num;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Num = _temp;
|
MaxNum = _temp;
|
||||||
if(Num > 0){
|
SetCanStart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MaxNum = 0;
|
||||||
|
SetCanStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCanStart()
|
||||||
|
{
|
||||||
|
if (Num > 0 && Num <= MaxNum && IsStart == false)
|
||||||
|
{
|
||||||
CanStart = true;
|
CanStart = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CanStart = false;
|
||||||
|
}
|
||||||
|
IsCanOperate = !IsStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VideoModel()
|
||||||
|
{
|
||||||
|
ConcatVideos = [];
|
||||||
|
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<ApplicationIcon>视频.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -15,6 +16,7 @@
|
|||||||
<PackageReference Include="log4net" Version="3.0.2" />
|
<PackageReference Include="log4net" Version="3.0.2" />
|
||||||
<PackageReference Include="MaterialDesignXaml.DialogsHelper" Version="1.0.4" />
|
<PackageReference Include="MaterialDesignXaml.DialogsHelper" Version="1.0.4" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
|
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
|
||||||
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.0" />
|
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
||||||
|
|||||||
@ -5,6 +5,8 @@ VisualStudioVersion = 17.11.35327.3
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VideoConcat", "VideoConcat.csproj", "{2FF5691C-3184-4B68-944B-C704E64C4E4E}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VideoConcat", "VideoConcat.csproj", "{2FF5691C-3184-4B68-944B-C704E64C4E4E}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "视频", "..\视频\视频.vdproj", "{6253EBA0-190A-4A7D-AC55-954172807A46}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -15,6 +17,8 @@ Global
|
|||||||
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
{2FF5691C-3184-4B68-944B-C704E64C4E4E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6253EBA0-190A-4A7D-AC55-954172807A46}.Debug|Any CPU.ActiveCfg = Debug
|
||||||
|
{6253EBA0-190A-4A7D-AC55-954172807A46}.Release|Any CPU.ActiveCfg = Release
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@ -1,24 +1,23 @@
|
|||||||
using System;
|
using System.Windows.Input;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using VideoConcat.Models;
|
using VideoConcat.Models;
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using MessageBox = System.Windows.MessageBox;
|
using MessageBox = System.Windows.MessageBox;
|
||||||
using System.ComponentModel;
|
|
||||||
using VideoConcat.Common.Tools;
|
using VideoConcat.Common.Tools;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Expression.Drawing.Core;
|
using Microsoft.Expression.Drawing.Core;
|
||||||
using FFMpegCore;
|
using FFMpegCore;
|
||||||
|
using FFMpegCore.Enums;
|
||||||
|
using static VideoConcat.Models.VideoModel;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace VideoConcat.ViewModels
|
namespace VideoConcat.ViewModels
|
||||||
{
|
{
|
||||||
public class VideoViewModel
|
public class VideoViewModel
|
||||||
{
|
{
|
||||||
private VideoModel _videoModel;
|
private VideoModel _videoModel;
|
||||||
|
|
||||||
|
public List<ConcatVideo> ConcatVideos { get; set; } = [];
|
||||||
|
|
||||||
public VideoModel VideoModel
|
public VideoModel VideoModel
|
||||||
{
|
{
|
||||||
get { return _videoModel; }
|
get { return _videoModel; }
|
||||||
@ -32,8 +31,15 @@ namespace VideoConcat.ViewModels
|
|||||||
|
|
||||||
public VideoViewModel()
|
public VideoViewModel()
|
||||||
{
|
{
|
||||||
VideoModel = new VideoModel();
|
VideoModel = new VideoModel
|
||||||
VideoModel.BtnOpenFolderCommand = new BtnOpenFolderCommand()
|
{
|
||||||
|
FolderInfos = [],
|
||||||
|
ConcatVideos = [],
|
||||||
|
MaxNum = 0,
|
||||||
|
CanStart = false,
|
||||||
|
IsStart = false,
|
||||||
|
|
||||||
|
BtnOpenFolderCommand = new Command()
|
||||||
{
|
{
|
||||||
DoExcue = obj =>
|
DoExcue = obj =>
|
||||||
{
|
{
|
||||||
@ -45,18 +51,62 @@ namespace VideoConcat.ViewModels
|
|||||||
ListFolder(VideoModel.FolderPath);
|
ListFolder(VideoModel.FolderPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
VideoModel.BtnStartVideoConcatCommand = new BtnStartVideoConcatCommand()
|
},
|
||||||
|
BtnChooseAuditImageCommand = new Command()
|
||||||
{
|
{
|
||||||
DoExcue = obj =>
|
DoExcue = obj =>
|
||||||
{
|
{
|
||||||
LogUtils.Info("开始合并视频");
|
// 创建一个 OpenFileDialog 实例
|
||||||
|
OpenFileDialog openFileDialog = new()
|
||||||
|
{
|
||||||
|
// 设置文件对话框的标题
|
||||||
|
Title = "选择广审图片",
|
||||||
|
// 设置文件筛选器,只允许选择文本文件和图像文件
|
||||||
|
Filter = "图片文件[*.png;*.jpg;*.jpeg;*.bmp]|*.png;*.jpg;*.jpeg;*.bmp",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 显示文件对话框并获取结果
|
||||||
|
DialogResult result = openFileDialog.ShowDialog();
|
||||||
|
|
||||||
|
|
||||||
|
// 检查用户是否点击了打开按钮
|
||||||
|
if (result == DialogResult.OK)
|
||||||
|
{
|
||||||
|
// 获取用户选择的文件路径列表
|
||||||
|
VideoModel.AuditImagePath = openFileDialog.FileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
BtnStartVideoConcatCommand = new Command()
|
||||||
|
{
|
||||||
|
DoExcue = obj =>
|
||||||
|
{
|
||||||
|
Task.Run(action: async () =>
|
||||||
|
{
|
||||||
|
VideoModel.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
VideoModel.ConcatVideos.Clear();
|
||||||
|
VideoModel.IsStart = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Directory.Exists($"{VideoModel.FolderPath}\\output") == false)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory($"{VideoModel.FolderPath}\\output");
|
||||||
|
}
|
||||||
|
|
||||||
|
//开始时间
|
||||||
|
DateTime startTime = DateTime.Now;
|
||||||
|
|
||||||
|
LogUtils.Info("开始合并视频,进行视频拼接组合");
|
||||||
List<List<string>> combinations = [];
|
List<List<string>> combinations = [];
|
||||||
List<string> currentCombination = [];
|
List<string> currentCombination = [];
|
||||||
List<List<string>> videoLists = [];
|
List<List<string>> videoLists = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
VideoModel.FolderInfos.ForEach(folderInfo =>
|
VideoModel.FolderInfos.ForEach(folderInfo =>
|
||||||
{
|
{
|
||||||
videoLists.Add(folderInfo.VideoPaths);
|
videoLists.Add(folderInfo.VideoPaths);
|
||||||
@ -64,22 +114,174 @@ namespace VideoConcat.ViewModels
|
|||||||
|
|
||||||
VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations);
|
VideoCombine.GenerateCombinations(videoLists, 0, currentCombination, combinations);
|
||||||
|
|
||||||
int combinationIndex = 1;
|
|
||||||
foreach (List<string> combination in combinations)
|
List<List<string>> result = [];
|
||||||
|
Random random = new();
|
||||||
|
|
||||||
|
|
||||||
|
// 复制原列表,避免修改原列表
|
||||||
|
List<List<string>> tempList = new(combinations);
|
||||||
|
|
||||||
|
string[] _converVideoPath = [];
|
||||||
|
List<string> _clearPath = [];
|
||||||
|
|
||||||
|
for (int i = 0; i < VideoModel.Num && tempList.Count > 0; i++)
|
||||||
{
|
{
|
||||||
Console.Write($"组合 {combinationIndex++}: ");
|
int index = random.Next(tempList.Count);
|
||||||
foreach (string video in combination)
|
result.Add(tempList[index]);
|
||||||
{
|
|
||||||
Console.Write(video + " ");
|
_converVideoPath = [.. _converVideoPath, .. tempList[index]];
|
||||||
|
|
||||||
|
tempList.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
SemaphoreSlim semaphore = new(10); // Limit to 3 threads
|
||||||
|
|
||||||
|
List<Task> _tasks = [];
|
||||||
|
|
||||||
|
foreach (var _path in _converVideoPath)
|
||||||
|
{
|
||||||
|
await semaphore.WaitAsync(); // Wait when more than 3 threads are running
|
||||||
|
var _task = Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_clearPath.Add(VideoCombine.ConvertVideos(_path));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphore.Release(); // Work is done, signal to semaphore that more work is possible
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_tasks.Add(_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(_tasks).ContinueWith((task) =>
|
||||||
|
{
|
||||||
|
LogUtils.Info($"转换完成,用时{(DateTime.Now - startTime).TotalSeconds}秒");
|
||||||
|
});
|
||||||
|
|
||||||
|
LogUtils.Info("开始拼接视频");
|
||||||
|
|
||||||
|
|
||||||
|
List<Task> taskList = [];
|
||||||
|
semaphore = new(10);
|
||||||
|
|
||||||
|
foreach (List<string> combination in result)
|
||||||
|
{
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
var _task = Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string _tempFileName = $"{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4";
|
||||||
|
string _outPutName = Path.Combine($"{VideoModel.FolderPath}", "output", _tempFileName); ;
|
||||||
|
|
||||||
|
|
||||||
|
var temporaryVideoParts = combination.Select((_videoPath) =>
|
||||||
|
{
|
||||||
|
string _tempPath = Path.GetDirectoryName(_videoPath) ?? "";
|
||||||
|
//GlobalFFOptions.Current.TemporaryFilesFolder
|
||||||
|
return Path.Combine(_tempPath, $"{Path.GetFileNameWithoutExtension(_videoPath)}{FileExtension.Ts}");
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
bool _isSuccess = false;
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isSuccess = FFMpegArguments
|
||||||
|
.FromConcatInput(temporaryVideoParts)
|
||||||
|
.OutputToFile(_outPutName, true, options => options
|
||||||
|
.CopyChannel()
|
||||||
|
.WithBitStreamFilter(Channel.Audio, Filter.Aac_AdtstoAsc))
|
||||||
|
.ProcessSynchronously();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_isSuccess = false;
|
||||||
|
LogUtils.Error("拼接视频失败", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
//bool _isSuccess = FFMpeg.Join(_outPutName, [.. combination]);
|
||||||
|
|
||||||
|
VideoModel.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
IMediaAnalysis _mediaInfo = FFProbe.Analyse(_outPutName);
|
||||||
|
FileInfo fileInfo = new(_outPutName);
|
||||||
|
|
||||||
|
VideoModel.ConcatVideos.Add(new ConcatVideo()
|
||||||
|
{
|
||||||
|
Index = VideoModel.ConcatVideos.Count + 1,
|
||||||
|
FileName = _tempFileName,
|
||||||
|
Size = $"{fileInfo.Length / 1024 / 1024}MB",
|
||||||
|
Seconds = ((int)_mediaInfo.Duration.TotalSeconds),
|
||||||
|
Status = _isSuccess ? "拼接成功" : "拼接失败",
|
||||||
|
Progress = "100%",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (_isSuccess && VideoModel.AuditImagePath != "")
|
||||||
|
{
|
||||||
|
// 使用 FFMpegCore 执行添加图片到视频的操作
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 配置 FFmpeg 二进制文件位置(如果 FFmpeg 不在系统路径中)
|
||||||
|
// GlobalFFOptions.Configure(new FFOptions { BinaryFolder = "path/to/ffmpeg/bin" });
|
||||||
|
|
||||||
|
string _outPutNameImg = $"{VideoModel.FolderPath}\\output\\{DateTime.Now:yyyyMMddHHmmss}{random.Next(100000, 999999)}.mp4";
|
||||||
|
|
||||||
|
string _customArg = "-filter_complex \"[0:v][1:v] overlay=0:H-h\" ";
|
||||||
|
// 使用 FFMpegArguments 构建命令
|
||||||
|
bool _isCoverSuccess = FFMpegArguments
|
||||||
|
.FromFileInput(_outPutName)
|
||||||
|
.AddFileInput(VideoModel.AuditImagePath)
|
||||||
|
.OutputToFile(
|
||||||
|
_outPutNameImg,
|
||||||
|
true,
|
||||||
|
options => options.WithCustomArgument(_customArg)
|
||||||
|
)
|
||||||
|
.ProcessSynchronously();
|
||||||
|
|
||||||
|
|
||||||
|
LogUtils.Info($"图片已成功添加到视频中,输出文件:{_outPutName}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogUtils.Error($"图片添加到视频中失败", ex);
|
||||||
}
|
}
|
||||||
Console.WriteLine();
|
|
||||||
FFMpeg.Join("./1.mp4",combination.ToArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine($"总共有 {combinations.Count} 种拼接方案。");
|
LogUtils.Info($"当前视频: {string.Join(";", combination)} 合并成功");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogUtils.Error($"视频:{string.Join(";", combination)} 合并失败", ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
semaphore.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
taskList.Add(_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await Task.WhenAll(taskList).ContinueWith((s) =>
|
||||||
|
{
|
||||||
|
//结束时间
|
||||||
|
DateTime endTime = DateTime.Now;
|
||||||
|
LogUtils.Info($"所有视频拼接完成,用时{(endTime - startTime).TotalSeconds}秒");
|
||||||
|
|
||||||
|
VideoModel.IsStart = false;
|
||||||
|
VideoCombine.Cleanup(_clearPath);
|
||||||
|
MessageBox.Show("所有视频拼接完成");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -94,12 +296,15 @@ namespace VideoConcat.ViewModels
|
|||||||
VideoModel.FolderInfos.Clear();
|
VideoModel.FolderInfos.Clear();
|
||||||
//获取文件夹下所有视频文件
|
//获取文件夹下所有视频文件
|
||||||
foreach (DirectoryInfo Folder in folders)
|
foreach (DirectoryInfo Folder in folders)
|
||||||
|
{
|
||||||
|
if (Folder.Name != "output")
|
||||||
{
|
{
|
||||||
string[] files = Directory.GetFiles(Folder.FullName, "*.mp4");
|
string[] files = Directory.GetFiles(Folder.FullName, "*.mp4");
|
||||||
LogUtils.Info($"{Folder.Name}下有{files.Length}个视频文件");
|
LogUtils.Info($"{Folder.Name}下有{files.Length}个视频文件");
|
||||||
|
|
||||||
VideoModel.FolderInfos.Add(new VideoModel.FolderInfo { DirectoryInfo = Folder, Num = files.Length, VideoPaths = new List<string>(files) });
|
VideoModel.FolderInfos.Add(new VideoModel.FolderInfo { DirectoryInfo = Folder, Num = files.Length, VideoPaths = new List<string>(files) });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
VideoModel.UpdateSum();
|
VideoModel.UpdateSum();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -110,20 +315,7 @@ namespace VideoConcat.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BtnOpenFolderCommand : ICommand
|
class Command : ICommand
|
||||||
{
|
|
||||||
public event EventHandler? CanExecuteChanged;
|
|
||||||
|
|
||||||
public bool CanExecute(object? parameter) => true;
|
|
||||||
|
|
||||||
public void Execute(object? parameter)
|
|
||||||
{
|
|
||||||
DoExcue?.Invoke(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<Object?>? DoExcue { get; set; }
|
|
||||||
}
|
|
||||||
class BtnStartVideoConcatCommand : ICommand
|
|
||||||
{
|
{
|
||||||
public event EventHandler? CanExecuteChanged;
|
public event EventHandler? CanExecuteChanged;
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,11 @@
|
|||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Border>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="39*"/>
|
||||||
|
<ColumnDefinition Width="461*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border Grid.ColumnSpan="2">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<StackPanel Width="500">
|
<StackPanel Width="500">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using VideoConcat.Common.Api.Base;
|
||||||
|
using VideoConcat.Common.Tools;
|
||||||
|
|
||||||
namespace VideoConcat.Views
|
namespace VideoConcat.Views
|
||||||
{
|
{
|
||||||
@ -17,13 +19,30 @@ namespace VideoConcat.Views
|
|||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BtnLogin_Click(object sender, RoutedEventArgs e)
|
private async void BtnLogin_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
|
||||||
if (Username.Text == "admin" && Password.Password == "123456")
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
string _userName = Username.Text;
|
||||||
|
string _password = Password.Password;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_userName) || string.IsNullOrEmpty(_password))
|
||||||
|
{
|
||||||
|
Username.Clear();
|
||||||
|
Password.Clear();
|
||||||
|
WPFDevelopers.Controls.MessageBox.Show("请输入用户名或者密码!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
ApiResponse<UserLoginResponse> res = await SystemApi.LoginAsync<UserLoginResponse>(_userName, _password);
|
||||||
|
if (res.Code !=0)
|
||||||
|
{
|
||||||
|
WPFDevelopers.Controls.MessageBox.Show(res.Msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
new Video().Show();
|
new Video().Show();
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
|
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
|
||||||
xmlns:local="clr-namespace:VideoConcat.Views"
|
xmlns:local="clr-namespace:VideoConcat.Views"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="视频拼接" Height="600" Width="800" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
|
Title="视频拼接" Height="800" Width="780" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
@ -20,36 +20,47 @@
|
|||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel>
|
|
||||||
<StackPanel>
|
|
||||||
<Grid VerticalAlignment="Center" Margin="10,10">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="35"></RowDefinition>
|
|
||||||
<RowDefinition Height="35"></RowDefinition>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="120"></ColumnDefinition>
|
|
||||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
|
||||||
<ColumnDefinition Width="120"></ColumnDefinition>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Label VerticalAlignment="Center" FontSize="16" Margin="5,0" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">视频文件夹:</Label>
|
|
||||||
<TextBox Grid.Column="1" Name="FoldPath" Text="{Binding VideoModel.FolderPath,Mode=TwoWay}" IsReadOnly="True" Margin="5,0" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" materialDesign:HintAssist.Hint="选择视频主文件夹"/>
|
|
||||||
<Button Grid.Column="2" Content="选择" FontSize="16" Command="{Binding VideoModel.BtnOpenFolderCommand}" Background="#40568D" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="选择含有视频的主文件夹" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,0" />
|
|
||||||
<Label Grid.Row="1" VerticalAlignment="Center" FontSize="16" Margin="5,0" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">视频个数:</Label>
|
|
||||||
<TextBox Grid.Row="1" Grid.Column="1" Name="Num" Text="{Binding VideoModel.Num,Mode=TwoWay}" Margin="5,0" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" ToolTip="请输入合成视频个数" materialDesign:HintAssist.Hint="请输入拼接视频数目"/>
|
|
||||||
<Button Grid.Row="1" Grid.Column="2" Content="开始拼接" FontSize="16" Command="{Binding VideoModel.BtnStartVideoConcatCommand}" Background="#E54858" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始拼接视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,0" IsEnabled="{Binding VideoModel.CanStart}" Cursor="Hand"/>
|
|
||||||
</Grid>
|
|
||||||
</StackPanel>
|
|
||||||
<StackPanel >
|
<StackPanel >
|
||||||
<DataGrid ItemsSource="{Binding VideoModel.FolderInfos}" AutoGenerateColumns="False">
|
<WrapPanel Height="120">
|
||||||
<DataGrid.Columns >
|
<Label VerticalAlignment="Center" Width="100" FontSize="16" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">视频文件夹:</Label>
|
||||||
<DataGridTextColumn Header="子文件夹" Binding="{Binding DirectoryInfo.Name,Mode=OneWay}" IsReadOnly="True" CanUserSort="False" CanUserReorder="False"/>
|
<TextBox Grid.Column="1" Width="500" Name="FoldPath" Text="{Binding VideoModel.FolderPath,Mode=TwoWay}" IsReadOnly="True" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" materialDesign:HintAssist.Hint="选择视频主文件夹"/>
|
||||||
<DataGridTextColumn Header="视频个数" Binding="{Binding Num}" IsReadOnly="True" CanUserReorder="False" CanUserSort="False"/>
|
<Button Grid.Column="2" Width="100" Content="选 择" FontSize="16" Command="{Binding VideoModel.BtnOpenFolderCommand}" Background="#40568D" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="选择含有视频的主文件夹" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding VideoModel.IsCanOperate}"/>
|
||||||
|
<Label Grid.Row="1" Width="100" VerticalAlignment="Center" FontSize="16" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">选择广审:</Label>
|
||||||
|
<TextBox Grid.Row="1" Width="500" Grid.Column="1" Text="{Binding VideoModel.AuditImagePath,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" ToolTip="请选择" materialDesign:HintAssist.Hint="请选择"/>
|
||||||
|
<Button Grid.Row="1" Width="100" Grid.Column="2" Content="选择文件" FontSize="16" Command="{Binding VideoModel.BtnChooseAuditImageCommand}" Background="#E54858" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="选择广审" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding VideoModel.IsCanOperate}"/>
|
||||||
|
<Label Grid.Row="1" Width="100" VerticalAlignment="Center" FontSize="16" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" Style="{StaticResource MaterialDesignLabel}" VerticalContentAlignment="Stretch">视频个数:</Label>
|
||||||
|
<TextBox Grid.Row="1" Width="500" Grid.Column="1" Text="{Binding VideoModel.Num,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="5,2" BorderBrush="#7F7F7F" BorderThickness="2" FontSize="16" VerticalContentAlignment="Center" ToolTip="请输入合成视频个数" materialDesign:HintAssist.Hint="请输入拼接视频数目" IsEnabled="{Binding VideoModel.IsCanOperate}" />
|
||||||
|
<Button Grid.Row="1" Width="100" Grid.Column="2" Content="开始拼接" FontSize="16" Command="{Binding VideoModel.BtnStartVideoConcatCommand}" Background="#E54858" BorderBrush="#7F7F7F" BorderThickness="2" ToolTip="开始拼接视频" Style="{StaticResource MaterialDesignRaisedButton}" Margin="5,2" IsEnabled="{Binding VideoModel.CanStart}" Cursor="Hand"/>
|
||||||
|
</WrapPanel>
|
||||||
|
<StackPanel Height="30">
|
||||||
|
<Label FontWeight="ExtraBold" FontSize="16" Grid.Row="1">文件信息:</Label>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Height="200">
|
||||||
|
<Grid>
|
||||||
|
<DataGrid Grid.Row="1" ItemsSource="{Binding VideoModel.FolderInfos}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" VerticalScrollBarVisibility="Auto" MaxHeight="200">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="文件夹" Binding="{Binding DirectoryInfo.Name,Mode=OneWay}" Width="*"></DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="视频数量" Binding="{Binding Num,Mode=OneWay}" Width="*"></DataGridTextColumn>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
|
||||||
|
|
||||||
|
<StackPanel Height="30">
|
||||||
|
<Label FontWeight="ExtraBold" FontSize="16">视频合成进度:</Label>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Height="400">
|
||||||
|
<DataGrid ItemsSource="{Binding VideoModel.ConcatVideos}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" IsReadOnly="True" VerticalScrollBarVisibility="Auto" MaxHeight="400" ScrollViewer.CanContentScroll="True">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="序号" Binding="{Binding Index}" Width="auto"></DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="文件名" Binding="{Binding FileName}" Width="auto"></DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="大小(MB)" Binding="{Binding Size}" Width="auto"></DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="时长(秒)" Binding="{Binding Seconds}" Width="auto"></DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="进度" Binding="{Binding Progress}" Width="auto"></DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="状态" Binding="{Binding Status}" Width="auto"></DataGridTextColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"BinaryFolder": "./",
|
|
||||||
"TemporaryFilesFolder": "./tmp"
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user