├── .gitattributes ├── .gitignore ├── Inkore.Extension ├── CommonDialog │ ├── CheckBoxDialog.xaml │ ├── CheckBoxDialog.xaml.cs │ ├── CommonDialog.cs │ ├── DetailTextDialog.xaml │ ├── DetailTextDialog.xaml.cs │ ├── DialogItem.cs │ ├── InputDialog.xaml │ ├── InputDialog.xaml.cs │ ├── Resources.xaml │ ├── SelectItemDialog.xaml │ └── SelectItemDialog.xaml.cs ├── Inkore.Extension.csproj ├── ProgressRingOverlay.xaml ├── ProgressRingOverlay.xaml.cs └── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── LICENSE ├── ReadMe.md ├── SimpleFFmpegGUI.Core ├── DependencyInjectionExtension.cs ├── Dto │ ├── CpuCoreUsageDto.cs │ ├── FileInfoDto.cs │ ├── MediaStreamDto.cs │ ├── PagedListDto.cs │ ├── ProgressDto.cs │ └── StatusDto.cs ├── FFmpegArgument │ ├── ArgumentsGenerator.cs │ ├── ArgumentsGeneratorBase.cs │ ├── AudioArgumentsGenerator.cs │ ├── FFmpegArgumentException.cs │ ├── InputArgumentsGenerator.cs │ ├── StreamArgumentsGenerator.cs │ └── VideoArgumentsGenerator.cs ├── FFmpegLib │ ├── AAC.cs │ ├── AomAV1.cs │ ├── AudioCodec.cs │ ├── CodecBase.cs │ ├── FFmpegArgument.cs │ ├── FFmpegEnums.cs │ ├── GeneralAudioCodec.cs │ ├── GeneralVideoCodec.cs │ ├── OPUS.cs │ ├── SVTAV1.cs │ ├── VideoCodec.cs │ ├── VideoFormat.cs │ ├── X264.cs │ ├── X265.cs │ └── XVP9.cs ├── FileSystemUtility.cs ├── IPipeService.cs ├── Logger.cs ├── Manager │ ├── ConfigManager.cs │ ├── FFmpegManager.cs │ ├── FFmpegOutputEventArgs.cs │ ├── FFmpegProcess.cs │ ├── LogManager.cs │ ├── MediaInfoManager.cs │ ├── PowerManager.cs │ ├── PresetManager.cs │ ├── ProcessChangedEventArgs.cs │ ├── ProcessExtension.cs │ ├── QueueManager.cs │ └── TaskManager.cs ├── Model │ ├── AudioCodeArguments.cs │ ├── CodePreset.cs │ ├── CombineArguments.cs │ ├── Config.cs │ ├── EfJsonConverter.cs │ ├── FFmpegDbContext.cs │ ├── IAudioCodeArguments.cs │ ├── IInputArguments.cs │ ├── IModel.cs │ ├── IVideoCodeArguments.cs │ ├── InputArguments.cs │ ├── Log.cs │ ├── MediaInfo │ │ ├── MediaInfoAudio.cs │ │ ├── MediaInfoGeneral.cs │ │ ├── MediaInfoImage.cs │ │ ├── MediaInfoItem.cs │ │ ├── MediaInfoText.cs │ │ ├── MediaInfoTrackBase.cs │ │ └── MediaInfoVideo.cs │ ├── ModelBase.cs │ ├── NameDescriptionAttribute.cs │ ├── OutputArguments.cs │ ├── ProcessedOptions.cs │ ├── StreamArguments.cs │ ├── StreamChannel.cs │ ├── StreamMapInfo.cs │ ├── TaskInfo.cs │ ├── TaskStatus.cs │ ├── TaskType.cs │ └── VideoCodeArguments.cs └── SimpleFFmpegGUI.Core.csproj ├── SimpleFFmpegGUI.Host.Console ├── Options.cs ├── Program.cs └── SimpleFFmpegGUI.Host.Console.csproj ├── SimpleFFmpegGUI.Host.WindowsService ├── CreateWindowsService.bat ├── DeleteWindowsService.bat ├── Program.cs ├── SimpleFFmpegGUI.Host.WindowsService.csproj ├── Worker.cs ├── appsettings.Development.json └── appsettings.json ├── SimpleFFmpegGUI.Host ├── ConsoleLogger.cs ├── FtpManager.cs ├── PipeService.cs ├── Properties │ └── launchSettings.json ├── SimpleFFmpegGUI.Host.csproj ├── Startup.cs └── log4net.config ├── SimpleFFmpegGUI.WPF ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── Config.cs ├── Converters │ ├── AttributeHelper.cs │ ├── Bitrate2StringConverter.cs │ ├── CountEqualsOneValueConverter.cs │ ├── EmptyIfZeroConverter.cs │ ├── HourMinSecTimeSpanConverter.cs │ ├── Index2StringConverter.cs │ ├── Int2StringConverter.cs │ └── NameDescriptionAttributeValueConverter.cs ├── CutWindow.xaml ├── CutWindow.xaml.cs ├── FileDialogExtension.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Messages │ ├── AddNewTabMessage.cs │ ├── FileDialogMessage.cs │ ├── QueueMessagesMessage.cs │ ├── ShowCodeArgumentsMessage.cs │ ├── SnapshotEnabledMessage.cs │ ├── WindowEnableMessage.cs │ └── WindowHandleMessage.cs ├── NotificationMessageExtension.cs ├── Pages │ ├── AddTaskPage.xaml │ ├── AddTaskPage.xaml.cs │ ├── FFmpegOutputPage.xaml │ ├── FFmpegOutputPage.xaml.cs │ ├── ICloseablePage.cs │ ├── LogsPage.xaml │ ├── LogsPage.xaml.cs │ ├── MediaInfoPage.xaml │ ├── MediaInfoPage.xaml.cs │ ├── PageHelper.cs │ ├── PresetsPage.xaml │ ├── PresetsPage.xaml.cs │ ├── SettingPage.xaml │ ├── SettingPage.xaml.cs │ ├── TasksPage.xaml │ └── TasksPage.xaml.cs ├── Panels │ ├── CodeArgumentsPanel.xaml │ ├── CodeArgumentsPanel.xaml.cs │ ├── FileIOPanel.xaml │ ├── FileIOPanel.xaml.cs │ ├── PresetsPanel.xaml │ ├── PresetsPanel.xaml.cs │ ├── StatusPanel.xaml │ ├── StatusPanel.xaml.cs │ ├── TaskList.xaml │ └── TaskList.xaml.cs ├── Properties │ └── launchSettings.json ├── SimpleFFmpegGUI.WPF.csproj ├── Styles.xaml ├── TestWindow.xaml ├── TestWindow.xaml.cs ├── ViewModels │ ├── AddTaskPageViewModel.cs │ ├── AllTasksViewModel.cs │ ├── ArgumentDescriptions.cs │ ├── AudioArgumentsViewModel.cs │ ├── CodeArgumentsPanelViewModel.cs │ ├── CurrentTasksViewModel.cs │ ├── CutWindowViewModel.cs │ ├── FFmpegOutputPageViewModel.cs │ ├── FileIOPanelViewModel.cs │ ├── FormatArgumentViewModel.cs │ ├── IArgumentVideModel.cs │ ├── InputArgumentsViewModel.cs │ ├── LogsPageViewModel.cs │ ├── MainWindowViewModel.cs │ ├── MediaInfoPageViewModel.cs │ ├── PerformanceTestViewModel.cs │ ├── PresetsPageViewModel.cs │ ├── PresetsPanelViewModel.cs │ ├── SettingPageViewModel.cs │ ├── SnapshotViewModel.cs │ ├── StatusPanelViewModel.cs │ ├── TaskCollectionViewModelBase.cs │ ├── TaskInfoViewModel.cs │ ├── TaskListViewModel.cs │ ├── TasksPageViewModel.cs │ ├── TestWindowViewModel.cs │ ├── VideoArgumentsViewModel.cs │ └── ViewModelBase.cs ├── icon.ico ├── icon.png └── log4net.config ├── SimpleFFmpegGUI.Web ├── .gitignore ├── babel.config.js ├── package-lock.json ├── package.json ├── public │ ├── icon.png │ ├── index.html │ └── robots.txt ├── src │ ├── App.vue │ ├── assets │ │ ├── global.css │ │ └── logo.png │ ├── common.ts │ ├── components │ │ ├── AddToTaskButtons.vue │ │ ├── CodeArguments.vue │ │ ├── CodeArgumentsDescription.vue │ │ ├── FileIOGroup.vue │ │ ├── FileSelect.vue │ │ ├── StatusBar.vue │ │ └── TimeInput.vue │ ├── main.ts │ ├── net.ts │ ├── parameters.ts │ ├── registerServiceWorker.ts │ ├── router │ │ └── index.ts │ ├── shims-tsx.d.ts │ ├── shims-vue.d.ts │ └── views │ │ ├── About.vue │ │ ├── Add │ │ ├── Code.vue │ │ ├── Combine.vue │ │ ├── Compare.vue │ │ ├── Concat.vue │ │ └── Custom.vue │ │ ├── Files.vue │ │ ├── Login.vue │ │ ├── Logs.vue │ │ ├── MediaInfo.vue │ │ ├── Power.vue │ │ ├── Presets.vue │ │ ├── Tasks.vue │ │ └── Welcome.vue ├── tsconfig.json └── vue.config.js ├── SimpleFFmpegGUI.WebAPI ├── .config │ └── dotnet-tools.json ├── Controllers │ ├── FFmpegControllerBase.cs │ ├── FileController.cs │ ├── LogController.cs │ ├── MediaInfoController.cs │ ├── PowerController.cs │ ├── PresetController.cs │ ├── QueueController.cs │ ├── TaskController.cs │ └── TokenController.cs ├── Converter │ ├── DoubleConverter.cs │ └── TimeSpanConverter.cs ├── Dto │ ├── CodePresetDto.cs │ ├── FtpStatusDto.cs │ └── TaskDto.cs ├── PipeClient.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── SimpleFFmpegGUI.WebAPI.csproj ├── Startup.cs ├── TokenFilter.cs ├── appsettings.Development.json └── appsettings.json ├── SimpleFFmpegGUI.sln ├── bin ├── ffmpeg │ └── files.txt ├── ffmpeg_FFME │ └── files.txt └── files.txt ├── build.ps1 ├── clean.ps1 ├── icon.png ├── icon.psd ├── imgs ├── Code.png ├── info.png ├── logs.png ├── tasks.png ├── wpf_clip.jpg ├── wpf_info.png ├── wpf_main.png ├── 软件架构图.png └── 软件架构图.vsdx ├── libs ├── Enterwell.Clients.Wpf.Notifications.dll ├── FzCoreLib.Windows.dll ├── FzStandardLib.dll ├── ModernWpf.FzExtension.dll └── WindowsAPICodePack.FzExtension.dll ├── package-lock.json └── 日志.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/CheckBoxDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace iNKORE.Extension.CommonDialog 18 | { 19 | public partial class CheckBoxDialog : CommonDialog 20 | { 21 | internal CheckBoxDialog() 22 | { 23 | InitializeComponent(); 24 | } 25 | 26 | private IList items; 27 | 28 | public IList Items 29 | { 30 | get => items; 31 | set => this.SetValueAndNotify(ref items, value, nameof(Items)); 32 | } 33 | 34 | private bool needAtLeastOneCheck; 35 | 36 | public bool NeedAtLeastOneCheck 37 | { 38 | get => needAtLeastOneCheck; 39 | set 40 | { 41 | this.SetValueAndNotify(ref needAtLeastOneCheck, value, nameof(NeedAtLeastOneCheck)); 42 | if (value) 43 | { 44 | IsPrimaryButtonEnabled = Items != null && Items.Any(p => p.IsChecked); 45 | } 46 | else 47 | { 48 | IsPrimaryButtonEnabled = true; 49 | } 50 | } 51 | } 52 | 53 | public int SelectedIndex { get; private set; } = -1; 54 | 55 | private void CheckBox_Checked(object sender, RoutedEventArgs e) 56 | { 57 | IsPrimaryButtonEnabled = true; 58 | } 59 | 60 | private void CheckBox_Unchecked(object sender, RoutedEventArgs e) 61 | { 62 | if (NeedAtLeastOneCheck) 63 | { 64 | IsPrimaryButtonEnabled = Items != null && Items.Any(p => p.IsChecked); 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/DetailTextDialog.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 34 | 38 | 45 | 46 | 47 | 55 | 56 | -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/DetailTextDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace iNKORE.Extension.CommonDialog 18 | { 19 | public partial class DetailTextDialog : CommonDialog 20 | { 21 | internal DetailTextDialog() 22 | { 23 | InitializeComponent(); 24 | } 25 | 26 | private string message; 27 | 28 | public string Message 29 | { 30 | get => message; 31 | set => this.SetValueAndNotify(ref message, value, nameof(Message)); 32 | } 33 | 34 | private string detail; 35 | 36 | public string Detail 37 | { 38 | get => detail; 39 | set => this.SetValueAndNotify(ref detail, value, nameof(Detail)); 40 | } 41 | 42 | private string icon; 43 | 44 | public string Icon 45 | { 46 | get => icon; 47 | set => this.SetValueAndNotify(ref icon, value, nameof(Icon)); 48 | } 49 | 50 | private Brush iconBrush; 51 | 52 | public Brush IconBrush 53 | { 54 | get => iconBrush; 55 | set => this.SetValueAndNotify(ref iconBrush, value, nameof(IconBrush)); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/DialogItem.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System; 3 | using System.ComponentModel; 4 | 5 | namespace iNKORE.Extension.CommonDialog 6 | { 7 | public abstract class DialogItem : INotifyPropertyChanged 8 | { 9 | public object Tag { get; set; } 10 | private string title; 11 | 12 | public string Title 13 | { 14 | get => title; 15 | set => this.SetValueAndNotify(ref title, value, nameof(Title)); 16 | } 17 | 18 | private string detail; 19 | 20 | public string Detail 21 | { 22 | get => detail; 23 | set => this.SetValueAndNotify(ref detail, value, nameof(Detail)); 24 | } 25 | 26 | public DialogItem(string title, string detail = null) 27 | { 28 | Title = title; 29 | Detail = detail; 30 | } 31 | 32 | public DialogItem() 33 | { 34 | } 35 | 36 | public event PropertyChangedEventHandler PropertyChanged; 37 | } 38 | 39 | public class SelectDialogItem : DialogItem 40 | { 41 | public SelectDialogItem(string title, string detail, Action selectAction) : base(title, detail) 42 | { 43 | SelectAction = selectAction; 44 | } 45 | 46 | public SelectDialogItem(string title, string detail = null) : base(title, detail) 47 | { 48 | } 49 | 50 | public Action SelectAction 51 | { 52 | get => selectAction; 53 | set => this.SetValueAndNotify(ref selectAction, value, nameof(SelectAction)); 54 | } 55 | 56 | private Action selectAction; 57 | } 58 | 59 | public class CheckDialogItem : DialogItem 60 | { 61 | public CheckDialogItem(string title, string detail = null) : base(title, detail) 62 | { 63 | } 64 | 65 | public CheckDialogItem(string title, string detail, bool isEnabled, bool isChecked) : base(title, detail) 66 | { 67 | IsEnabled = isEnabled; 68 | IsChecked = isChecked; 69 | } 70 | 71 | private bool isChecked; 72 | 73 | public bool IsChecked 74 | { 75 | get => isChecked; 76 | set => this.SetValueAndNotify(ref isChecked, value, nameof(IsChecked)); 77 | } 78 | 79 | private bool isEnabled = true; 80 | 81 | public bool IsEnabled 82 | { 83 | get => isEnabled; 84 | set => this.SetValueAndNotify(ref isEnabled, value, nameof(IsEnabled)); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/InputDialog.xaml: -------------------------------------------------------------------------------- 1 |  17 | 21 | 22 | 23 | 24 | 25 | 32 | 42 | 43 | -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/Resources.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/SelectItemDialog.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Inkore.Extension/CommonDialog/SelectItemDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Navigation; 16 | using System.Windows.Shapes; 17 | using FzLib; 18 | 19 | namespace iNKORE.Extension.CommonDialog 20 | { 21 | public partial class SelectItemDialog : CommonDialog 22 | { 23 | internal SelectItemDialog() 24 | { 25 | InitializeComponent(); 26 | } 27 | 28 | private IList items; 29 | 30 | public IList Items 31 | { 32 | get => items; 33 | set => this.SetValueAndNotify(ref items, value, nameof(Items)); 34 | } 35 | 36 | public int SelectedIndex { get; private set; } = -1; 37 | 38 | private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) 39 | { 40 | SelectedIndex = (sender as ListView).SelectedIndex; 41 | var item = (sender as ListView).SelectedItem as SelectDialogItem; 42 | 43 | Hide(); 44 | item.SelectAction?.Invoke(); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Inkore.Extension/Inkore.Extension.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows7.0 4 | Library 5 | false 6 | true 7 | true 8 | iNKORE.Extension 9 | 10 | 11 | ..\Release\AnyCPU\ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ..\libs\FzCoreLib.Windows.dll 20 | 21 | 22 | ..\libs\FzStandardLib.dll 23 | 24 | 25 | -------------------------------------------------------------------------------- /Inkore.Extension/ProgressRingOverlay.xaml: -------------------------------------------------------------------------------- 1 |  12 | 16 | 17 | 18 | 19 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 请稍等 41 | 42 | 49 | 54 | 55 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Inkore.Extension/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的一般信息由以下 8 | // 控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("iNKORE.Extension")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("iNKORE.Extension")] 15 | [assembly: AssemblyCopyright("Copyright © 2021")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 会使此程序集中的类型 20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 21 | //请将此类型的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(false)] 23 | 24 | //若要开始生成可本地化的应用程序,请设置 25 | //.csproj 文件中的 CultureYouAreCodingWith 26 | //例如,如果您在源文件中使用的是美国英语, 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly:ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(未在页面中找到资源时使用, 37 | //或应用程序资源字典中找到时使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(未在页面中找到资源时使用, 40 | //、应用程序或任何主题专用资源字典中找到时使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下列四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 52 | //通过使用 "*",如下所示: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /Inkore.Extension/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace iNKORE.Extension.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("iNKORE.Extension.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Inkore.Extension/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace iNKORE.Extension.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Inkore.Extension/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/DependencyInjectionExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using SimpleFFmpegGUI.Manager; 3 | using SimpleFFmpegGUI.Model; 4 | 5 | namespace SimpleFFmpegGUI 6 | { 7 | public static class DependencyInjectionExtension 8 | { 9 | public static void AddFFmpegServices(this IServiceCollection services) 10 | { 11 | services.AddDbContext() 12 | .AddTransient() 13 | .AddTransient() 14 | .AddTransient() 15 | .AddTransient() 16 | .AddTransient() 17 | .AddSingleton(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Dto/CpuCoreUsageDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleFFmpegGUI.Dto 8 | { 9 | public class CpuCoreUsageDto 10 | { 11 | public int CpuIndex { get; set; } 12 | public int CoreIndex { get; set; } 13 | public double Usage { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Dto/FileInfoDto.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace SimpleFFmpegGUI.Dto 10 | { 11 | public class FileInfoDto 12 | { 13 | public FileInfoDto() 14 | { 15 | } 16 | 17 | public FileInfoDto(string path) 18 | { 19 | FileInfo file = new FileInfo(path); 20 | Name = file.Name; 21 | Length = file.Length; 22 | LengthText = NumberConverter.ByteToFitString(Length); 23 | LastWriteTime = file.LastWriteTime; 24 | } 25 | 26 | public string Name { get; set; } 27 | public long Length { get; set; } 28 | public string LengthText { get; set; } 29 | public DateTime LastWriteTime { get; set; } 30 | public string Id { get; set; } 31 | } 32 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Dto/MediaStreamDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SimpleFFmpegGUI.Dto 5 | { 6 | public class MediaStreamDto 7 | { 8 | public int Index { get; set; } 9 | public string CodecName { get; set; } 10 | public string CodecLongName { get; set; } 11 | public int BitRate { get; set; } 12 | public TimeSpan Duration { get; set; } 13 | public string Language { get; set; } 14 | public Dictionary Tags { get; set; } 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Dto/PagedListDto.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace SimpleFFmpegGUI.Dto 5 | { 6 | public class PagedListDto 7 | { 8 | public PagedListDto() 9 | { 10 | List = new List(); 11 | } 12 | 13 | public PagedListDto(IList list, int totalCount) 14 | { 15 | TotalCount = totalCount; 16 | List = list; 17 | } 18 | 19 | public IList List { get; set; } 20 | 21 | [JsonProperty] 22 | public int TotalCount { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegArgument/ArgumentsGeneratorBase.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegLib; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SimpleFFmpegGUI.FFmpegArgument 6 | { 7 | public abstract class ArgumentsGeneratorBase 8 | { 9 | /// 10 | /// 单个参数对的集合 11 | /// 12 | protected List arguments = new List(); 13 | 14 | /// 15 | /// 额外参数 16 | /// 17 | /// 18 | public virtual IEnumerable ExtraArguments() 19 | { 20 | return Enumerable.Empty(); 21 | } 22 | 23 | /// 24 | /// 将中的参数连接成为字符串 25 | /// 26 | /// 27 | public virtual string GetArguments() 28 | { 29 | var args = arguments.Concat(ExtraArguments()).Where(p => p != null).ToList(); 30 | while (args.Any(p => p.Other != null)) 31 | { 32 | var hasOthers = args.Where(p => p.Other != null).ToList(); 33 | foreach (var item in hasOthers) 34 | { 35 | args.Add(item.Other); 36 | item.Other = null; 37 | } 38 | } 39 | 40 | List list = new List(); 41 | var groups = args.GroupBy(p => p.Parent).ToList(); 42 | foreach (var group in groups) 43 | { 44 | if (group.Key == null) 45 | { 46 | foreach (var arg in group) 47 | { 48 | list.Add($"-{arg.Key} {arg.Value}"); 49 | } 50 | } 51 | else 52 | { 53 | List subList = new List(); 54 | foreach (var arg in group) 55 | { 56 | subList.Add($"{arg.Key}={arg.Value}"); 57 | } 58 | list.Add($"-{group.Key} {string.Join(group.First().Seprator, subList)}"); 59 | } 60 | } 61 | 62 | return string.Join(' ', list); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegArgument/AudioArgumentsGenerator.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegLib; 2 | using AudioCodec = SimpleFFmpegGUI.FFmpegLib.AudioCodec; 3 | 4 | namespace SimpleFFmpegGUI.FFmpegArgument 5 | { 6 | public class AudioArgumentsGenerator : ArgumentsGeneratorBase 7 | { 8 | /// 9 | /// 编码 10 | /// 11 | public AudioCodec AudioCodec { get; private set; } 12 | 13 | /// 14 | /// 码率 15 | /// 16 | /// 17 | /// 18 | public AudioArgumentsGenerator Bitrate(double? kb) 19 | { 20 | if (kb.HasValue) 21 | { 22 | arguments.Add(AudioCodec.Bitrate(kb.Value)); 23 | } 24 | return this; 25 | } 26 | 27 | /// 28 | /// 编码 29 | /// 30 | /// 31 | /// 32 | public AudioArgumentsGenerator Codec(string codec) 33 | { 34 | codec = codec.ToLower(); 35 | foreach (var c in AudioCodec.AudioCodecs) 36 | { 37 | if (c.Name.ToLower() == codec || c.Lib.ToLower() == codec) 38 | { 39 | AudioCodec = c; 40 | arguments.Add(new FFmpegArgumentItem("c:a", c.Lib)); 41 | return this; 42 | } 43 | } 44 | AudioCodec = new GeneralAudioCodec(); 45 | if (codec is not ("自动" or "auto") && !string.IsNullOrEmpty(codec)) 46 | { 47 | arguments.Add(new FFmpegArgumentItem("c:a", codec)); 48 | } 49 | return this; 50 | } 51 | 52 | /// 53 | /// 复制音频流 54 | /// 55 | /// 56 | public AudioArgumentsGenerator Copy() 57 | { 58 | arguments.Add(new FFmpegArgumentItem("c:a", "copy")); 59 | return this; 60 | } 61 | 62 | /// 63 | /// 禁用音频流 64 | /// 65 | /// 66 | public AudioArgumentsGenerator Disable() 67 | { 68 | arguments.Add(new FFmpegArgumentItem("an")); 69 | return this; 70 | } 71 | 72 | public AudioArgumentsGenerator SamplingRate(int? hz) 73 | { 74 | if (hz.HasValue) 75 | { 76 | arguments.Add(AudioCodec.SamplingRate(hz.Value)); 77 | } 78 | return this; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegArgument/FFmpegArgumentException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.FFmpegArgument 4 | { 5 | public class FFmpegArgumentException : Exception 6 | { 7 | public FFmpegArgumentException() 8 | { 9 | } 10 | 11 | public FFmpegArgumentException(string message) : base(message) 12 | { 13 | } 14 | 15 | public FFmpegArgumentException(string message, Exception innerException) : base(message, innerException) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegArgument/InputArgumentsGenerator.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegLib; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.FFmpegArgument 5 | { 6 | public class InputArgumentsGenerator : ArgumentsGeneratorBase 7 | { 8 | /// 9 | /// 持续时间 10 | /// 11 | /// 12 | public void Duration(TimeSpan? length) 13 | { 14 | if (!length.HasValue) 15 | { 16 | return; 17 | } 18 | arguments.Add(new FFmpegArgumentItem("t", length.Value.TotalSeconds.ToString("0.000"))); 19 | } 20 | 21 | /// 22 | /// 强制输入格式 23 | /// 24 | /// 25 | public void Format(string format) 26 | { 27 | if (format == null) 28 | { 29 | return; 30 | } 31 | arguments.Add(new FFmpegArgumentItem("f", format)); 32 | } 33 | 34 | /// 35 | /// 输入帧率 36 | /// 37 | /// 38 | public void Framerate(double? fps) 39 | { 40 | if (!fps.HasValue || double.IsNaN(fps.Value)) 41 | { 42 | return; 43 | } 44 | arguments.Add(new FFmpegArgumentItem("framerate", fps.ToString())); 45 | } 46 | 47 | /// 48 | /// 输入文件 49 | /// 50 | /// 51 | public void Input(string file) 52 | { 53 | if (file == null) 54 | { 55 | return; 56 | } 57 | arguments.Add(new FFmpegArgumentItem("i", $"\"{file}\"")); 58 | } 59 | 60 | /// 61 | /// 开始时间 62 | /// 63 | /// 64 | public void Seek(TimeSpan? seek) 65 | { 66 | if (!seek.HasValue) 67 | { 68 | return; 69 | } 70 | arguments.Add(new FFmpegArgumentItem("ss", seek.Value.TotalSeconds.ToString("0.000"))); 71 | } 72 | 73 | /// 74 | /// 结束时间 75 | /// 76 | /// 77 | public void To(TimeSpan? to) 78 | { 79 | if (!to.HasValue) 80 | { 81 | return; 82 | } 83 | arguments.Add(new FFmpegArgumentItem("to", to.Value.TotalSeconds.ToString("0.000"))); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegArgument/StreamArgumentsGenerator.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegLib; 2 | using SimpleFFmpegGUI.Model; 3 | using System.Text; 4 | 5 | namespace SimpleFFmpegGUI.FFmpegArgument 6 | { 7 | public class StreamArgumentsGenerator : ArgumentsGeneratorBase 8 | { 9 | /// 10 | /// 设置流映射 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | public StreamArgumentsGenerator Map(int inputIndex, StreamChannel channel, int? streamIndex) 18 | { 19 | StringBuilder str = new StringBuilder(); 20 | str.Append(inputIndex); 21 | if (channel != StreamChannel.All) 22 | { 23 | str.Append(':'); 24 | str.Append(channel switch 25 | { 26 | StreamChannel.Video => 'v', 27 | StreamChannel.Audio => 'a', 28 | StreamChannel.Subtitle => 's', 29 | _ => throw new System.NotImplementedException(), 30 | }); 31 | } 32 | if (streamIndex.HasValue) 33 | { 34 | str.Append(':').Append(streamIndex.Value); 35 | } 36 | arguments.Add(new FFmpegArgumentItem("map", str.ToString())); 37 | return this; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/AAC.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.FFmpegLib 2 | { 3 | public class AAC : AudioCodec 4 | { 5 | public override string Name => "AAC"; 6 | public override string Lib => "aac"; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/AomAV1.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace SimpleFFmpegGUI.FFmpegLib 6 | { 7 | public class AomAV1 : VideoCodec 8 | { 9 | public override string Name => "AV1 (aom)"; 10 | public override string Lib => "libaom-av1"; 11 | public override int MaxSpeedLevel => 8; 12 | public override int DefaultSpeedLevel => 5; 13 | public override int DefaultCRF => 28; 14 | public override int MaxCRF => 63; 15 | 16 | public override double[] SpeedFPSRelationship => new[] { 0.02, 0.0677, 0.2514, 0.7954, 1.1870, 1.4515, 2.7150, 2.7180, 2.7496 }; 17 | 18 | public override FFmpegArgumentItem Speed(int speed) 19 | { 20 | base.Speed(speed); 21 | return new FFmpegArgumentItem("cpu-used", speed.ToString()); 22 | } 23 | 24 | public override IEnumerable ExtraArguments() 25 | { 26 | yield return new FFmpegArgumentItem("row-mt", "1"); 27 | 28 | //寻找将线程数切成两个最接近的数 29 | int threadCount = Environment.ProcessorCount; 30 | int best = 1; 31 | double sqrt = Math.Sqrt(threadCount); 32 | for (int i = Convert.ToInt32(sqrt); i > 0; i--) 33 | { 34 | if (threadCount / i * i == threadCount) 35 | { 36 | best = i; 37 | break; 38 | } 39 | } 40 | 41 | yield return new FFmpegArgumentItem("tiles", $"{best}x{threadCount / best}"); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/AudioCodec.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | 3 | namespace SimpleFFmpegGUI.FFmpegLib 4 | { 5 | public abstract class AudioCodec : CodecBase 6 | { 7 | public static readonly AudioCodec[] AudioCodecs = new AudioCodec[] 8 | { 9 | new AAC(), 10 | new OPUS() 11 | }; 12 | 13 | /// 14 | /// 码率 15 | /// 16 | /// 17 | /// 18 | /// 19 | public virtual FFmpegArgumentItem Bitrate(double kb) 20 | { 21 | if (kb < 0) 22 | { 23 | throw new FFmpegArgumentException("码率超出范围"); 24 | } 25 | return new FFmpegArgumentItem("b:a", $"{kb}K"); 26 | } 27 | 28 | /// 29 | /// 采样率 30 | /// 31 | /// 32 | /// 33 | /// 34 | public virtual FFmpegArgumentItem SamplingRate(int hz) 35 | { 36 | if (hz < 9600) 37 | { 38 | throw new FFmpegArgumentException("采样率超出范围"); 39 | } 40 | return new FFmpegArgumentItem("ar", hz.ToString()); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/CodecBase.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.FFmpegLib 2 | { 3 | public abstract class CodecBase 4 | { 5 | /// 6 | /// 编码名称 7 | /// 8 | public abstract string Name { get; } 9 | 10 | /// 11 | /// 编码在FFmpeg中的库名称 12 | /// 13 | public abstract string Lib { get; } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/FFmpegArgument.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.FFmpegLib 2 | { 3 | public class FFmpegArgumentItem 4 | { 5 | public FFmpegArgumentItem(string key) 6 | { 7 | Key = key; 8 | } 9 | 10 | public FFmpegArgumentItem(string key, string value) 11 | { 12 | Key = key; 13 | Value = value; 14 | } 15 | 16 | public FFmpegArgumentItem(string key, string value, string parent,char seprator) : this(key, value) 17 | { 18 | Parent = parent; 19 | Seprator = seprator; 20 | } 21 | 22 | /// 23 | /// 参数的名称,即“-”后面的内容 24 | /// 25 | public string Key { get; set; } 26 | 27 | /// 28 | /// 参数的值 29 | /// 30 | public string Value { get; set; } 31 | 32 | /// 33 | /// 如果该参数为某一参数的子参数,则该属性为父参数的Key 34 | /// 35 | public string Parent { get; set; } 36 | 37 | /// 38 | /// 如果该参数为某一参数的子参数,则该属性为划分该父参数下子参数的分隔符 39 | /// 40 | public char Seprator { get; } 41 | 42 | /// 43 | /// 用于串联多个参数 44 | /// 45 | public FFmpegArgumentItem Other { get; set; } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/FFmpegEnums.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.FFmpegLib 2 | { 3 | public class FFmpegEnums 4 | { 5 | /// 6 | /// X264、X265中的速度预设 7 | /// 8 | public readonly static string[] Presets = new[] { 9 | "veryslow", 10 | "slower", 11 | "slow", 12 | "medium", 13 | "fast", 14 | "faster", 15 | "veryfast", 16 | "superfast", 17 | "ultrafast", 18 | }; 19 | 20 | /// 21 | /// 支持的像素格式 22 | /// 23 | public readonly static string[] PixelFormats = new[] { 24 | "yuv420p", 25 | "yuvj420p", 26 | "yuv422p", 27 | "yuvj422p", 28 | "rgb24", 29 | "gray", 30 | "yuv420p10le" 31 | }; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/GeneralAudioCodec.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.FFmpegLib 2 | { 3 | public class GeneralAudioCodec : AudioCodec 4 | { 5 | public override string Name => null; 6 | public override string Lib => null; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/GeneralVideoCodec.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.FFmpegLib 5 | { 6 | public class GeneralVideoCodec : VideoCodec 7 | { 8 | public override int DefaultCRF => 5; 9 | public override int DefaultSpeedLevel => 3; 10 | public override string Lib => null; 11 | public override int MaxSpeedLevel => 10; 12 | public override string Name => null; 13 | public override int MaxCRF => 63; 14 | 15 | public override double[] SpeedFPSRelationship => new[] { 1d, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 16 | 17 | public override FFmpegArgumentItem Speed(int speed) 18 | { 19 | if (speed > MaxSpeedLevel) 20 | { 21 | throw new FFmpegArgumentException("速度值超出范围"); 22 | } 23 | return new FFmpegArgumentItem("preset", FFmpegEnums.Presets[speed]); throw new System.NotImplementedException(); 24 | } 25 | 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/OPUS.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.FFmpegLib 2 | { 3 | public class OPUS : AudioCodec 4 | { 5 | public override string Name => "OPUS"; 6 | public override string Lib => "libopus"; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/SVTAV1.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.FFmpegLib 5 | { 6 | public class SVTAV1 : VideoCodec 7 | { 8 | public override string Name => "AV1 (SVT)"; 9 | public override string Lib => "libsvtav1"; 10 | public override int MaxSpeedLevel => 8; 11 | public override int DefaultSpeedLevel => 6; 12 | public override int DefaultCRF => 28; 13 | public override int MaxCRF => 63; 14 | 15 | public override double[] SpeedFPSRelationship => new[] { 0.05, 0.05, 0.1063, 0.2032, 0.5364, 3.5965, 9.4903, 16.3359, 27.3078 }; 16 | 17 | 18 | public override FFmpegArgumentItem Speed(int speed) 19 | { 20 | base.Speed(speed); 21 | return new FFmpegArgumentItem("preset", speed.ToString()); 22 | } 23 | 24 | public override FFmpegArgumentItem AverageBitrate(double mb) 25 | { 26 | if (mb < 0) 27 | { 28 | throw new FFmpegArgumentException("平均码率超出范围"); 29 | } 30 | return new FFmpegArgumentItem("rc", "1", "svtav1-params", ':') 31 | { 32 | Other = new FFmpegArgumentItem("tbr", Convert.ToInt32(mb * 1000).ToString(), "svtav1-params", ':') 33 | }; 34 | } 35 | 36 | public override FFmpegArgumentItem MaxBitrate(double mb) 37 | { 38 | if (mb < 0) 39 | { 40 | throw new FFmpegArgumentException("最大码率超出范围"); 41 | } 42 | return new FFmpegArgumentItem("mbr", Convert.ToInt32(mb * 1000).ToString(), "svtav1-params", ':'); 43 | } 44 | public override FFmpegArgumentItem BufferSize(double mb) 45 | { 46 | throw new FFmpegArgumentException("SVTAV1编译器不支持该参数:" + nameof(BufferSize)); 47 | } 48 | public override FFmpegArgumentItem FrameRate(double fps) 49 | { 50 | if (fps < 0) 51 | { 52 | throw new FFmpegArgumentException("帧速率大小超出范围"); 53 | } 54 | return new FFmpegArgumentItem("fps", fps.ToString(), "svtav1-params", ':'); 55 | } 56 | 57 | public override FFmpegArgumentItem Pass(int pass) 58 | { 59 | throw new FFmpegArgumentException("SVTAV1暂不支持2Pass"); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/VideoFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.FFmpegLib 4 | { 5 | public enum VideoFormatType 6 | { 7 | Video, 8 | Audio, 9 | Image 10 | } 11 | /// 12 | /// 视频容器格式 13 | /// 14 | public class VideoFormat 15 | { 16 | public static readonly VideoFormat[] Formats = new[] 17 | { 18 | new VideoFormat("mp4","mp4",main:true), 19 | new VideoFormat("matroska","mkv",main:true), 20 | new VideoFormat("mov","mov",main:true), 21 | new VideoFormat("avi","avi",main:true), 22 | new VideoFormat("webm","webm",main:true), 23 | new VideoFormat("mp3","mp3",VideoFormatType.Audio,true), 24 | new VideoFormat("adts","aac",VideoFormatType.Audio,true), 25 | new VideoFormat("image2","jpg",VideoFormatType.Image,true), 26 | new VideoFormat("image2","bmp",VideoFormatType.Image,true), 27 | new VideoFormat("mpegts","ts"), 28 | new VideoFormat("ogv","ogv",VideoFormatType.Audio), 29 | new VideoFormat("ac3","ac3",VideoFormatType.Audio), 30 | new VideoFormat("wav","wav",VideoFormatType.Audio), 31 | new VideoFormat("mp2","mp2",VideoFormatType.Audio), 32 | new VideoFormat("vob","vob"), 33 | new VideoFormat("av1","av1",main:true), 34 | new VideoFormat("image2","png",VideoFormatType.Image), 35 | }; 36 | 37 | public VideoFormat() 38 | { 39 | } 40 | 41 | public VideoFormat(string name, string extension, VideoFormatType type = VideoFormatType.Video, bool main = false) 42 | { 43 | Name = name ?? throw new ArgumentNullException(nameof(name)); 44 | Extension = extension ?? throw new ArgumentNullException(nameof(extension)); 45 | Type = type; 46 | Main = main; 47 | } 48 | 49 | /// 50 | /// 容器名 51 | /// 52 | public string Name { get; set; } 53 | 54 | /// 55 | /// 格式扩展名 56 | /// 57 | public string Extension { get; set; } 58 | 59 | /// 60 | /// 格式类型 61 | /// 62 | public VideoFormatType Type { get; set; } 63 | 64 | /// 65 | /// 是否为仅音频 66 | /// 67 | public bool AudioOnly =>Type==VideoFormatType.Audio; 68 | 69 | /// 70 | /// 是否为图像 71 | /// 72 | public bool ImageOnly=>Type==VideoFormatType.Image; 73 | 74 | /// 75 | /// 是否为主要格式 76 | /// 77 | public bool Main { get; set; } 78 | } 79 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/X264.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | 3 | namespace SimpleFFmpegGUI.FFmpegLib 4 | { 5 | public class X264 : VideoCodec 6 | { 7 | public readonly static string[] Profiles = new string[] 8 | { 9 | "baseline", 10 | "main", 11 | "high", 12 | "high10", 13 | "high422", 14 | "high444" 15 | }; 16 | 17 | public override string Name => "H264"; 18 | public override string Lib => "libx264"; 19 | public override int MaxSpeedLevel => FFmpegEnums.Presets.Length - 1; 20 | public override int DefaultSpeedLevel => 3; 21 | public override int DefaultCRF => 23; 22 | public override int MaxCRF => 51; 23 | 24 | public override double[] SpeedFPSRelationship => new[] { 9.6405, 21.3954, 47.3501, 65.9966, 74.8024, 89.1454, 126.9204, 163.6194, 236.2421 }; 25 | 26 | 27 | public override FFmpegArgumentItem Speed(int speed) 28 | { 29 | base.Speed(speed); 30 | return new FFmpegArgumentItem("preset", FFmpegEnums.Presets[speed]); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/X265.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | 3 | namespace SimpleFFmpegGUI.FFmpegLib 4 | { 5 | public class X265 : VideoCodec 6 | { 7 | public readonly static string[] Profiles = new string[] 8 | { 9 | "main", 10 | "main444-8", 11 | "main10", 12 | "main422-10", 13 | "main444-10" 14 | }; 15 | 16 | public override string Name => "H265"; 17 | public override string Lib => "libx265"; 18 | public override int MaxSpeedLevel => FFmpegEnums.Presets.Length - 1; 19 | public override int DefaultSpeedLevel => 3; 20 | public override int DefaultCRF => 28; 21 | public override int MaxCRF => 51; 22 | 23 | public override double[] SpeedFPSRelationship => new[] { 1.5142, 2.6010, 11.5024, 33.7668, 34.5906, 42.4121, 42.5331, 61.8358, 80.5850 }; 24 | 25 | 26 | public override FFmpegArgumentItem Speed(int speed) 27 | { 28 | base.Speed(speed); 29 | return new FFmpegArgumentItem("preset", FFmpegEnums.Presets[speed]); 30 | } 31 | 32 | public override FFmpegArgumentItem Pass(int pass) 33 | { 34 | base.Pass(pass); 35 | return new FFmpegArgumentItem("pass", pass.ToString(), "x265-params", ':'); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/FFmpegLib/XVP9.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.FFmpegArgument; 2 | using System.Collections.Generic; 3 | 4 | namespace SimpleFFmpegGUI.FFmpegLib 5 | { 6 | public class XVP9 : VideoCodec 7 | { 8 | public override string Name => "VP9"; 9 | public override string Lib => "libvpx-vp9"; 10 | public override int MaxSpeedLevel => 5; 11 | public override int DefaultSpeedLevel => 3; 12 | public override int DefaultCRF => 23; 13 | public override int MaxCRF => 63; 14 | 15 | public override double[] SpeedFPSRelationship => new[] { 2.1692, 6.0832, 6.0899, 9.7819, 12.6956, 14.1973, 9.2309 }; 16 | 17 | 18 | public override FFmpegArgumentItem Speed(int speed) 19 | { 20 | base.Speed(speed); 21 | return new FFmpegArgumentItem("cpu-used", speed.ToString()); 22 | } 23 | public override IEnumerable ExtraArguments() 24 | { 25 | yield return new FFmpegArgumentItem("row-mt", "1"); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Manager/ConfigManager.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using SimpleFFmpegGUI.Model; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace SimpleFFmpegGUI.Manager 10 | { 11 | public class ConfigManager 12 | { 13 | public const string DefaultProcessPriorityKey = "DefaultProcessPriority"; 14 | private static readonly Dictionary cache = new Dictionary(); 15 | 16 | private readonly FFmpegDbContext db; 17 | 18 | public ConfigManager(FFmpegDbContext db) 19 | { 20 | this.db = db; 21 | } 22 | 23 | public int DefaultProcessPriority 24 | { 25 | get => GetConfig(DefaultProcessPriorityKey, 2); 26 | set => SetConfig(DefaultProcessPriorityKey, value); 27 | } 28 | 29 | public T GetConfig(string key, T defaultValue) 30 | { 31 | if (cache.ContainsKey(key)) 32 | { 33 | return (T)cache[key]; 34 | } 35 | var item = db.Configs.Where(p => p.Key == key).FirstOrDefault(); 36 | if (item == null) 37 | { 38 | return defaultValue; 39 | } 40 | T value = Parse(item.Value); 41 | cache.Add(key, value); 42 | 43 | Logger.Info($"读取配置:[{key}]={value}"); 44 | return value; 45 | } 46 | public void SetConfig(string key, T value) 47 | { 48 | if (cache.ContainsKey(key)) 49 | { 50 | cache[key] = value; 51 | } 52 | var item = db.Configs.Where(p => p.Key == key).FirstOrDefault(); 53 | if (item == null) 54 | { 55 | item = new Config() 56 | { 57 | Key = key, 58 | Value = GetString(value) 59 | }; 60 | db.Configs.Add(item); 61 | } 62 | else 63 | { 64 | item.Value = GetString(value); 65 | db.Entry(item).State = Microsoft.EntityFrameworkCore.EntityState.Modified; 66 | } 67 | 68 | db.SaveChanges(); 69 | 70 | Logger.Info($"写入配置:[{key}]={value}"); 71 | } 72 | 73 | private static string GetString(T data) 74 | { 75 | return JsonConvert.SerializeObject(data); 76 | } 77 | 78 | private static T Parse(string data) 79 | { 80 | return JsonConvert.DeserializeObject(data); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Manager/FFmpegOutputEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Manager 2 | { 3 | public class FFmpegOutputEventArgs 4 | { 5 | public FFmpegOutputEventArgs() 6 | { 7 | } 8 | 9 | public FFmpegOutputEventArgs(string data) 10 | { 11 | Data = data; 12 | } 13 | 14 | public string Data { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Manager/LogManager.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using SimpleFFmpegGUI.Dto; 3 | using SimpleFFmpegGUI.Model; 4 | using System; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace SimpleFFmpegGUI.Manager 9 | { 10 | public class LogManager 11 | { 12 | private readonly FFmpegDbContext db; 13 | 14 | public LogManager(FFmpegDbContext db) 15 | { 16 | this.db = db; 17 | } 18 | 19 | public async Task> GetLogsAsync(char? type = null, 20 | int taskId = 0, 21 | DateTime? from = null, 22 | DateTime? to = null, 23 | int skip = 0, 24 | int take = 0) 25 | { 26 | await Logger.SaveAllAsync(); 27 | 28 | IQueryable logs = db.Logs; 29 | if (type.HasValue) 30 | { 31 | logs = logs.Where(p => p.Type == type.Value); 32 | } 33 | if (from.HasValue) 34 | { 35 | logs = logs.Where(p => p.Time > from.Value); 36 | } 37 | if (to.HasValue) 38 | { 39 | logs = logs.Where(p => p.Time < to.Value); 40 | } 41 | if (taskId != 0) 42 | { 43 | logs = logs.Where(p => p.TaskId == taskId); 44 | } 45 | logs = logs.OrderByDescending(p => p.Time); 46 | int count = await logs.CountAsync(); 47 | if (skip > 0) 48 | { 49 | logs = logs.Skip(skip); 50 | } 51 | if (take > 0) 52 | { 53 | logs = logs.Take(take); 54 | } 55 | return new PagedListDto(await logs.ToListAsync(), count); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Manager/ProcessChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.Manager 4 | { 5 | public class ProcessChangedEventArgs : EventArgs 6 | { 7 | public ProcessChangedEventArgs(FFmpegProcess oldProcess, FFmpegProcess newProcess) 8 | { 9 | OldProcess = oldProcess; 10 | NewProcess = newProcess; 11 | } 12 | 13 | public FFmpegProcess NewProcess { get; } 14 | public FFmpegProcess OldProcess { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Manager/ProcessExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace SimpleFFmpegGUI.Manager 6 | { 7 | public static class ProcessExtension 8 | { 9 | [Flags] 10 | public enum ThreadAccess : int 11 | { 12 | TERMINATE = (0x0001), 13 | SUSPEND_RESUME = (0x0002), 14 | GET_CONTEXT = (0x0008), 15 | SET_CONTEXT = (0x0010), 16 | SET_INFORMATION = (0x0020), 17 | QUERY_INFORMATION = (0x0040), 18 | SET_THREAD_TOKEN = (0x0080), 19 | IMPERSONATE = (0x0100), 20 | DIRECT_IMPERSONATION = (0x0200) 21 | } 22 | 23 | [DllImport("kernel32.dll")] 24 | private static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); 25 | 26 | [DllImport("kernel32.dll")] 27 | private static extern uint SuspendThread(IntPtr hThread); 28 | 29 | [DllImport("kernel32.dll")] 30 | private static extern int ResumeThread(IntPtr hThread); 31 | 32 | [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] 33 | private static extern bool CloseHandle(IntPtr handle); 34 | 35 | public static void SuspendProcess(int pid) 36 | { 37 | var process = Process.GetProcessById(pid); // throws exception if process does not exist 38 | 39 | foreach (ProcessThread pT in process.Threads) 40 | { 41 | IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); 42 | 43 | if (pOpenThread == IntPtr.Zero) 44 | { 45 | continue; 46 | } 47 | 48 | SuspendThread(pOpenThread); 49 | 50 | CloseHandle(pOpenThread); 51 | } 52 | } 53 | 54 | public static void ResumeProcess(int pid) 55 | { 56 | var process = Process.GetProcessById(pid); 57 | 58 | if (process.ProcessName == string.Empty) 59 | return; 60 | 61 | foreach (ProcessThread pT in process.Threads) 62 | { 63 | IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); 64 | 65 | if (pOpenThread == IntPtr.Zero) 66 | { 67 | continue; 68 | } 69 | 70 | var suspendCount = 0; 71 | do 72 | { 73 | suspendCount = ResumeThread(pOpenThread); 74 | } while (suspendCount > 0); 75 | 76 | CloseHandle(pOpenThread); 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/AudioCodeArguments.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System.ComponentModel; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class AudioCodeArguments : IAudioCodeArguments 7 | { 8 | /// 9 | /// 码率 10 | /// 11 | public int? Bitrate { get; set; } 12 | 13 | /// 14 | /// 编码 15 | /// 16 | public string Code { get; set; } 17 | 18 | /// 19 | /// 采样率 20 | /// 21 | public int? SamplingRate { get; set; } 22 | } 23 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/CodePreset.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System.ComponentModel; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class CodePreset : ModelBase, INotifyPropertyChanged 7 | { 8 | private bool @default; 9 | private OutputArguments arguments; 10 | private string name; 11 | private TaskType type; 12 | 13 | public event PropertyChangedEventHandler PropertyChanged; 14 | 15 | /// 16 | /// 输出参数 17 | /// 18 | public OutputArguments Arguments 19 | { 20 | get => arguments; 21 | set => this.SetValueAndNotify(ref arguments, value, nameof(Arguments)); 22 | } 23 | 24 | /// 25 | /// 是否为该类中的默认预设 26 | /// 27 | public bool Default 28 | { 29 | get => @default; 30 | set => this.SetValueAndNotify(ref @default, value, nameof(Default)); 31 | } 32 | 33 | /// 34 | /// 预设名 35 | /// 36 | public string Name 37 | { 38 | get => name; 39 | set => this.SetValueAndNotify(ref name, value, nameof(Name)); 40 | } 41 | 42 | /// 43 | /// 预设对应的类型 44 | /// 45 | public TaskType Type 46 | { 47 | get => type; 48 | set => this.SetValueAndNotify(ref type, value, nameof(Type)); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/CombineArguments.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System.ComponentModel; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class CombineArguments : INotifyPropertyChanged 7 | { 8 | private bool shortest; 9 | 10 | public bool Shortest 11 | { 12 | get => shortest; 13 | set => this.SetValueAndNotify(ref shortest, value, nameof(Shortest)); 14 | } 15 | 16 | public event PropertyChangedEventHandler PropertyChanged; 17 | } 18 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/Config.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class Config : ModelBase 7 | { 8 | public Config() 9 | { 10 | } 11 | 12 | public Config(string key, string value) 13 | { 14 | Key = key; 15 | Value = value; 16 | } 17 | 18 | public string Key { get; set; } 19 | public string Value { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/EfJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 2 | using Newtonsoft.Json; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class EFJsonConverter : ValueConverter 7 | { 8 | public EFJsonConverter() : base(p => JsonConvert.SerializeObject(p), p => JsonConvert.DeserializeObject(p)) 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/IAudioCodeArguments.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public interface IAudioCodeArguments 4 | { 5 | int? Bitrate { get; set; } 6 | string Code { get; set; } 7 | int? SamplingRate { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/IInputArguments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.Model 4 | { 5 | public interface IInputArguments 6 | { 7 | TimeSpan? Duration { get; set; } 8 | string Extra { get; set; } 9 | string FilePath { get; set; } 10 | string Format { get; set; } 11 | double? Framerate { get; set; } 12 | TimeSpan? From { get; set; } 13 | bool Image2 { get; set; } 14 | TimeSpan? To { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/IModel.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public interface IModel 4 | { 5 | int Id { get; set; } 6 | 7 | bool IsDeleted { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/IVideoCodeArguments.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public interface IVideoCodeArguments 4 | { 5 | string AspectRatio { get; set; } 6 | double? AverageBitrate { get; set; } 7 | string Code { get; set; } 8 | int? Crf { get; set; } 9 | double? Fps { get; set; } 10 | double? MaxBitrate { get; set; } 11 | double? MaxBitrateBuffer { get; set; } 12 | string PixelFormat { get; set; } 13 | int Preset { get; set; } 14 | string Size { get; set; } 15 | bool TwoPass { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/InputArguments.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System; 3 | using System.ComponentModel; 4 | 5 | namespace SimpleFFmpegGUI.Model 6 | { 7 | public class InputArguments : IInputArguments 8 | { 9 | /// 10 | /// 持续时间 11 | /// 12 | public TimeSpan? Duration { get; set; } 13 | 14 | /// 15 | /// 其他参数 16 | /// 17 | public string Extra { get; set; } 18 | 19 | /// 20 | /// 输入文件的路径 21 | /// 22 | public string FilePath { get; set; } 23 | 24 | /// 25 | /// 输入格式 26 | /// 27 | public string Format { get; set; } 28 | 29 | /// 30 | /// 输入帧率(主要针对图像序列) 31 | /// 32 | public double? Framerate { get; set; } 33 | 34 | /// 35 | /// 开始时间 36 | /// 37 | public TimeSpan? From { get; set; } 38 | /// 39 | /// 输入是否为图像帧序列 40 | /// 41 | public bool Image2 { get; set; } 42 | 43 | /// 44 | /// 结束时间 45 | /// 46 | public TimeSpan? To { get; set; } 47 | } 48 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.Model 4 | { 5 | public class Log : ModelBase 6 | { 7 | public Log() 8 | { 9 | } 10 | 11 | public DateTime Time { get; set; } 12 | public char Type { get; set; } 13 | public string Message { get; set; } 14 | public int? TaskId { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoAudio.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.Model.MediaInfo 5 | { 6 | public class MediaInfoAudio : MediaInfoTrackBase 7 | { 8 | public int AlternateGroup { get; set; } 9 | public string BitRate_Mode { get; set; } 10 | public string ChannelLayout { get; set; } 11 | public string ChannelPositions { get; set; } 12 | public int Channels { get; set; } 13 | public string Compression_Mode { get; set; } 14 | public double Delay { get; set; } 15 | public string Delay_DropFrame { get; set; } 16 | public string Delay_Source { get; set; } 17 | public string Format_AdditionalFeatures { get; set; } 18 | public int FrameCount { get; set; } 19 | public int SamplesPerFrame { get; set; } 20 | public long SamplingCount { get; set; } 21 | public int SamplingRate { get; set; } 22 | public double Video_Delay { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoGeneral.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SimpleFFmpegGUI.Model.MediaInfo 9 | { 10 | public class MediaInfoGeneral 11 | { 12 | public List Audios { get; set; } = new List(); 13 | public string CodecID { get; set; } 14 | public string CodecID_Compatible { get; set; } 15 | public long DataSize { get; set; } 16 | [JsonIgnore] 17 | public TimeSpan Duration => TimeSpan.FromSeconds(DurationSeconds); 18 | 19 | [JsonProperty("Duration")] 20 | public double DurationSeconds { get; set; } 21 | 22 | public string Encoded_Application { get; set; } 23 | public string FileExtension { get; set; } 24 | public long FileSize { get; set; } 25 | public long FooterSize { get; set; } 26 | public string Format { get; set; } 27 | public string Format_Profile { get; set; } 28 | public int FrameCount { get; set; } 29 | public double FrameRate { get; set; } 30 | public int HeaderSize { get; set; } 31 | public string IsStreamable { get; set; } 32 | public int OverallBitRate { get; set; } 33 | public string Raw { get; set; } 34 | public long StreamSize { get; set; } 35 | public List Texts { get; set; } = new List(); 36 | public List Videos { get; set; } = new List(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoImage.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model.MediaInfo 2 | { 3 | public class MediaInfoImage: MediaInfoTrackBase 4 | { 5 | public string ColorSpace { get; set; } 6 | public string Compression_Mode { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoItem.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public class MediaInfoItem 4 | { 5 | public string Name { get; set; } 6 | public object Value { get; set; } 7 | public MediaInfoItem() 8 | { 9 | 10 | } 11 | public MediaInfoItem(string name, string value) 12 | { 13 | Name = name; 14 | Value = value; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoText.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.Model.MediaInfo 5 | { 6 | public class MediaInfoText : MediaInfoTrackBase 7 | { 8 | public int ElementCount { get; set; } 9 | public string Forced { get; set; } 10 | public int FrameCount { get; set; } 11 | public string Language { get; set; } 12 | public string Title { get; set; } 13 | public int Typeorder { get; set; } 14 | public long UniqueID { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoTrackBase.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace SimpleFFmpegGUI.Model.MediaInfo 5 | { 6 | public class MediaInfoTrackBase 7 | { 8 | public string BitDepth { get; set; } 9 | 10 | public int BitRate { get; set; } 11 | 12 | public string CodecID { get; set; } 13 | 14 | public string Default { get; set; } 15 | 16 | [JsonIgnore] 17 | public TimeSpan Duration => TimeSpan.FromSeconds(DurationSeconds); 18 | 19 | [JsonProperty("Duration")] 20 | public double DurationSeconds { get; set; } 21 | public string Format { get; set; } 22 | public double FrameRate { get; set; } 23 | public int Height { get; set; } 24 | public int ID { get; set; } 25 | public int Index { get; set; } 26 | public int StreamOrder { get; set; } 27 | public long StreamSize { get; set; } 28 | public int Width { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/MediaInfo/MediaInfoVideo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SimpleFFmpegGUI.Model.MediaInfo 4 | { 5 | public class MediaInfoVideo : MediaInfoTrackBase 6 | { 7 | public int BitRate_Maximum { get; set; } 8 | public string ChromaSubsampling { get; set; } 9 | public string ColorSpace { get; set; } 10 | public double Delay { get; set; } 11 | public string Delay_DropFrame { get; set; } 12 | public string Delay_Settings { get; set; } 13 | public string Delay_Source { get; set; } 14 | public double DisplayAspectRatio { get; set; } 15 | public string Encoded_Library { get; set; } 16 | public string Encoded_Library_Name { get; set; } 17 | public string Encoded_Library_Settings { get; set; } 18 | public string Encoded_Library_Version { get; set; } 19 | public List EncodingSettings { get; set; } 20 | public double Format_Level { get; set; } 21 | public string Format_Profile { get; set; } 22 | public string Format_Tier { get; set; } 23 | public int FrameCount { get; set; } 24 | public string FrameRate_Mode { get; set; } 25 | public double PixelAspectRatio { get; set; } 26 | public double Rotation { get; set; } 27 | public int Sampled_Height { get; set; } 28 | public int Sampled_Width { get; set; } 29 | public string ScanType { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/ModelBase.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace SimpleFFmpegGUI.Model 4 | { 5 | public abstract class ModelBase : IModel 6 | { 7 | [Key] 8 | public int Id { get; set; } 9 | 10 | public bool IsDeleted { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/NameDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.Model 4 | { 5 | [AttributeUsage(AttributeTargets.All)] 6 | public class NameDescriptionAttribute : Attribute 7 | { 8 | public NameDescriptionAttribute() 9 | { 10 | } 11 | 12 | public NameDescriptionAttribute(string name, string description) 13 | { 14 | Name = name; 15 | Description = description; 16 | } 17 | 18 | public string Name { get; set; } 19 | public string Description { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/OutputArguments.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System.ComponentModel; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class OutputArguments : INotifyPropertyChanged 7 | { 8 | private AudioCodeArguments audio; 9 | private CombineArguments combine; 10 | private bool disableAudio; 11 | private bool disableVideo; 12 | private string extra; 13 | private string format; 14 | private StreamArguments stream; 15 | private VideoCodeArguments video; 16 | 17 | public event PropertyChangedEventHandler PropertyChanged; 18 | 19 | /// 20 | /// 音频参数 21 | /// 22 | public AudioCodeArguments Audio 23 | { 24 | get => audio; 25 | set => this.SetValueAndNotify(ref audio, value, nameof(Audio)); 26 | } 27 | 28 | /// 29 | /// 音视频合并参数 30 | /// 31 | public CombineArguments Combine 32 | { 33 | get => combine; 34 | set => this.SetValueAndNotify(ref combine, value, nameof(Combine)); 35 | } 36 | 37 | /// 38 | /// 是否禁用音频 39 | /// 40 | public bool DisableAudio 41 | { 42 | get => disableAudio; 43 | set => this.SetValueAndNotify(ref disableAudio, value, nameof(DisableAudio)); 44 | } 45 | 46 | /// 47 | /// 是否禁用视频(画面) 48 | /// 49 | public bool DisableVideo 50 | { 51 | get => disableVideo; 52 | set => this.SetValueAndNotify(ref disableVideo, value, nameof(DisableVideo)); 53 | } 54 | 55 | /// 56 | /// 额外参数 57 | /// 58 | public string Extra 59 | { 60 | get => extra; 61 | set => this.SetValueAndNotify(ref extra, value, nameof(Extra)); 62 | } 63 | 64 | /// 65 | /// 容器格式(后缀名) 66 | /// 67 | public string Format 68 | { 69 | get => format; 70 | set => this.SetValueAndNotify(ref format, value, nameof(Format)); 71 | } 72 | 73 | /// 74 | /// 流参数 75 | /// 76 | public StreamArguments Stream 77 | { 78 | get => stream; 79 | set => this.SetValueAndNotify(ref stream, value, nameof(Stream)); 80 | } 81 | 82 | /// 83 | /// 视频参数 84 | /// 85 | public VideoCodeArguments Video 86 | { 87 | get => video; 88 | set => this.SetValueAndNotify(ref video, value, nameof(Video)); 89 | } 90 | 91 | public ProcessedOptions ProcessedOptions { get; set; } = new ProcessedOptions(); 92 | } 93 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/ProcessedOptions.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public class ProcessedOptions 4 | { 5 | /// 6 | /// 将输出文件的修改时间设置为最后一个输入文件的修改时间 7 | /// 8 | public bool SyncModifiedTime { get; set; } 9 | 10 | /// 11 | /// 处理后删除输入文件。若可以,将删除到回收站。 12 | /// 13 | public bool DeleteInputFiles { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/StreamArguments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SimpleFFmpegGUI.Model 4 | { 5 | public class StreamArguments 6 | { 7 | public List Maps { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/StreamChannel.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public enum StreamChannel 4 | { 5 | Video = 0x01, 6 | Audio = 0x02, 7 | Subtitle = 0x04, 8 | All = Video | Audio | Subtitle, 9 | } 10 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/StreamMapInfo.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public class StreamMapInfo 4 | { 5 | /// 6 | /// 输入文件的序号 7 | /// 8 | public int InputIndex { get; set; } 9 | /// 10 | /// 指定的通道 11 | /// 12 | public StreamChannel Channel { get; set; } 13 | /// 14 | /// 在指定文件(和通道)中,选取的流的序号 15 | /// 16 | public int? StreamIndex { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/TaskInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | /// 7 | /// FFmpeg任务 8 | /// 9 | public class TaskInfo : ModelBase 10 | { 11 | public TaskInfo() 12 | { 13 | CreateTime = DateTime.Now; 14 | Status = TaskStatus.Queue; 15 | } 16 | 17 | /// 18 | /// 任务类型 19 | /// 20 | public TaskType Type { get; set; } 21 | 22 | /// 23 | /// 当前任务状态 24 | /// 25 | public TaskStatus Status { get; set; } 26 | 27 | /// 28 | /// 输入文件和参数 29 | /// 30 | public List Inputs { get; set; } 31 | 32 | /// 33 | /// 指定的输出路径 34 | /// 35 | public string Output { get; set; } 36 | 37 | /// 38 | /// 实际的输出路径 39 | /// 40 | public string RealOutput { get; set; } 41 | 42 | /// 43 | /// 输出参数 44 | /// 45 | public OutputArguments Arguments { get; set; } 46 | 47 | /// 48 | /// 任务创建时间 49 | /// 50 | public DateTime CreateTime { get; set; } 51 | 52 | /// 53 | /// 开始时间 54 | /// 55 | public DateTime? StartTime { get; set; } 56 | 57 | /// 58 | /// 结束时间 59 | /// 60 | public DateTime? FinishTime { get; set; } 61 | 62 | /// 63 | /// 相关信息,包括错误信息、执行结果等 64 | /// 65 | public string Message { get; set; } 66 | 67 | /// 68 | /// 执行时FFmpeg的执行参数 69 | /// 70 | public string FFmpegArguments { get; set; } 71 | } 72 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/TaskStatus.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace SimpleFFmpegGUI.Model 4 | { 5 | public enum TaskStatus 6 | { 7 | [Description("排队中")] 8 | Queue = 1, 9 | 10 | [Description("处理中")] 11 | Processing = 2, 12 | 13 | [Description("已完成")] 14 | Done = 3, 15 | 16 | [Description("发生错误")] 17 | Error = 4, 18 | 19 | [Description("取消")] 20 | Cancel = 5 21 | } 22 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/TaskType.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.Model 2 | { 3 | public enum TaskType 4 | { 5 | [NameDescription("转码", "将视频重新编码")] 6 | Code = 0, 7 | 8 | [NameDescription("合并视音频", "将视频和音频合并为一个文件")] 9 | Combine = 1, 10 | 11 | [NameDescription("视频比较", "比较两个视频之间的一致性")] 12 | Compare = 2, 13 | 14 | [NameDescription("自定义参数", "完全自定义参数")] 15 | Custom = 3, 16 | 17 | [NameDescription("视频拼接", "将多个视频首尾相连生成一个视频")] 18 | Concat = 4, 19 | 20 | //[NameDescription("帧转视频","将一系列的图片转为视频")] 21 | //Frame2Video=5, 22 | } 23 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/Model/VideoCodeArguments.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System.ComponentModel; 3 | 4 | namespace SimpleFFmpegGUI.Model 5 | { 6 | public class VideoCodeArguments : IVideoCodeArguments 7 | { 8 | 9 | /// 10 | /// 画面比例 11 | /// 12 | public string AspectRatio{ get; set; } 13 | 14 | /// 15 | /// 平均码率 16 | /// 17 | public double? AverageBitrate { get; set; } 18 | 19 | /// 20 | /// 编码 21 | /// 22 | public string Code { get; set; } 23 | 24 | /// 25 | /// CRF(视频目标质量) 26 | /// 27 | public int? Crf { get; set; } 28 | 29 | /// 30 | /// 帧率 31 | /// 32 | public double? Fps { get; set; } 33 | 34 | /// 35 | /// 最大码率 36 | /// 37 | public double? MaxBitrate { get; set; } 38 | 39 | /// 40 | /// 最大码率缓冲倍率 41 | /// 42 | public double? MaxBitrateBuffer { get; set; } 43 | 44 | /// 45 | /// 像素格式 46 | /// 47 | public string PixelFormat { get; set; } 48 | 49 | /// 50 | /// 编码速度或速度预设 51 | /// 52 | public int Preset { get; set; } 53 | 54 | /// 55 | /// 视频尺寸(分辨率) 56 | /// 57 | public string Size { get; set; } 58 | 59 | /// 60 | /// 是否二次编码 61 | /// 62 | public bool TwoPass { get; set; } 63 | } 64 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Core/SimpleFFmpegGUI.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | SimpleFFmpegGUI 6 | false 7 | 8 | 9 | 10 | ..\Generation\Debug\Core 11 | 12 | 13 | 14 | ..\Generation\Release\Core 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ..\libs\FzStandardLib.dll 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.Console/Options.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | 3 | namespace SimpleFFmpegGUI 4 | { 5 | internal class Options 6 | { 7 | [Option('p', Required = false, HelpText = "命名管道名称")] 8 | public string PipeName { get; set; } 9 | 10 | [Option('d', Default = false, Required = false, HelpText = "设置工作目录为程序所在目录")] 11 | public bool WorkingDirectoryHere { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.Console/Program.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using Microsoft.Extensions.Hosting; 3 | using SimpleFFmpegGUI; 4 | using System.Runtime.InteropServices; 5 | 6 | string pipeName = Startup.DefaultPipeName; 7 | Parser.Default.ParseArguments(args) 8 | .WithParsed(o => 9 | { 10 | if (o.PipeName != null) 11 | { 12 | Console.WriteLine($"管道名设置为: {o.PipeName}"); 13 | pipeName = o.PipeName; 14 | } 15 | else 16 | { 17 | Console.WriteLine($"管道名未设置,默认为: {Startup.DefaultPipeName}"); 18 | } 19 | if (o.WorkingDirectoryHere) 20 | { 21 | FzLib.Program.App.SetWorkingDirectoryToAppPath(); 22 | Console.WriteLine("工作目录设置为程序目录:" + FzLib.Program.App.ProgramDirectoryPath); 23 | } 24 | }); 25 | 26 | var builder = Host.CreateDefaultBuilder(args); 27 | ConsoleLogger.StartListen(); 28 | Startup.InitializeServices(builder,pipeName); 29 | builder.Build().Run(); -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.Console/SimpleFFmpegGUI.Host.Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | disable 8 | false 9 | 10 | 11 | 12 | ..\Generation\Debug\Host 13 | 14 | 15 | 16 | ..\Generation\Release\Host 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | ..\libs\FzStandardLib.dll 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/CreateWindowsService.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | net session >nul 2>&1 4 | if %errorlevel% neq 0 ( 5 | echo need Administrator 6 | 7 | powershell -Command "Start-Process '%~0' -Verb RunAs" 8 | exit /b 9 | ) 10 | 11 | 12 | set "current_dir=%~dp0" 13 | set "service_name=SimpleFFmpegService" 14 | set "exe_path=%current_dir%SimpleFFmpegGUI.Host.WindowsService.exe" 15 | 16 | sc create %service_name% binPath= "%exe_path%" start= auto 17 | sc start %service_name% 18 | 19 | echo Service has been created and set to start automatically at system boot. 20 | pause 21 | 22 | 23 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/DeleteWindowsService.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | net session >nul 2>&1 4 | if %errorlevel% neq 0 ( 5 | echo need Administrator 6 | 7 | powershell -Command "Start-Process '%~0' -Verb RunAs" 8 | exit /b 9 | ) 10 | 11 | 12 | set "service_name=SimpleFFmpegService" 13 | sc stop %service_name% 14 | sc delete %service_name% 15 | 16 | echo Service has been deleted. 17 | pause 18 | 19 | 20 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/Program.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI; 2 | using SimpleFFmpegGUI.Host.Service; 3 | using System.Diagnostics; 4 | 5 | var builder = Host.CreateDefaultBuilder(args); 6 | builder.ConfigureServices(services => 7 | { 8 | services.AddHostedService(); 9 | services.AddWindowsService(options => 10 | { 11 | options.ServiceName = "SimpleFFmpegHost"; 12 | }); 13 | }); 14 | Directory.SetCurrentDirectory(Path.GetDirectoryName(Environment.ProcessPath)); 15 | Startup.InitializeServices(builder); 16 | builder.Build().Run(); 17 | 18 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/SimpleFFmpegGUI.Host.WindowsService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | false 6 | enable 7 | dotnet-SimpleFFmpegGUI.Host.Service-8825e1c0-f6ff-4b92-b814-abf06f255eec 8 | 9 | 10 | 11 | ..\Generation\Debug\Host 12 | 13 | 14 | 15 | ..\Generation\Release\Host 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Always 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/Worker.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | 4 | namespace SimpleFFmpegGUI.Host.Service 5 | { 6 | public class Worker : BackgroundService 7 | { 8 | //private readonly ILogger _logger; 9 | //private Process _process; 10 | 11 | //public Worker(ILogger logger) 12 | //{ 13 | // _logger = logger; 14 | //} 15 | 16 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 17 | { 18 | while (!stoppingToken.IsCancellationRequested) 19 | { 20 | await Task.Delay(1000, stoppingToken); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host.WindowsService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host/ConsoleLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI 4 | { 5 | public class ConsoleLogger 6 | { 7 | private static ConsoleLogger instance; 8 | 9 | private ConsoleLogger() 10 | { 11 | Logger.Log += Logger_Log; 12 | } 13 | 14 | public static void StartListen() 15 | { 16 | if (instance == null) 17 | { 18 | instance = new ConsoleLogger(); 19 | } 20 | } 21 | 22 | private string lastLogContent = null; 23 | private void Logger_Log(object sender, LogEventArgs e) 24 | { 25 | ConsoleColor defaultColor = Console.ForegroundColor; 26 | ConsoleColor color = e.Log.Type switch 27 | { 28 | 'E' => ConsoleColor.Red, 29 | 'D' => defaultColor, 30 | 'I' => defaultColor, 31 | 'W' => ConsoleColor.Yellow, 32 | 'O' => ConsoleColor.Gray, 33 | _ => defaultColor 34 | }; 35 | string type = e.Log.Type switch 36 | { 37 | 'E' => "错误", 38 | 'D' => "调试", 39 | 'I' => "信息", 40 | 'W' => "警告", 41 | 'O' => "输出", 42 | _ => e.Log.Type.ToString().PadLeft(2) 43 | }; 44 | string time = e.Log.Time.ToString("yyyy-MM-dd HH:mm:ss"); 45 | 46 | if (lastLogContent != null && lastLogContent.StartsWith("frame=") && e.Log.Message.StartsWith("frame=")) 47 | { 48 | ClearLine(); 49 | } 50 | 51 | Console.Write(time); 52 | Console.Write(" \t"); 53 | Console.ForegroundColor = color; 54 | Console.Write(type); 55 | Console.ForegroundColor = defaultColor; 56 | Console.Write(" \t"); 57 | Console.Write(e.Log.Message); 58 | Console.WriteLine(); 59 | 60 | 61 | lastLogContent = e.Log.Message; 62 | } 63 | static void ClearLine() 64 | { 65 | Console.SetCursorPosition(0, Console.CursorTop); 66 | Console.Write(new string(' ', Console.WindowWidth)); 67 | Console.SetCursorPosition(0, Console.CursorTop - 1); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host/FtpManager.cs: -------------------------------------------------------------------------------- 1 | using FubarDev.FtpServer; 2 | using FubarDev.FtpServer.FileSystem.DotNet; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Net.Sockets; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace SimpleFFmpegGUI 14 | { 15 | internal class FtpManager : IDisposable 16 | { 17 | internal FtpManager(string path, int port) 18 | { 19 | if (port <= 0) 20 | { 21 | port = FreeTcpPort(); 22 | } 23 | var services = new ServiceCollection(); 24 | 25 | services.Configure(opt => opt 26 | .RootPath = path); 27 | 28 | services.AddFtpServer(builder => builder 29 | .UseDotNetFileSystem() 30 | .EnableAnonymousAuthentication()); 31 | 32 | services.Configure(opt => opt.Port = port); 33 | 34 | serviceProvider = services.BuildServiceProvider(); 35 | ftpServerHost = serviceProvider.GetRequiredService(); 36 | Path = path; 37 | Port = port; 38 | } 39 | 40 | private readonly IFtpServerHost ftpServerHost; 41 | private readonly ServiceProvider serviceProvider; 42 | 43 | public string Path { get; } 44 | public int Port { get; } 45 | 46 | public Task StartAsync() 47 | { 48 | if (ftpServerHost == null) 49 | { 50 | throw new NullReferenceException("请先初始化"); 51 | } 52 | return ftpServerHost.StartAsync(CancellationToken.None); 53 | } 54 | 55 | public Task StopAsync() 56 | { 57 | if (ftpServerHost == null) 58 | { 59 | throw new NullReferenceException("请先初始化"); 60 | } 61 | return ftpServerHost.StopAsync(CancellationToken.None); 62 | } 63 | 64 | public static int FreeTcpPort() 65 | { 66 | TcpListener l = new TcpListener(IPAddress.Loopback, 0); 67 | l.Start(); 68 | int port = ((IPEndPoint)l.LocalEndpoint).Port; 69 | l.Stop(); 70 | return port; 71 | } 72 | 73 | public void Dispose() 74 | { 75 | serviceProvider?.Dispose(); 76 | } 77 | 78 | ~FtpManager() 79 | { 80 | Dispose(); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SimpleFFmpegGUI.Host": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host/SimpleFFmpegGUI.Host.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | net8.0 6 | SimpleFFmpegGUI 7 | false 8 | 9 | 10 | 11 | ..\Generation\Debug\Host 12 | 13 | 14 | 15 | ..\Generation\Release\Host 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ..\libs\FzCoreLib.Windows.dll 36 | 37 | 38 | ..\libs\FzStandardLib.dll 39 | 40 | 41 | 42 | 43 | 44 | Always 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Host/log4net.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/App.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | yyyy-MM-dd HH:mm:ss 26 | hh\:mm\:ss\.fff 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/AttributeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace SimpleFFmpegGUI.WPF.Converters 5 | { 6 | public static class AttributeHelper 7 | { 8 | public static TResult GetAttributeValue(object obj, Func valueConverter) where T : Attribute 9 | { 10 | MemberInfo[] member = obj.GetType().GetMember(obj.ToString()); 11 | if (member != null && member.Length != 0) 12 | { 13 | object[] customAttributes = member[0].GetCustomAttributes(typeof(T), inherit: false); 14 | if (customAttributes != null && customAttributes.Length != 0) 15 | { 16 | return valueConverter(customAttributes[0] as T); 17 | } 18 | } 19 | 20 | throw new Exception(); 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/Bitrate2StringConverter.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using System; 3 | using System.Globalization; 4 | using System.Windows.Data; 5 | 6 | namespace SimpleFFmpegGUI.WPF.Converters 7 | { 8 | public class Bitrate2StringConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | long num = System.Convert.ToInt64(value); 13 | string str = NumberConverter.ByteToFitString(num, 2, " bps", " Kbps", " Mbps", " Gbps", " Tbps"); 14 | return str; 15 | } 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/CountEqualsOneValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace SimpleFFmpegGUI.WPF.Converters 7 | { 8 | public class CountEqualsOneValueConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (1.Equals(value)) 13 | { 14 | return Visibility.Visible; 15 | } 16 | return Visibility.Hidden; 17 | } 18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/EmptyIfZeroConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | 5 | namespace SimpleFFmpegGUI.WPF.Converters 6 | { 7 | 8 | public class EmptyIfZeroConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value is 0 or 0d) 13 | { 14 | return ""; 15 | } 16 | return value; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/HourMinSecTimeSpanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | namespace SimpleFFmpegGUI.WPF.Converters 11 | { 12 | public class HourMinSecTimeSpanConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | Debug.Assert(value is TimeSpan); 17 | TimeSpan t = (TimeSpan)value; 18 | if("1".Equals(parameter)) 19 | { 20 | return $"{t.Days * 24 + t.Hours}:{t.Minutes:00}:{t.Seconds:00}.{t.Milliseconds/10:00}"; 21 | } 22 | return $"{t.Days * 24 + t.Hours}:{t.Minutes:00}:{t.Seconds:00}"; 23 | } 24 | 25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/Index2StringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | 9 | namespace SimpleFFmpegGUI.WPF.Converters 10 | { 11 | public class Index2StringConverter : IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (targetType != typeof(string)) 16 | { 17 | throw new ArgumentException("目标类型应为string", nameof(targetType)); 18 | } 19 | if (value is not int) 20 | { 21 | throw new ArgumentException("源类型应为int", nameof(value)); 22 | } 23 | string[] strs = null; 24 | if (parameter is string s1) 25 | { 26 | strs = s1.Split(';'); 27 | } 28 | else if (parameter is string[] s2) 29 | { 30 | strs = s2; 31 | } 32 | else 33 | { 34 | throw new ArgumentException("parameter类型应为string或string[]", nameof(parameter)); 35 | } 36 | return strs[(int)value]; 37 | 38 | } 39 | 40 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 41 | { 42 | throw new NotImplementedException(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/Int2StringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Windows.Data; 6 | 7 | namespace SimpleFFmpegGUI.WPF.Converters 8 | { 9 | public class Int2StringConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (parameter is string s) 14 | { 15 | Dictionary dic = null; 16 | try 17 | { 18 | dic = s.Split(';').Select(p => p.Split(':')).ToDictionary(p => int.Parse(p[0]), p => p[1]); 19 | } 20 | catch (Exception ex) 21 | { 22 | throw new Exception("无法解析参数", ex); 23 | } 24 | if (value is int i) 25 | { 26 | if (!dic.ContainsKey(i)) 27 | { 28 | throw new Exception("没有值" + i + "的转换目标"); 29 | } 30 | return dic[i]; 31 | } 32 | throw new ArgumentException("绑定值必须为整数"); 33 | } 34 | throw new ArgumentException("参数必须为字符串"); 35 | } 36 | 37 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 38 | { 39 | throw new NotImplementedException(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Converters/NameDescriptionAttributeValueConverter.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.Model; 2 | using System; 3 | using System.Globalization; 4 | using System.Windows.Data; 5 | 6 | namespace SimpleFFmpegGUI.WPF.Converters 7 | { 8 | public class NameDescriptionAttributeValueConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | return AttributeHelper.GetAttributeValue(value, p => p.Name); 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/FileDialogExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Windows; 5 | 6 | namespace SimpleFFmpegGUI.WPF 7 | { 8 | public static class FileDialogExtension 9 | { 10 | /// 11 | /// 满足条件时加入文件筛选器 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | public static T AddFilterIf(this T dialog, bool b, string display, params string[] extensions) where T : FileDialog 19 | { 20 | if (b) 21 | { 22 | return AddFilter(dialog, display, extensions); 23 | } 24 | return dialog; 25 | } 26 | public static T AddFilter(this T dialog, string display, params string[] extensions) where T : FileDialog 27 | { 28 | StringBuilder filter = new StringBuilder(dialog.Filter); 29 | if (filter.Length > 0) 30 | { 31 | filter.Append('|'); 32 | } 33 | filter.Append(display); 34 | filter.Append('|'); 35 | filter.Append(string.Join(';', extensions.Select(p => "*." + p))); 36 | dialog.Filter = filter.ToString(); 37 | return dialog; 38 | } 39 | 40 | public static T AddAllFilesFilter(this T dialog, string display = "所有文件") where T : FileDialog 41 | { 42 | StringBuilder filter = new StringBuilder(dialog.Filter); 43 | if (filter.Length > 0) 44 | { 45 | filter.Append('|'); 46 | } 47 | filter.Append(display); 48 | filter.Append("|*.*"); 49 | dialog.Filter = filter.ToString(); 50 | return dialog; 51 | } 52 | 53 | public static string GetPath(this FileDialog dialog, Window owner) 54 | { 55 | if ((owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner)) == true) 56 | { 57 | return dialog.FileName; 58 | } 59 | return null; 60 | } 61 | 62 | public static string[] GetPaths(this OpenFileDialog dialog, Window owner) 63 | { 64 | dialog.Multiselect = true; 65 | if ((owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner)) == true) 66 | { 67 | return dialog.FileNames; 68 | } 69 | return null; 70 | } 71 | 72 | public static string GetPath(this OpenFolderDialog dialog, Window owner) 73 | { 74 | if ((owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner)) == true) 75 | { 76 | return dialog.FolderName; 77 | } 78 | return null; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/AddNewTabMessage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Windows.Controls; 4 | 5 | namespace SimpleFFmpegGUI.WPF.Messages 6 | { 7 | public class AddNewTabMessage(Type type, bool top = false, bool showWindow = false) 8 | { 9 | public object Page { get; set; } 10 | public Type Type { get; } = type; 11 | 12 | public bool Top { get; set; } = top; 13 | public bool ShowWindow { get; } = showWindow; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/FileDialogMessage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleFFmpegGUI.WPF.Messages 8 | { 9 | public class FileDialogMessage(CommonItemDialog dialog) 10 | { 11 | public CommonItemDialog Dialog { get; } = dialog; 12 | public bool? Result { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/QueueMessagesMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.WPF.Messages 4 | { 5 | public class QueueMessagesMessage(char type, string message, Exception exception = null) 6 | { 7 | public char Type { get; } = type; 8 | public string Message { get; } = message; 9 | public Exception Exception { get; } = exception; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/ShowCodeArgumentsMessage.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.Model; 2 | using SimpleFFmpegGUI.WPF.ViewModels; 3 | 4 | namespace SimpleFFmpegGUI.WPF.Messages 5 | { 6 | public class ShowCodeArgumentsMessage(TaskInfoViewModel task) 7 | { 8 | public TaskInfoViewModel Task { get; } = task; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/SnapshotEnabledMessage.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.WPF.ViewModels; 2 | 3 | namespace SimpleFFmpegGUI.WPF.Messages 4 | { 5 | public class SnapshotEnabledMessage(SnapshotViewModel options) 6 | { 7 | public SnapshotViewModel Options { get; private set; } = options; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/WindowEnableMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.WPF.Messages 2 | { 3 | public class WindowEnableMessage(bool isEnabled) 4 | { 5 | public bool IsEnabled { get; } = isEnabled; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Messages/WindowHandleMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.WPF.Messages 2 | { 3 | public class WindowHandleMessage() 4 | { 5 | public nint Handle { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/FFmpegOutputPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.WPF.ViewModels; 2 | using System; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Input; 6 | 7 | namespace SimpleFFmpegGUI.WPF.Pages 8 | { 9 | public partial class FFmpegOutputPage : UserControl 10 | { 11 | public FFmpegOutputPage() 12 | { 13 | ViewModel = this.SetDataContext(); 14 | InitializeComponent(); 15 | ViewModel.Outputs.CollectionChanged += Outputs_CollectionChanged; 16 | } 17 | 18 | public FFmpegOutputPageViewModel ViewModel { get; set; } 19 | 20 | private void Outputs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 21 | { 22 | if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) 23 | { 24 | scr.ScrollToEnd(); 25 | } 26 | } 27 | private void TextBlock_PreviewMouseDown(object sender, MouseButtonEventArgs e) 28 | { 29 | try 30 | { 31 | Clipboard.SetText((sender as TextBlock).Text); 32 | this.CreateMessage().QueueSuccess("已复制内容到剪贴板"); 33 | } 34 | catch(Exception ex) 35 | { 36 | this.CreateMessage().QueueError("复制内容失败", ex); 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/ICloseablePage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleFFmpegGUI.WPF.Pages 4 | { 5 | public interface ICloseablePage 6 | { 7 | public event EventHandler RequestToClose; 8 | } 9 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/LogsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Enterwell.Clients.Wpf.Notifications; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using SimpleFFmpegGUI.WPF.ViewModels; 4 | using System; 5 | using System.Collections; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Windows; 11 | using System.Windows.Controls; 12 | using System.Windows.Data; 13 | using System.Windows.Documents; 14 | using System.Windows.Input; 15 | using System.Windows.Media; 16 | using System.Windows.Media.Imaging; 17 | using System.Windows.Navigation; 18 | using System.Windows.Shapes; 19 | 20 | namespace SimpleFFmpegGUI.WPF.Pages 21 | { 22 | public partial class LogsPage : UserControl 23 | { 24 | public LogsPageViewModel ViewModel { get; set; } 25 | 26 | public LogsPage() 27 | { 28 | ViewModel = this.SetDataContext(); 29 | InitializeComponent(); 30 | } 31 | public async void FillLogs(int taskID) 32 | { 33 | var task = ViewModel.Tasks.FirstOrDefault(p => p.Id == taskID); 34 | Debug.Assert(task != null); 35 | ViewModel.SelectedTask = task; 36 | ViewModel.From = DateTime.MinValue; 37 | await ViewModel.FillLogsAsync(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/MediaInfoPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.WPF.ViewModels; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Input; 5 | 6 | namespace SimpleFFmpegGUI.WPF.Pages 7 | { 8 | public partial class MediaInfoPage : UserControl 9 | { 10 | public MediaInfoPage() 11 | { 12 | ViewModel = this.SetDataContext(); 13 | InitializeComponent(); 14 | } 15 | 16 | public MediaInfoPageViewModel ViewModel { get; set; } 17 | 18 | public void SetFile(string file) 19 | { 20 | ViewModel.FilePath = file; 21 | } 22 | 23 | protected override void OnDragOver(DragEventArgs e) 24 | { 25 | base.OnDragOver(e); 26 | if (e.Data.GetDataPresent(DataFormats.FileDrop) 27 | && (e.Data.GetData(DataFormats.FileDrop) as string[]).Length == 1 28 | && System.IO.File.Exists((e.Data.GetData(DataFormats.FileDrop) as string[])[0])) 29 | { 30 | e.Effects = DragDropEffects.Link; 31 | } 32 | } 33 | 34 | protected override void OnDrop(DragEventArgs e) 35 | { 36 | base.OnDrop(e); 37 | if (e.Data.GetDataPresent(DataFormats.FileDrop) 38 | && (e.Data.GetData(DataFormats.FileDrop) as string[]).Length == 1 39 | && System.IO.File.Exists((e.Data.GetData(DataFormats.FileDrop) as string[])[0])) 40 | { 41 | ViewModel.FilePath = (e.Data.GetData(DataFormats.FileDrop) as string[])[0]; 42 | } 43 | } 44 | 45 | private void TextBox_MouseLeave(object sender, MouseEventArgs e) 46 | { 47 | Keyboard.ClearFocus(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/PageHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleFFmpegGUI.WPF.Pages 8 | { 9 | public static class PageHelper 10 | { 11 | public static string GetTitle(Type type) 12 | { 13 | return type.Name switch 14 | { 15 | nameof(AddTaskPage) => "新增任务", 16 | nameof(MediaInfoPage) => "媒体信息", 17 | nameof(PresetsPage) => "所有预设", 18 | nameof(TasksPage) => "所有任务", 19 | nameof(LogsPage) => "日志", 20 | nameof(SettingPage) => "设置", 21 | nameof(FFmpegOutputPage) => "FFmpeg输出命令行", 22 | _ => throw new NotImplementedException(), 23 | }; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/PresetsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Enterwell.Clients.Wpf.Notifications; 2 | using FzLib; 3 | using FzLib.WPF; 4 | using Mapster; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Win32; 7 | using iNKORE.Extension.CommonDialog; 8 | using Newtonsoft.Json; 9 | using SimpleFFmpegGUI.Manager; 10 | using SimpleFFmpegGUI.Model; 11 | using SimpleFFmpegGUI.WPF.ViewModels; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Diagnostics; 15 | using System.Globalization; 16 | using System.IO; 17 | using System.Linq; 18 | using System.Text; 19 | using System.Windows; 20 | using System.Windows.Controls; 21 | using System.Windows.Data; 22 | using System.Windows.Documents; 23 | using System.Windows.Input; 24 | using System.Windows.Media; 25 | using System.Windows.Media.Imaging; 26 | using System.Windows.Navigation; 27 | using System.Windows.Shapes; 28 | 29 | namespace SimpleFFmpegGUI.WPF.Pages 30 | { 31 | public partial class PresetsPage : UserControl 32 | { 33 | public PresetsPage() 34 | { 35 | ViewModel = this.SetDataContext(); 36 | InitializeComponent(); 37 | ViewModel.CodeArgumentsViewModel = argumentsPanel.ViewModel; 38 | Loaded += async (s, e) => await ViewModel.FillPresetsAsync(); 39 | } 40 | 41 | public PresetsPageViewModel ViewModel { get; set; } 42 | 43 | private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) 44 | { 45 | if (e.AddedItems.Count > 0 && e.AddedItems[0] is CodePreset preset) 46 | { 47 | grd.RowDefinitions[2].Height = new GridLength(48); 48 | lvw.IsHitTestVisible = false; 49 | lvw.ScrollIntoView(preset); 50 | grd.RowDefinitions[4].Height = new GridLength(1, GridUnitType.Star); 51 | argumentsPanel.Update(preset.Type, preset.Arguments); 52 | } 53 | else 54 | { 55 | grd.RowDefinitions[2].Height = new GridLength(1, GridUnitType.Star); 56 | lvw.IsHitTestVisible = true; 57 | grd.RowDefinitions[4].Height = new GridLength(0); 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/SettingPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Enterwell.Clients.Wpf.Notifications; 2 | using FzLib.WPF; 3 | using Mapster; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Win32; 6 | using SimpleFFmpegGUI.Dto; 7 | using SimpleFFmpegGUI.Model; 8 | using SimpleFFmpegGUI.WPF.ViewModels; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.ComponentModel; 12 | using System.Globalization; 13 | using System.Linq; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | using System.Windows; 17 | using System.Windows.Controls; 18 | using System.Windows.Data; 19 | using System.Windows.Documents; 20 | using System.Windows.Input; 21 | using System.Windows.Media; 22 | using System.Windows.Media.Imaging; 23 | using System.Windows.Navigation; 24 | using System.Windows.Shapes; 25 | 26 | namespace SimpleFFmpegGUI.WPF.Pages 27 | { 28 | 29 | public partial class SettingPage : UserControl, ICloseablePage 30 | { 31 | public SettingPage() 32 | { 33 | ViewModel = this.SetDataContext(); 34 | InitializeComponent(); 35 | } 36 | 37 | public event EventHandler RequestToClose 38 | { 39 | add => ViewModel.RequestToClose += value; 40 | remove => ViewModel.RequestToClose -= value; 41 | } 42 | 43 | public SettingPageViewModel ViewModel { get; set; } 44 | private void CommandBar_MouseEnter(object sender, MouseEventArgs e) 45 | { 46 | Keyboard.ClearFocus(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Pages/TasksPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Enterwell.Clients.Wpf.Notifications; 2 | using FFMpegCore; 3 | using FFMpegCore.Enums; 4 | using FFMpegCore.Pipes; 5 | using FzLib.WPF; 6 | using Mapster; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using SimpleFFmpegGUI.Dto; 9 | using SimpleFFmpegGUI.Manager; 10 | using SimpleFFmpegGUI.Model; 11 | using SimpleFFmpegGUI.WPF.ViewModels; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Diagnostics; 15 | using System.Drawing; 16 | using System.Globalization; 17 | using System.IO; 18 | using System.Text; 19 | using System.Threading; 20 | using System.Threading.Tasks; 21 | using System.Windows; 22 | using System.Windows.Controls; 23 | using System.Windows.Controls.Primitives; 24 | using System.Windows.Data; 25 | using System.Windows.Documents; 26 | using System.Windows.Input; 27 | using System.Windows.Media; 28 | using System.Windows.Media.Imaging; 29 | using System.Windows.Navigation; 30 | using System.Windows.Shapes; 31 | using Path = System.IO.Path; 32 | using Size = System.Drawing.Size; 33 | 34 | namespace SimpleFFmpegGUI.WPF.Pages 35 | { 36 | 37 | /// 38 | /// Interaction logic for TasksPage.xaml 39 | /// 40 | public partial class TasksPage : UserControl 41 | { 42 | public TasksPageViewModel ViewModel { get; set; } 43 | 44 | public TasksPage() 45 | { 46 | InitializeComponent(); 47 | ViewModel = this.SetDataContext(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Panels/PresetsPanel.xaml.cs: -------------------------------------------------------------------------------- 1 | using FzLib; 2 | using Microsoft.DotNet.PlatformAbstractions; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using iNKORE.Extension.CommonDialog; 5 | using SimpleFFmpegGUI.Manager; 6 | using SimpleFFmpegGUI.Model; 7 | using SimpleFFmpegGUI.WPF.ViewModels; 8 | using System; 9 | using System.Collections; 10 | using System.Collections.Generic; 11 | using System.ComponentModel; 12 | using System.Diagnostics; 13 | using System.Globalization; 14 | using System.IO; 15 | using System.Text; 16 | using System.Threading.Tasks; 17 | using System.Windows; 18 | using System.Windows.Controls; 19 | using System.Windows.Data; 20 | using System.Windows.Documents; 21 | using System.Windows.Input; 22 | using System.Windows.Media.Imaging; 23 | using System.Windows.Navigation; 24 | using System.Windows.Shapes; 25 | 26 | namespace SimpleFFmpegGUI.WPF.Panels 27 | { 28 | 29 | public partial class PresetsPanel : UserControl 30 | { 31 | public PresetsPanel() 32 | { 33 | ViewModel = this.SetDataContext(); 34 | InitializeComponent(); 35 | } 36 | 37 | public PresetsPanelViewModel ViewModel { get; } 38 | 39 | public Task UpdateTypeAsync(TaskType type) 40 | { 41 | return ViewModel.UpdateTypeAsync(type); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Panels/StatusPanel.xaml.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.WPF.ViewModels; 2 | using System.Windows.Controls; 3 | 4 | namespace SimpleFFmpegGUI.WPF.Panels 5 | { 6 | public partial class StatusPanel : UserControl 7 | { 8 | public StatusPanel() 9 | { 10 | ViewModel = this.SetDataContext(); 11 | InitializeComponent(); 12 | } 13 | 14 | public StatusPanelViewModel ViewModel { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Panels/TaskList.xaml.cs: -------------------------------------------------------------------------------- 1 | using FzLib.WPF; 2 | using Mapster; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using iNKORE.Extension.CommonDialog; 5 | using SimpleFFmpegGUI.Manager; 6 | using SimpleFFmpegGUI.Model; 7 | using SimpleFFmpegGUI.WPF; 8 | using SimpleFFmpegGUI.WPF.ViewModels; 9 | using SimpleFFmpegGUI.WPF.Pages; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Collections.ObjectModel; 13 | using System.Diagnostics; 14 | using System.Globalization; 15 | using System.IO; 16 | using System.Linq; 17 | using System.Text; 18 | using System.Threading.Tasks; 19 | using System.Windows; 20 | using System.Windows.Controls; 21 | using System.Windows.Data; 22 | using System.Windows.Documents; 23 | using System.Windows.Input; 24 | using System.Windows.Media.Imaging; 25 | using System.Windows.Navigation; 26 | using TaskStatus = SimpleFFmpegGUI.Model.TaskStatus; 27 | 28 | namespace SimpleFFmpegGUI.WPF.Panels 29 | { 30 | public partial class TaskList : UserControl 31 | { 32 | public static readonly DependencyProperty ShowAllTasksProperty = DependencyProperty.Register( 33 | nameof(ShowAllTasks), typeof(bool), typeof(TaskList)); 34 | 35 | public TaskList() 36 | { 37 | InitializeComponent(); 38 | ViewModel = this.SetDataContext(); 39 | } 40 | public bool ShowAllTasks 41 | { 42 | get => (bool)GetValue(ShowAllTasksProperty); 43 | set => SetValue(ShowAllTasksProperty, value); 44 | } 45 | 46 | public TaskListViewModel ViewModel { get; } 47 | 48 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 49 | { 50 | base.OnPropertyChanged(e); 51 | if (e.Property == ShowAllTasksProperty) 52 | { 53 | ViewModel.ShowAllTasks = (bool)e.NewValue; 54 | } 55 | } 56 | private void UpdateDetailHeight() 57 | { 58 | bdDetail.Height = App.ServiceProvider.GetService().IsUiCompressMode && !ShowAllTasks ? 108 : double.NaN; 59 | } 60 | 61 | private void UserControl_Loaded(object sender, RoutedEventArgs e) 62 | { 63 | UpdateDetailHeight(); 64 | this.GetWindow().IsUiCompressModeChanged += (s, e) => UpdateDetailHeight(); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SimpleFFmpegGUI.WPF": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/AllTasksViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using FzLib; 3 | using Mapster; 4 | using SimpleFFmpegGUI.Manager; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Collections.ObjectModel; 8 | using System.ComponentModel; 9 | using System.Threading.Tasks; 10 | 11 | namespace SimpleFFmpegGUI.WPF.ViewModels 12 | { 13 | public partial class AllTasksViewModel : TaskCollectionViewModelBase 14 | { 15 | private readonly TaskManager taskManager; 16 | 17 | [ObservableProperty] 18 | private int count; 19 | 20 | [ObservableProperty] 21 | private int countPerPage = 20; 22 | 23 | [ObservableProperty] 24 | private int page = 0; 25 | 26 | [ObservableProperty] 27 | private int pageCount; 28 | 29 | public AllTasksViewModel(TaskManager tm) 30 | { 31 | taskManager = tm; 32 | RefreshAsync(); 33 | } 34 | public override async Task RefreshAsync() 35 | { 36 | var tasks = await taskManager.GetTasksAsync(null, Page * CountPerPage, CountPerPage); 37 | Count = tasks.TotalCount; 38 | PageCount = (int)Math.Ceiling(1.0 * Count / CountPerPage); 39 | Tasks = new ObservableCollection(tasks.List.Adapt>()); 40 | } 41 | 42 | protected override async void OnPropertyChanged(PropertyChangedEventArgs e) 43 | { 44 | base.OnPropertyChanged(e); 45 | if (e.PropertyName is nameof(Page) or nameof(CountPerPage)) 46 | { 47 | await 48 | RefreshAsync(); 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/ArgumentDescriptions.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.WPF.ViewModels 2 | { 3 | public static class ArgumentDescriptions 4 | { 5 | 6 | public static string AspectRatio => "视频宽度与高度的比例。可以输入宽:高或宽/高的数值。"; 7 | public static string VideoAverageBitrate => "整个视频部分的目标比特率。平均码率可以帮助保持视频文件大小在一个合理的范围内。"; 8 | public static string AudioAverageBitrate => "音频文件的目标比特率。平均码率可以帮助保持音频部分大小在一个合理的范围内。"; 9 | public static string VideoCode => "视频的编码格式。压制速度方面,H264>H265≈VP9>AV1 (SVT)>AV1 (aom);同等码率的画质方面,H264 "一个用于控制视频质量的选项,特别是在使用可变比特率 (VBR) 编码时。CRF 值越小,画质越好。不同编码格式的CRF质检没有参考性。CRF 是一个相对恒定的质量模式,适用于在保证一定视觉质量的同时,让编码器自动调整输出比特率。"; 11 | public static string FPS => "每秒显示的画面数量。较高的帧率可以使动作看起来更加流畅,但也可能导致较大的文件大小。"; 12 | public static string MaxBitrate => "编码过程中允许达到的最大比特率。这有助于在网络带宽有限的情况下防止数据过载。当视频复杂度突然增加时,可能会短暂地达到这个上限。"; 13 | public static string MaxBitrateBuffer => "用于控制视频码率超过最大码率的时间限制。值越大允许在遇到复杂场景时有更长时间超过视频的最大码率。"; 14 | public static string PixelFormat => "描述了单个像素如何在数字信号中表示。常见的格式包括 YUV420p, YUV422p, YUV444p 等,它们决定了色彩采样率以及颜色深度等信息。"; 15 | public static string Preset => "表示编码的速度与压缩效率之间的权衡。值越小,压制速度越慢,单位大小的视频片段质量越高;值越大,压制速度越快,单位大小的视频片段质量越低"; 16 | public static string Size => "视频图像的尺寸。640:480表示640×480;640:-1表示宽度为640,高度自动调节;640:-2表示宽度为640,高度自动调节且调整到偶数;iw/2:ih/2表示视频分辨率缩放到原来的一半。"; 17 | public static string TwoPass => "一种在编码视频时使用的策略,目的是为了更精确地控制视频的比特率,并且优化最终输出的视频质量。可以获得更好的比特率管理和视频质量,尤其是在恒定比特率(CBR)模式下。然而,这种方法的缺点是需要两次编码过程,因此会消耗更多的时间。"; 18 | public static string AudioCode => "音频编码格式"; 19 | public static string AudioBitrate => "音频比特率,单位为 kbps。较高的音频比特率通常意味着更好的音质,但也会导致更大的文件体积。"; 20 | public static string SampleRate => "每秒钟对音频信号进行数字化采样的次数,单位通常是赫兹 (Hz)。常见的采样率有 44.1 kHz(用于 CD )、48 kHz(DVD和网络视频)、以及 96 kHz等。采样率越高,音频信号的频率范围就越宽,理论上能够捕捉到的声音细节也就越多,音质也就越好。"; 21 | public static string Format => "多媒体文件的封装格式,它可以包含一个或多个音频、视频、字幕轨道以及其他元数据。常用的有mp4(简单容器)、mkv(复杂容器)。容器的主要功能是将不同类型的媒体数据整合在一起,并提供一种方式来描述这些数据是如何组织和同步的。"; 22 | public static string VideoOutput => "重新编码表示会对视频流重新计算,转码到新的编码、码率、分辨率等。复制表示仅复制数据,不重新计算,速度很快。不导出表示最终文件中不包含画面,只包含音频。"; 23 | public static string AudioOutput => "重新编码表示会对音频流重新计算,转码到新的编码、码率、分辨率等。复制表示仅复制数据,不重新计算。不导出表示最终文件中不包含音频,只包含画面。"; 24 | public static string Extra => "自定义的FFmpeg输出参数,这些参数将附加在输入文件后、输出文件前。"; 25 | public static string SyncModifiedTime => "将输出文件的修改时间设置为最后一个输入文件的修改时间。"; 26 | public static string DeleteInputFiles => "在处理完成后删除所有输入文件(优先删除到回收站)。"; 27 | 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/AudioArgumentsViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using FzLib; 3 | using SimpleFFmpegGUI.Model; 4 | using SimpleFFmpegGUI.WPF.ViewModels; 5 | using System.ComponentModel; 6 | 7 | namespace SimpleFFmpegGUI.WPF.ViewModels 8 | { 9 | public partial class AudioArgumentsViewModel : ViewModelBase, IAudioCodeArguments, IArgumentVideModel 10 | { 11 | [ObservableProperty] 12 | private int? bitrate; 13 | 14 | [ObservableProperty] 15 | private string code; 16 | 17 | [ObservableProperty] 18 | private bool enableBitrate; 19 | 20 | [ObservableProperty] 21 | private bool enableSamplingRate; 22 | 23 | [ObservableProperty] 24 | private int? samplingRate; 25 | 26 | public AudioArgumentsViewModel() 27 | { 28 | Bitrate = 128; 29 | SamplingRate = 48000; 30 | Code = "自动"; 31 | } 32 | public void Apply() 33 | { 34 | Bitrate = EnableBitrate ? Bitrate : null; 35 | SamplingRate = EnableSamplingRate ? SamplingRate : null; 36 | } 37 | 38 | public void Update() 39 | { 40 | EnableBitrate = Bitrate.HasValue; 41 | EnableSamplingRate = SamplingRate.HasValue; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/FFmpegOutputPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Input; 2 | using SimpleFFmpegGUI.Model; 3 | using System; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace SimpleFFmpegGUI.WPF.ViewModels 9 | { 10 | public partial class FFmpegOutputPageViewModel : ViewModelBase 11 | { 12 | public FFmpegOutputPageViewModel() 13 | { 14 | Logger.Log += Logger_Log; 15 | } 16 | public ObservableCollection Outputs { get; } = new ObservableCollection(); 17 | 18 | [RelayCommand] 19 | private void ClearAll() 20 | { 21 | Outputs.Clear(); 22 | } 23 | 24 | [RelayCommand] 25 | private void CopyAll() 26 | { 27 | try 28 | { 29 | Clipboard.SetText(string.Join(Environment.NewLine, Outputs.Select(p => p.Message))); 30 | QueueSuccessMessage("已复制内容到剪贴板"); 31 | } 32 | catch (Exception ex) 33 | { 34 | QueueErrorMessage("复制内容失败", ex); 35 | } 36 | } 37 | 38 | private async void Logger_Log(object sender, LogEventArgs e) 39 | { 40 | await System.Windows.Application.Current.Dispatcher.InvokeAsync(() => 41 | { 42 | if (e.Log.Message.StartsWith("frame=") 43 | && Outputs.Count > 0 && Outputs[^1].Message.StartsWith("frame=")) 44 | { 45 | Outputs[^1] = e.Log; 46 | } 47 | else 48 | { 49 | Outputs.Add(e.Log); 50 | } 51 | }); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/FormatArgumentViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using FzLib; 3 | using SimpleFFmpegGUI.WPF.ViewModels; 4 | using System.ComponentModel; 5 | 6 | namespace SimpleFFmpegGUI.WPF.ViewModels 7 | { 8 | public partial class FormatArgumentViewModel : ViewModelBase, IArgumentVideModel 9 | { 10 | [ObservableProperty] 11 | private bool enableFormat = true; 12 | 13 | [ObservableProperty] 14 | private string format = "mp4"; 15 | 16 | public void Apply() 17 | { 18 | Format = EnableFormat ? Format : null; 19 | } 20 | 21 | public void Update() 22 | { 23 | EnableFormat = Format != null; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/IArgumentVideModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace SimpleFFmpegGUI.WPF.ViewModels 4 | { 5 | public interface IArgumentVideModel 6 | { 7 | /// 8 | /// 将原始数据更新到UI数据 9 | /// 10 | public void Update(); 11 | 12 | /// 13 | /// 从UI数据应用到原始数据 14 | /// 15 | public void Apply(); 16 | } 17 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/LogsPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using FzLib; 4 | using Mapster; 5 | using SimpleFFmpegGUI.Manager; 6 | using SimpleFFmpegGUI.Model; 7 | using SimpleFFmpegGUI.WPF.ViewModels; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.ComponentModel; 11 | using System.Diagnostics; 12 | using System.Threading.Tasks; 13 | using System.Windows; 14 | 15 | namespace SimpleFFmpegGUI.WPF.ViewModels 16 | { 17 | public partial class LogsPageViewModel : ViewModelBase 18 | { 19 | private readonly LogManager logManager; 20 | 21 | [ObservableProperty] 22 | private DateTime from = DateTime.Now.AddDays(-1); 23 | 24 | [ObservableProperty] 25 | private IList logs; 26 | 27 | [ObservableProperty] 28 | private TaskInfoViewModel selectedTask; 29 | 30 | [ObservableProperty] 31 | private List tasks; 32 | 33 | [ObservableProperty] 34 | private DateTime to = DateTime.Today.AddDays(1); 35 | 36 | [ObservableProperty] 37 | private int typeIndex; 38 | 39 | public LogsPageViewModel(TaskManager taskManager, LogManager logManager) 40 | { 41 | taskManager.GetTasksAsync(take: 20).ContinueWith(data => 42 | { 43 | Tasks = data.Result.List.Adapt>(); 44 | }); 45 | this.logManager = logManager; 46 | } 47 | public char? Type => TypeIndex switch 48 | { 49 | 0 => null, 50 | 1 => 'E', 51 | 2 => 'W', 52 | 3 => 'I', 53 | 4 => 'O', 54 | _ => throw new NotImplementedException() 55 | }; 56 | 57 | [RelayCommand] 58 | public async Task FillLogsAsync() 59 | { 60 | Logs = (await logManager.GetLogsAsync(type: Type, taskId: SelectedTask?.Id ?? 0, from: From, to: To)).List; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/MediaInfoPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using FzLib; 4 | using Microsoft.Win32; 5 | using Newtonsoft.Json.Linq; 6 | using SimpleFFmpegGUI.Manager; 7 | using SimpleFFmpegGUI.Model.MediaInfo; 8 | using SimpleFFmpegGUI.WPF.Messages; 9 | using System; 10 | using System.ComponentModel; 11 | using System.Threading.Tasks; 12 | using System.Windows; 13 | 14 | namespace SimpleFFmpegGUI.WPF.ViewModels 15 | { 16 | public partial class MediaInfoPageViewModel : ViewModelBase 17 | { 18 | [ObservableProperty] 19 | private string filePath; 20 | 21 | [ObservableProperty] 22 | private MediaInfoGeneral mediaInfo; 23 | public MediaInfoPageViewModel() 24 | { 25 | } 26 | 27 | protected override async void OnPropertyChanged(PropertyChangedEventArgs e) 28 | { 29 | base.OnPropertyChanged(e); 30 | if (e.PropertyName == nameof(FilePath)) 31 | { 32 | if (!string.IsNullOrWhiteSpace(FilePath) && System.IO.File.Exists(FilePath)) 33 | { 34 | await ShowInfoAsync(); 35 | } 36 | } 37 | } 38 | 39 | private async Task ShowInfoAsync() 40 | { 41 | SendMessage(new WindowEnableMessage(false)); 42 | try 43 | { 44 | MediaInfo = await MediaInfoManager.GetMediaInfoAsync(FilePath); 45 | } 46 | catch (Exception ex) 47 | { 48 | QueueErrorMessage("读取媒体信息失败", ex); 49 | } 50 | finally 51 | { 52 | SendMessage(new WindowEnableMessage(true)); 53 | } 54 | } 55 | 56 | [RelayCommand] 57 | private void BrowseFile() 58 | { 59 | var dialog = new OpenFileDialog().AddAllFilesFilter(); 60 | SendMessage(new FileDialogMessage(dialog)); 61 | 62 | string path = dialog.FileName; 63 | if (!string.IsNullOrEmpty(path)) 64 | { 65 | FilePath = path; 66 | } 67 | } 68 | 69 | } 70 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/SettingPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using FzLib; 4 | using Mapster; 5 | using Microsoft.Win32; 6 | using SimpleFFmpegGUI.Manager; 7 | using SimpleFFmpegGUI.WPF.Messages; 8 | using System; 9 | using System.Collections; 10 | using System.Collections.ObjectModel; 11 | using System.Linq; 12 | using System.Windows; 13 | 14 | namespace SimpleFFmpegGUI.WPF.ViewModels 15 | { 16 | public partial class SettingPageViewModel : ViewModelBase 17 | { 18 | private readonly ConfigManager configManager; 19 | 20 | [ObservableProperty] 21 | private Config configs; 22 | 23 | public SettingPageViewModel(ConfigManager configManager) 24 | { 25 | configs = Config.Instance.DeepCopy(); 26 | this.configManager = configManager; 27 | ObservableRemoteHosts = new ObservableCollection(Configs.RemoteHosts); 28 | this.Notify(nameof(ObservableRemoteHosts)); 29 | } 30 | 31 | public event EventHandler RequestToClose; 32 | 33 | public IEnumerable DefaultOutputDirTypes => Enum.GetValues(); 34 | 35 | public int DefaultProcessPriority 36 | { 37 | get => configManager.DefaultProcessPriority; 38 | set => configManager.DefaultProcessPriority = value; 39 | } 40 | 41 | public ObservableCollection ObservableRemoteHosts { get; set; } 42 | [RelayCommand] 43 | private void AddRemoteHost() 44 | { 45 | ObservableRemoteHosts.Add(new RemoteHost()); 46 | } 47 | 48 | [RelayCommand] 49 | private void BrowseSpecialDirPath() 50 | { 51 | var dialog = new OpenFolderDialog(); 52 | SendMessage(new FileDialogMessage(dialog)); 53 | var path = dialog.FolderName; 54 | if (!string.IsNullOrEmpty(path)) 55 | { 56 | Configs.DefaultOutputDirSpecialDirPath = path; 57 | } 58 | } 59 | 60 | [RelayCommand] 61 | private void Cancel() 62 | { 63 | RequestToClose?.Invoke(this, EventArgs.Empty); 64 | } 65 | 66 | [RelayCommand] 67 | private void Save() 68 | { 69 | Configs.RemoteHosts = ObservableRemoteHosts.ToList(); 70 | Configs.Adapt(Config.Instance); 71 | Config.Instance.Save(); 72 | RequestToClose?.Invoke(this, EventArgs.Empty); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/SnapshotViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using SimpleFFmpegGUI.WPF.ViewModels; 3 | using System; 4 | 5 | namespace SimpleFFmpegGUI.WPF.ViewModels 6 | { 7 | public partial class SnapshotViewModel : ViewModelBase 8 | { 9 | [ObservableProperty] 10 | private bool displayFrame; 11 | 12 | [ObservableProperty] 13 | private bool canUpdate; 14 | 15 | [ObservableProperty] 16 | private Uri source; 17 | } 18 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/TaskCollectionViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using FzLib; 3 | using SimpleFFmpegGUI.WPF.ViewModels; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.ComponentModel; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace SimpleFFmpegGUI.WPF.ViewModels 11 | { 12 | public abstract partial class TaskCollectionViewModelBase : ViewModelBase 13 | { 14 | public abstract Task RefreshAsync(); 15 | 16 | [ObservableProperty] 17 | private ObservableCollection tasks; 18 | 19 | [ObservableProperty] 20 | private TaskInfoViewModel selectedTask; 21 | 22 | public IList SelectedTasks=>Tasks.Where(p=>p.IsSelected).ToList(); 23 | } 24 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/ViewModels/TasksPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using FzLib; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using SimpleFFmpegGUI.WPF.ViewModels; 6 | using System; 7 | using System.Collections; 8 | using System.ComponentModel; 9 | using System.Linq; 10 | 11 | namespace SimpleFFmpegGUI.WPF.ViewModels 12 | { 13 | public partial class TasksPageViewModel : ViewModelBase 14 | { 15 | public TasksPageViewModel(AllTasksViewModel allTasks) 16 | { 17 | AllTasks = allTasks; 18 | AllTasks.PropertyChanged += AllTasks_PropertyChanged; 19 | RefreshPages(); 20 | } 21 | 22 | private void AllTasks_PropertyChanged(object sender, PropertyChangedEventArgs e) 23 | { 24 | RefreshPages(); 25 | } 26 | 27 | [RelayCommand] 28 | private void RefreshPages() 29 | { 30 | var pages = Enumerable.Range(0, AllTasks.PageCount) 31 | .Select(p => new 32 | { 33 | Label = "第" + (p + 1) + "页", 34 | Value = p, 35 | Value1 = p + 1 36 | }); 37 | if (Pages == null || pages.Count() != Pages.Cast().Count()) 38 | { 39 | Pages = pages.ToList(); 40 | } 41 | } 42 | 43 | [ObservableProperty] 44 | private IEnumerable pages; 45 | 46 | public AllTasksViewModel AllTasks { get; } 47 | } 48 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/SimpleFFmpegGUI.WPF/icon.ico -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/SimpleFFmpegGUI.WPF/icon.png -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WPF/log4net.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remote-ffmpeg-gui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@babel/core": "^7.21.0", 12 | "axios": "^0.19.2", 13 | "core-js": "^3.27.2", 14 | "element-ui": "^2.15.12", 15 | "js-cookie": "^2.2.1", 16 | "register-service-worker": "^1.7.2", 17 | "vue": "^2.7.14", 18 | "vue-axios": "^2.1.5", 19 | "vue-class-component": "^7.2.6", 20 | "vue-property-decorator": "^8.5.1", 21 | "vue-router": "^3.6.5" 22 | }, 23 | "devDependencies": { 24 | "@types/js-cookie": "^2.2.7", 25 | "@typescript-eslint/eslint-plugin": "^4.33.0", 26 | "@typescript-eslint/parser": "^4.33.0", 27 | "@vue/cli-plugin-babel": "^5.0.8", 28 | "@vue/cli-plugin-eslint": "^5.0.8", 29 | "@vue/cli-plugin-pwa": "^5.0.8", 30 | "@vue/cli-plugin-router": "^5.0.8", 31 | "@vue/cli-plugin-typescript": "^5.0.8", 32 | "@vue/cli-service": "^5.0.8", 33 | "@vue/eslint-config-typescript": "^7.0.0", 34 | "eslint": "^7.20.0", 35 | "eslint-plugin-vue": "^7.20.0", 36 | "typescript": "~4.1.5", 37 | "vue-template-compiler": "^2.7.14" 38 | }, 39 | "eslintConfig": { 40 | "root": true, 41 | "env": { 42 | "node": true 43 | }, 44 | "extends": [ 45 | "plugin:vue/essential", 46 | "eslint:recommended", 47 | "@vue/typescript/recommended" 48 | ], 49 | "parserOptions": { 50 | "ecmaVersion": 2020, 51 | "parser": "@typescript-eslint/parser" 52 | }, 53 | "rules": {} 54 | }, 55 | "browserslist": [ 56 | "> 1%", 57 | "last 2 versions", 58 | "not dead" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/SimpleFFmpegGUI.Web/public/icon.png -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 远程 FFmpeg 工具箱 9 | 10 | 11 | 12 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/assets/global.css: -------------------------------------------------------------------------------- 1 | .right24 { 2 | margin-right: 24px; 3 | } 4 | .right12 { 5 | margin-right: 12px; 6 | } 7 | .left24 { 8 | margin-left: 24px; 9 | } 10 | .left12 { 11 | margin-left: 12px; 12 | } 13 | 14 | .bottom12 { 15 | margin-bottom: 12px; 16 | } 17 | 18 | .bottom24 { 19 | margin-bottom: 24px; 20 | } 21 | 22 | .bottom12 { 23 | margin-bottom: 12px; 24 | } 25 | 26 | .top24 { 27 | margin-top: 24px; 28 | } 29 | 30 | .top12 { 31 | margin-top: 12px; 32 | } 33 | 34 | 35 | .width240 { 36 | width: 240px; 37 | } 38 | 39 | .width160 { 40 | width: 160px; 41 | } 42 | 43 | .width120 { 44 | width: 120px; 45 | } 46 | 47 | .width80 { 48 | width: 80px; 49 | } 50 | 51 | .single-line { 52 | overflow: hidden; 53 | text-overflow: ellipsis; 54 | white-space: nowrap; 55 | } 56 | 57 | html { 58 | height: 100%; 59 | width:100%; 60 | margin: 0; 61 | position:fixed 62 | } 63 | 64 | body { 65 | height: 100%; 66 | overflow: hidden; 67 | margin: 0; 68 | } 69 | 70 | .is-vertical { 71 | height: 100%; 72 | } 73 | 74 | .center { 75 | height: 100%; 76 | margin: 0; 77 | } 78 | 79 | .one-line { 80 | overflow: hidden; 81 | text-overflow: ellipsis; 82 | white-space: nowrap; 83 | } 84 | .el-slider__marks-text{ 85 | white-space: nowrap; 86 | /* elementUI的BUG,Slider标签最后一个只能显示一个字符,其他会换行 */ 87 | } 88 | .gray{ 89 | color:#606266 90 | } 91 | 92 | .s{ 93 | white-space: pre-wrap 94 | } 95 | 96 | .inline{ 97 | display: inline 98 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/SimpleFFmpegGUI.Web/src/assets/logo.png -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/components/AddToTaskButtons.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 42 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/components/FileSelect.vue: -------------------------------------------------------------------------------- 1 | 2 | 15 | 52 | 63 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './registerServiceWorker' 4 | import router from './router' 5 | import ElementUI from 'element-ui'; 6 | import 'element-ui/lib/theme-chalk/index.css'; 7 | import axios from 'axios' 8 | import VueAxios from 'vue-axios' 9 | import './assets/global.css' 10 | 11 | Vue.use(VueAxios, axios) 12 | Vue.config.productionTip = false 13 | Vue.use(ElementUI,{ 14 | size:'small' 15 | }); 16 | new Vue({ 17 | router, 18 | render: h => h(App), 19 | }).$mount('#app') 20 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/parameters.ts: -------------------------------------------------------------------------------- 1 | const argKey="args"; 2 | const inputKey="input" 3 | const outputKey="output" 4 | export {argKey,inputKey,outputKey} -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready () { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered () { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached () { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound () { 20 | console.log('New content is downloading.') 21 | }, 22 | updated () { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline () { 26 | console.log('No internet connection found. App is running in offline mode.') 27 | }, 28 | error (error) { 29 | console.error('Error during service worker registration:', error) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/Add/Code.vue: -------------------------------------------------------------------------------- 1 | 18 | 93 | 98 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/Add/Compare.vue: -------------------------------------------------------------------------------- 1 | 16 | 71 | 78 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/Add/Concat.vue: -------------------------------------------------------------------------------- 1 | 18 | 94 | 99 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/Add/Custom.vue: -------------------------------------------------------------------------------- 1 | 7 | 47 | 49 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 28 | 68 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/src/views/Welcome.vue: -------------------------------------------------------------------------------- 1 | 48 | 70 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "src/*" 20 | ] 21 | }, 22 | "lib": [ 23 | "esnext", 24 | "dom", 25 | "dom.iterable", 26 | "scripthost" 27 | ] 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "tests/**/*.ts", 34 | "tests/**/*.tsx" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.Web/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: '/', //配置本地引用目录文件,如不配置,默认为`/` 3 | pwa:{ 4 | iconPaths:{ 5 | favicon32:'icon.png' 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "5.0.9", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Controllers/LogController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using SimpleFFmpegGUI.Dto; 5 | using SimpleFFmpegGUI.Model; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace SimpleFFmpegGUI.WebAPI.Controllers 10 | { 11 | public class LogController : FFmpegControllerBase 12 | { 13 | public LogController(ILogger Logger, 14 | IConfiguration config, 15 | PipeClient pipeClient) : base(Logger, config, pipeClient) { } 16 | 17 | [HttpGet] 18 | [Route("List")] 19 | public async Task> GetLogs(char? type = null, int taskId = 0, DateTime? from = null, DateTime? to = null, int skip = 0, int take = 0) 20 | { 21 | if (from.HasValue) 22 | { 23 | from = from.Value.ToLocalTime(); 24 | } 25 | if (to.HasValue) 26 | { 27 | to = to.Value.ToLocalTime(); 28 | } 29 | var result = await pipeClient.InvokeAsync(p => p.GetLogsAsync(type, taskId, from, to, skip, take)); 30 | return result; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Controllers/MediaInfoController.cs: -------------------------------------------------------------------------------- 1 | using Furion.FriendlyException; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.Logging; 5 | using SimpleFFmpegGUI.Dto; 6 | using SimpleFFmpegGUI.Manager; 7 | using SimpleFFmpegGUI.Model.MediaInfo; 8 | using System; 9 | using System.IO; 10 | using System.Threading.Tasks; 11 | using System.Xml.Linq; 12 | 13 | namespace SimpleFFmpegGUI.WebAPI.Controllers 14 | { 15 | public class MediaInfoController : FFmpegControllerBase 16 | { 17 | public MediaInfoController(ILogger Logger, 18 | IConfiguration config, 19 | PipeClient pipeClient) : base(Logger, config, pipeClient) { } 20 | 21 | [HttpGet] 22 | public async Task GetAsync(string name) 23 | { 24 | CheckNull(name, "文件"); 25 | string path = await CheckAndGetInputFilePathAsync(name); 26 | 27 | var result = await pipeClient.InvokeAsync(p => p.GetInfoAsync(path)); 28 | return result; 29 | } 30 | 31 | [HttpGet] 32 | [Route("Snapshot")] 33 | public async Task GetSnapshotAsync(string videoPath, double seconds) 34 | { 35 | try 36 | { 37 | videoPath = await CheckAndGetInputFilePathAsync(videoPath); 38 | string path = await pipeClient.InvokeAsync(p => p.GetSnapshot(videoPath, seconds)); 39 | 40 | if (CanAccessInputDir()) 41 | { 42 | return PhysicalFile(path, "image/jpeg"); 43 | } 44 | else 45 | { 46 | return File(await pipeClient.InvokeAsync(p => p.ReadFiles(path)), "image/jpeg"); 47 | } 48 | } 49 | catch (Exception ex) 50 | { 51 | throw Oops.Oh("获取截图失败:" + ex.Message); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Controllers/PowerController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using SimpleFFmpegGUI.Dto; 5 | using SimpleFFmpegGUI.Model; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace SimpleFFmpegGUI.WebAPI.Controllers 10 | { 11 | public class PowerController : FFmpegControllerBase 12 | { 13 | public PowerController(ILogger Logger, 14 | IConfiguration config, 15 | PipeClient pipeClient) : base(Logger, config, pipeClient) { } 16 | 17 | [HttpPost] 18 | [Route("Shutdown")] 19 | public async Task Shutdown() 20 | { 21 | await pipeClient.InvokeAsync(p => p.Shutdown()); 22 | } 23 | [HttpPost] 24 | [Route("AbortShutdown")] 25 | public async Task AbortShutdown() 26 | { 27 | await pipeClient.InvokeAsync(p => p.AbortShutdown()); 28 | } 29 | [HttpPost] 30 | [Route("ShutdownQueue")] 31 | public async Task SetShutdownAfterQueueFinished([FromForm] bool on) 32 | { 33 | await pipeClient.InvokeAsync(p => p.SetShutdownAfterQueueFinished(on)); 34 | } 35 | [HttpGet] 36 | [Route("ShutdownQueue")] 37 | public async Task IsShutdownAfterQueueFinished() 38 | { 39 | return await pipeClient.InvokeAsync(p => p.IsShutdownAfterQueueFinished()); 40 | } 41 | 42 | [HttpGet] 43 | [Route("CpuCoreUsage")] 44 | public async Task GetCpuCoreUsage() 45 | { 46 | return await pipeClient.InvokeAsync(p => p.GetCpuUsage(TimeSpan.FromSeconds(0.1))); 47 | } 48 | 49 | [HttpGet] 50 | [Route("DefaultProcessPriority")] 51 | public async Task GetDefaultProcessPriority() 52 | { 53 | return await pipeClient.InvokeAsync(p => p.GetDefaultProcessPriority()); 54 | } 55 | 56 | [HttpPost] 57 | [Route("DefaultProcessPriority")] 58 | public async Task SetDefaultProcessPriority(int priority) 59 | { 60 | await pipeClient.InvokeAsync(p=>p.SetDefaultProcessPriority(priority)); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Controllers/PresetController.cs: -------------------------------------------------------------------------------- 1 | using Furion.FriendlyException; 2 | using FzLib; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Logging; 7 | using SimpleFFmpegGUI.Dto; 8 | using SimpleFFmpegGUI.Model; 9 | using SimpleFFmpegGUI.WebAPI.Dto; 10 | using System.Collections.Generic; 11 | using System.IO; 12 | using System.Linq; 13 | using System.Threading.Tasks; 14 | 15 | namespace SimpleFFmpegGUI.WebAPI.Controllers 16 | { 17 | public class PresetController : FFmpegControllerBase 18 | { 19 | public PresetController(ILogger Logger, 20 | IConfiguration config, 21 | PipeClient pipeClient) : base(Logger, config, pipeClient) { } 22 | 23 | [HttpGet] 24 | [Route("List")] 25 | public async Task> GetPresets(TaskType? type) 26 | { 27 | if (type.HasValue) 28 | { 29 | return (await pipeClient.InvokeAsync(p => p.GetPresetsAsync())).Where(p => p.Type == type).ToList(); 30 | } 31 | return await pipeClient.InvokeAsync(p => p.GetPresetsAsync()); 32 | } 33 | 34 | [HttpPost] 35 | [Route("Add")] 36 | public Task AddAsync([FromBody] CodePresetDto request) 37 | { 38 | CheckNull(request, "请求"); 39 | return pipeClient.InvokeAsync(p => p.AddOrUpdatePresetAsync(request.Name, request.Type, request.Arguments)); 40 | } 41 | 42 | [HttpPost] 43 | [Route("Delete")] 44 | public async Task DeleteAsync(int id) 45 | { 46 | await pipeClient.InvokeAsync(p => p.DeletePresetAsync(id)); 47 | return Ok(); 48 | } 49 | 50 | [HttpGet] 51 | [Route("Export")] 52 | public async Task ExportAsync() 53 | { 54 | string json = await pipeClient.InvokeAsync(p => p.ExportPresetsAsync()); 55 | return File(json.ToUTF8Bytes(), "application/json"); 56 | } 57 | 58 | [HttpPost, HttpOptions] 59 | [Route("Import")] 60 | public async Task ImportAsync([FromQuery] IFormFile file) 61 | { 62 | using var s = file.OpenReadStream(); 63 | byte[] buffer = new byte[s.Length]; 64 | await s.ReadAsync(buffer, 0, buffer.Length); 65 | string json = buffer.ToUTF8String(); 66 | await pipeClient.InvokeAsync(p => p.ImportPresetsAsync(json)); 67 | return Ok(); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Controllers/QueueController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | using SimpleFFmpegGUI.Dto; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace SimpleFFmpegGUI.WebAPI.Controllers 9 | { 10 | public class QueueController : FFmpegControllerBase 11 | { 12 | public QueueController(ILogger Logger, 13 | IConfiguration config, 14 | PipeClient pipeClient) : base(Logger, config, pipeClient) { } 15 | 16 | [HttpGet] 17 | [Route("Status")] 18 | public async Task GetStatus() 19 | { 20 | var status = await pipeClient.InvokeAsync(p => p.GetStatus()); 21 | HideAbsolutePath(status.Task); 22 | return status; 23 | } 24 | 25 | [HttpPost] 26 | [Route("Start")] 27 | public async Task StartAsync() 28 | { 29 | await pipeClient.InvokeAsync(p => p.StartQueue()); 30 | } 31 | 32 | [HttpPost] 33 | [Route("Pause")] 34 | public async Task PauseAsync() 35 | { 36 | await pipeClient.InvokeAsync(p => p.PauseQueue()); 37 | } 38 | 39 | [HttpPost] 40 | [Route("Resume")] 41 | public async Task ResumeAsync() 42 | { 43 | await pipeClient.InvokeAsync(p => p.ResumeQueue()); 44 | } 45 | 46 | [HttpPost] 47 | [Route("Cancel")] 48 | public async Task CancelAsync() 49 | { 50 | await pipeClient.InvokeAsync(p => p.CancelQueue()); 51 | } 52 | [HttpPost] 53 | [Route("Schedule")] 54 | public async Task ScheduleAsync(DateTime time) 55 | { 56 | if (time <= DateTime.Now) 57 | { 58 | throw new ArgumentException("计划的时间早于当前时间"); 59 | } 60 | await pipeClient.InvokeAsync(p => p.ScheduleQueue(time)); 61 | } 62 | [HttpPost] 63 | [Route("CancelSchedule")] 64 | public async Task CancelScheduleAsync() 65 | { 66 | await pipeClient.InvokeAsync(p => p.CancelQueueSchedule()); 67 | } 68 | [HttpGet] 69 | [Route("QueueScheduleTime")] 70 | public async Task GetQueueScheduleTime() 71 | { 72 | return await pipeClient.InvokeAsync(p => p.GetQueueScheduleTime()); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Controllers/TokenController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace SimpleFFmpegGUI.WebAPI.Controllers 6 | { 7 | public class TokenController : FFmpegControllerBase 8 | { 9 | public TokenController(ILogger Logger, 10 | IConfiguration config, 11 | PipeClient pipeClient) : base(Logger, config, pipeClient) { } 12 | 13 | [HttpGet] 14 | [Route("Need")] 15 | public bool NeedToken() 16 | { 17 | return config.GetValue("Token") != null; 18 | } 19 | 20 | [HttpGet] 21 | [Route("Check")] 22 | public bool CheckToken(string token) 23 | { 24 | return config.GetValue("Token") == token; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Converter/DoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace SimpleFFmpegGUI.WebAPI.Converter 6 | { 7 | public class DoubleConverter : JsonConverter 8 | { 9 | public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 10 | { 11 | if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN") 12 | { 13 | return double.NaN; 14 | } 15 | 16 | return reader.GetDouble(); // JsonException thrown if reader.TokenType != JsonTokenType.Number 17 | } 18 | 19 | public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) 20 | { 21 | if (double.IsNaN(value)) 22 | { 23 | writer.WriteNullValue(); 24 | } 25 | if (double.IsInfinity(value)) 26 | { 27 | writer.WriteNullValue(); 28 | } 29 | else 30 | { 31 | writer.WriteNumberValue(value); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Converter/TimeSpanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace SimpleFFmpegGUI.WebAPI.Converter 6 | { 7 | public class TimeSpanConverter : JsonConverter 8 | { 9 | public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 10 | { 11 | if (reader.TokenType == JsonTokenType.Number && reader.GetDouble() > 0) 12 | { 13 | return TimeSpan.FromSeconds(reader.GetDouble()); 14 | } 15 | if (reader.TokenType == JsonTokenType.String && TimeSpan.TryParse(reader.GetString(), out TimeSpan ts)) 16 | { 17 | return ts; 18 | } 19 | return TimeSpan.Zero; 20 | } 21 | 22 | public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) 23 | { 24 | writer.WriteNumberValue(value.TotalSeconds); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Dto/CodePresetDto.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.Model; 2 | 3 | namespace SimpleFFmpegGUI.WebAPI.Dto 4 | { 5 | public class CodePresetDto 6 | { 7 | public string Name { get; set; } 8 | public OutputArguments Arguments { get; set; } 9 | public TaskType Type { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Dto/FtpStatusDto.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleFFmpegGUI.WebAPI.Dto 2 | { 3 | public class FtpStatusDto 4 | { 5 | public bool InputOn { get; set; } 6 | public bool OutputOn { get; set; } 7 | public int InputPort { get; set; } 8 | public int OutputPort { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Dto/TaskDto.cs: -------------------------------------------------------------------------------- 1 | using SimpleFFmpegGUI.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace SimpleFFmpegGUI.WebAPI.Dto 8 | { 9 | public class TaskDto 10 | { 11 | public List Inputs { get; set; } 12 | public string Output { get; set; } 13 | public OutputArguments Argument { get; set; } 14 | public bool Start { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | namespace SimpleFFmpegGUI.WebAPI 12 | { 13 | public class Program 14 | { 15 | internal static bool WebApp { get; private set; } 16 | internal static string PipeName { get; private set; } 17 | 18 | public static void Main(string[] args) 19 | { 20 | CreateHostBuilder().Build().Run(); 21 | } 22 | 23 | public static void Main(int port, string pipeName) 24 | { 25 | PipeName = pipeName; 26 | WebApp = true; 27 | CreateHostBuilder($"http://localhost:{port}/").Build().Run(); 28 | } 29 | 30 | public static IHostBuilder CreateHostBuilder(string url = null) => 31 | Host.CreateDefaultBuilder() 32 | .ConfigureServices(c => 33 | { 34 | c.AddSingleton(); 35 | }) 36 | .ConfigureWebHostDefaults(webBuilder => 37 | { 38 | var builder = webBuilder 39 | .Inject() 40 | .UseStartup(); 41 | if (url != null) 42 | { 43 | builder.UseUrls(url); 44 | } 45 | }); 46 | } 47 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:4561", 7 | "sslPort": 44305 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "SimpleFFmpegGUI.WebAPI": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "launchUrl": "api", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | }, 26 | "dotnetRunMessages": "true", 27 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/SimpleFFmpegGUI.WebAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | false 6 | zh-cn 7 | 8 | 9 | 10 | ..\Generation\Debug\Api 11 | 12 | 13 | 14 | ..\Generation\Release\Api 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ..\libs\FzStandardLib.dll 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http.Features; 4 | using Microsoft.AspNetCore.HttpsPolicy; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.FileProviders; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.Extensions.Logging; 10 | using SimpleFFmpegGUI.WebAPI.Converter; 11 | using System.Collections.Generic; 12 | using System.Threading.Tasks; 13 | 14 | namespace SimpleFFmpegGUI.WebAPI 15 | { 16 | public class Startup 17 | { 18 | public Startup(IConfiguration configuration) 19 | { 20 | Configuration = configuration; 21 | } 22 | 23 | public IConfiguration Configuration { get; } 24 | 25 | // This method gets called by the runtime. Use this method to add services to the container. 26 | public void ConfigureServices(IServiceCollection services) 27 | { 28 | services.AddCorsAccessor(); 29 | services.AddControllers(o => 30 | { 31 | o.Filters.Add(new TokenFilter(Configuration)); 32 | }).AddJsonOptions(o => 33 | { 34 | o.JsonSerializerOptions.Converters.Add(new DoubleConverter()); 35 | o.JsonSerializerOptions.Converters.Add(new TimeSpanConverter()); 36 | }) 37 | .AddInject() 38 | .AddFriendlyException(); 39 | services.Configure(o => 40 | { 41 | o.MultipartBodyLengthLimit = int.MaxValue; 42 | }); 43 | } 44 | 45 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 46 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 47 | { 48 | if (env.IsDevelopment()) 49 | { 50 | app.UseDeveloperExceptionPage(); 51 | } 52 | 53 | app.UseHttpsRedirection(); 54 | if (Program.WebApp) 55 | { 56 | app.UseStaticFiles(new StaticFileOptions() 57 | { 58 | FileProvider = new PhysicalFileProvider(env.ContentRootPath + "/html") 59 | }); 60 | } 61 | app.UseRouting(); 62 | app.UseCorsAccessor(); 63 | app.UseAuthorization(); 64 | app.UseInject(string.Empty); 65 | app.UseEndpoints(endpoints => 66 | { 67 | endpoints.MapControllers(); 68 | }); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/TokenFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Filters; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.Primitives; 5 | using SimpleFFmpegGUI.WebAPI.Controllers; 6 | using System.Linq; 7 | 8 | namespace SimpleFFmpegGUI.WebAPI 9 | { 10 | public class TokenFilter : ActionFilterAttribute 11 | { 12 | private readonly IConfiguration config; 13 | private string token; 14 | 15 | public TokenFilter(IConfiguration config) 16 | { 17 | this.config = config; 18 | } 19 | 20 | public override void OnActionExecuting(ActionExecutingContext context) 21 | { 22 | if (context.Controller is TokenController) 23 | { 24 | return; 25 | } 26 | var http = context.HttpContext; 27 | 28 | if (token == null) 29 | { 30 | token = config.GetValue("token") ?? ""; 31 | } 32 | if (token != "") 33 | { 34 | if (!http.Request.Headers.ContainsKey("Authorization") 35 | || StringValues.IsNullOrEmpty(http.Request.Headers["Authorization"]) 36 | || http.Request.Headers["Authorization"].FirstOrDefault() == "undefined") 37 | { 38 | context.Result = new UnauthorizedObjectResult("需要Token"); 39 | return; 40 | } 41 | if (http.Request.Headers["Authorization"] != token) 42 | { 43 | context.Result = new UnauthorizedObjectResult("Token不正确"); 44 | return; 45 | } 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /SimpleFFmpegGUI.WebAPI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "InputDir": "C:\\Users\\autod\\Desktop\\output", //输入文件夹 11 | "OutputDir": "C:\\Users\\autod\\Desktop\\output", //输出文件夹 12 | "InputDirAccessable": false, //WebAPI是否可以输入输出文件夹。若否,则将通过Host间接访问 13 | "OutputDirAccessable": false, //WebAPI是否可以访问输出文件夹。若否,则将通过Host间接访问 14 | "PipeName": "ffpipe", //管道名 15 | "InputFtpPort": 36734, //输入文件夹FTP端口 16 | "OutputFtpPort": 36735, //输出文件夹FTP端口 17 | "Token": "123456" //设置该值后,需要在Header中配置“Authorization”属性并确保值与该值相同才可以访问API 18 | } -------------------------------------------------------------------------------- /bin/ffmpeg/files.txt: -------------------------------------------------------------------------------- 1 | 在此处放置适用于视频转码的ffmpeg,版本无限制,下载地址:https://www.gyan.dev/ffmpeg/builds/ -------------------------------------------------------------------------------- /bin/ffmpeg_FFME/files.txt: -------------------------------------------------------------------------------- 1 | 在此处放置适用于FFME的、用于视频裁剪的ffmpeg,测试版本:6.1.1 -------------------------------------------------------------------------------- /bin/files.txt: -------------------------------------------------------------------------------- 1 | 在此处放置: 2 | 查询媒体信息的MediaInfo.exe,下载地址:https://mediaarea.net/en/MediaInfo/Download 3 | 编码测试功能的VMAF模型vmaf_v0.6.1.json,下载地址:https://github.com/Netflix/vmaf/blob/master/model/vmaf_v0.6.1.json 4 | 一段测试视频test.mp4。 -------------------------------------------------------------------------------- /clean.ps1: -------------------------------------------------------------------------------- 1 | Remove-Item -r -Force */bin 2 | Remove-Item -r -Force */obj -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/icon.png -------------------------------------------------------------------------------- /icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/icon.psd -------------------------------------------------------------------------------- /imgs/Code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/Code.png -------------------------------------------------------------------------------- /imgs/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/info.png -------------------------------------------------------------------------------- /imgs/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/logs.png -------------------------------------------------------------------------------- /imgs/tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/tasks.png -------------------------------------------------------------------------------- /imgs/wpf_clip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/wpf_clip.jpg -------------------------------------------------------------------------------- /imgs/wpf_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/wpf_info.png -------------------------------------------------------------------------------- /imgs/wpf_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/wpf_main.png -------------------------------------------------------------------------------- /imgs/软件架构图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/软件架构图.png -------------------------------------------------------------------------------- /imgs/软件架构图.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/imgs/软件架构图.vsdx -------------------------------------------------------------------------------- /libs/Enterwell.Clients.Wpf.Notifications.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/libs/Enterwell.Clients.Wpf.Notifications.dll -------------------------------------------------------------------------------- /libs/FzCoreLib.Windows.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/libs/FzCoreLib.Windows.dll -------------------------------------------------------------------------------- /libs/FzStandardLib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/libs/FzStandardLib.dll -------------------------------------------------------------------------------- /libs/ModernWpf.FzExtension.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/libs/ModernWpf.FzExtension.dll -------------------------------------------------------------------------------- /libs/WindowsAPICodePack.FzExtension.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f-shake/RemoteFFmpegGUI/bf7cd4cf5294d5fecd88f6c773572738e5c8d625/libs/WindowsAPICodePack.FzExtension.dll -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | --------------------------------------------------------------------------------