diff --git a/src/audio_provider_factory.cpp b/src/audio_provider_factory.cpp index d849f019b9..58c375bb03 100644 --- a/src/audio_provider_factory.cpp +++ b/src/audio_provider_factory.cpp @@ -26,6 +26,9 @@ #include #include +#include +#include + using namespace agi; std::unique_ptr CreateAvisynthAudioProvider(fs::path const& filename, BackgroundRunner *); @@ -36,16 +39,20 @@ struct factory { const char *name; std::unique_ptr (*create)(fs::path const&, BackgroundRunner *); bool hidden; + std::vector extensions; }; const std::initializer_list providers = { - {"Dummy", CreateDummyAudioProvider, true}, - {"PCM", CreatePCMAudioProvider, true}, + {"Dummy", CreateDummyAudioProvider, true, {}}, + {"PCM", CreatePCMAudioProvider, true, {".w64", ".wav"}}, #ifdef WITH_FFMS2 - {"FFmpegSource", CreateFFmpegSourceAudioProvider, false}, + {"FFmpegSource", CreateFFmpegSourceAudioProvider, false, + {".aac", ".ac3", ".ape", ".asf", ".avi", ".avs", ".d2v", ".dts", ".eac3", ".flac", ".m2ts", + ".m4a", ".m4v", ".mka", ".mkv", ".mov", ".mp3", ".mp4", ".mpeg", ".mpg", ".ogg", ".ogm", ".opus", + ".ts", ".w64", ".wav", ".webm", ".wma", ".wmv"}}, #endif #ifdef WITH_AVISYNTH - {"Avisynth", CreateAvisynthAudioProvider, false}, + {"Avisynth", CreateAvisynthAudioProvider, false, {".avi", ".avs"}}, #endif }; } @@ -54,6 +61,15 @@ std::vector GetAudioProviderNames() { return ::GetClasses(providers); } +std::vector GetAudioProviderFileExtensions() { + std::vector extensions; + for (auto const& provider : providers) + extensions.insert(extensions.end(), provider.extensions.begin(), provider.extensions.end()); + std::sort(extensions.begin(), extensions.end()); + extensions.erase(std::unique(extensions.begin(), extensions.end()), extensions.end()); + return extensions; +} + std::unique_ptr GetAudioProvider(fs::path const& filename, Path const& path_helper, BackgroundRunner *br) { diff --git a/src/audio_provider_factory.h b/src/audio_provider_factory.h index 7e0c6b0649..0ac92b2568 100644 --- a/src/audio_provider_factory.h +++ b/src/audio_provider_factory.h @@ -28,3 +28,4 @@ std::unique_ptr GetAudioProvider(agi::fs::path const& filena agi::Path const& path_helper, agi::BackgroundRunner *br); std::vector GetAudioProviderNames(); +std::vector GetAudioProviderFileExtensions(); diff --git a/src/command/audio.cpp b/src/command/audio.cpp index b2097c73fb..58f5fd5529 100644 --- a/src/command/audio.cpp +++ b/src/command/audio.cpp @@ -42,6 +42,8 @@ #include "../libresrc/libresrc.h" #include "../options.h" #include "../project.h" +#include "../audio_provider_factory.h" +#include "../provider_file_formats.h" #include "../selection_controller.h" #include "../utils.h" #include "../video_controller.h" @@ -79,9 +81,9 @@ struct audio_open final : public Command { STR_HELP("Open an audio file") void operator()(agi::Context *c) override { - auto str = from_wx(_("Audio Formats") + " (*.aac,*.ac3,*.ape,*.dts,*.eac3,*.flac,*.m4a,*.mka,*.mp3,*.mp4,*.ogg,*.opus,*.w64,*.wav,*.wma)|*.aac;*.ac3;*.ape;*.dts;*.eac3;*.flac;*.m4a;*.mka;*.mp3;*.mp4;*.ogg;*.opus;*.w64;*.wav;*.wma|" - + _("Video Formats") + " (*.asf,*.avi,*.avs,*.d2v,*.m2ts,*.m4v,*.mkv,*.mov,*.mp4,*.mpeg,*.mpg,*.ogm,*.webm,*.wmv,*.ts)|*.asf;*.avi;*.avs;*.d2v;*.m2ts;*.m4v;*.mkv;*.mov;*.mp4;*.mpeg;*.mpg;*.ogm;*.webm;*.wmv;*.ts|" - + _("All Files") + " (*.*)|*.*"); + auto formats = MakeWildcard(GetAudioProviderFileExtensions()); + auto str = from_wx(_("Audio and Video Formats")) + " (" + formats + ")|" + formats + "|" + + from_wx(_("All Files")) + " (*.*)|*.*"; auto filename = OpenFileSelector(wxGETTEXT_IN_CONTEXT("dialog title", "Open Audio File"), "Path/Last/Audio", "", "", str, c->parent); if (!filename.empty()) c->project->LoadAudio(filename); diff --git a/src/command/video.cpp b/src/command/video.cpp index f85b670240..deb7cd6925 100644 --- a/src/command/video.cpp +++ b/src/command/video.cpp @@ -44,6 +44,8 @@ #include "../libresrc/libresrc.h" #include "../options.h" #include "../project.h" +#include "../provider_file_formats.h" +#include "../video_provider_manager.h" #include "../selection_controller.h" #include "../utils.h" #include "../video_controller.h" @@ -588,8 +590,9 @@ struct video_open final : public Command { STR_HELP("Open a video file") void operator()(agi::Context *c) override { - auto str = from_wx(_("Video Formats") + " (*.asf,*.avi,*.avs,*.d2v,*.h264,*.hevc,*.m2ts,*.m4v,*.mkv,*.mov,*.mp4,*.mpeg,*.mpg,*.ogm,*.webm,*.wmv,*.ts,*.y4m,*.yuv)|*.asf;*.avi;*.avs;*.d2v;*.h264;*.hevc;*.m2ts;*.m4v;*.mkv;*.mov;*.mp4;*.mpeg;*.mpg;*.ogm;*.webm;*.wmv;*.ts;*.y4m;*.yuv|" - + _("All Files") + " (*.*)|*.*"); + auto formats = MakeWildcard(VideoProviderFactory::GetFileExtensions()); + auto str = from_wx(_("Video Formats")) + " (" + formats + ")|" + formats + "|" + + from_wx(_("All Files")) + " (*.*)|*.*"; auto filename = OpenFileSelector(_("Open Video File"), "Path/Last/Video", "", "", str, c->parent); if (!filename.empty()) c->project->LoadVideo(filename); diff --git a/src/meson.build b/src/meson.build index b5350d7bb5..876d551ffb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -105,6 +105,7 @@ aegisub_src = files( 'preferences.cpp', 'preferences_base.cpp', 'project.cpp', + 'provider_file_formats.cpp', 'resolution_resampler.cpp', 'search_replace_engine.cpp', 'selection_controller.cpp', diff --git a/src/project.cpp b/src/project.cpp index b8a30dc815..5787701ac8 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -23,6 +23,7 @@ #include "audio_provider_factory.h" #include "base_grid.h" #include "charset_detect.h" +#include "audio_provider_factory.h" #include "compat.h" #include "dialog_progress.h" #include "dialogs.h" @@ -31,11 +32,13 @@ #include "include/aegisub/video_provider.h" #include "mkv_wrap.h" #include "options.h" +#include "provider_file_formats.h" #include "selection_controller.h" #include "subs_controller.h" #include "utils.h" #include "video_controller.h" #include "video_display.h" +#include "video_provider_manager.h" #include #include @@ -431,31 +434,6 @@ void Project::CloseKeyframes() { void Project::LoadList(std::vector const& files) { // Keep these lists sorted - // Video formats - const char *videoList[] = { - ".asf", - ".avi", - ".avs", - ".d2v", - ".h264", - ".hevc", - ".m2ts", - ".m4v", - ".mkv", - ".mov", - ".mp4", - ".mpeg", - ".mpg", - ".ogm", - ".rm", - ".rmvb", - ".ts", - ".webm", - ".wmv", - ".y4m", - ".yuv" - }; - // Subtitle formats const char *subsList[] = { ".ass", @@ -465,28 +443,10 @@ void Project::LoadList(std::vector const& files) { ".ttxt" }; - // Audio formats - const char *audioList[] = { - ".aac", - ".ac3", - ".ape", - ".dts", - ".eac3", - ".flac", - ".m4a", - ".mka", - ".mp3", - ".ogg", - ".opus", - ".w64", - ".wav", - ".wma" - }; - - auto search = [](const char **begin, const char **end, std::string const& str) { - return std::binary_search(begin, end, str.c_str(), [](const char *a, const char *b) { - return strcmp(a, b) < 0; - }); + auto video_formats = VideoProviderFactory::GetFileExtensions(); + auto audio_formats = GetAudioProviderFileExtensions(); + auto search = [](auto const& formats, std::string const& str) { + return std::binary_search(formats.begin(), formats.end(), str); }; agi::fs::path audio, video, subs, timecodes, keyframes; @@ -520,11 +480,11 @@ void Project::LoadList(std::vector const& files) { continue; } - if (subs.empty() && search(std::begin(subsList), std::end(subsList), ext)) + if (subs.empty() && std::binary_search(std::begin(subsList), std::end(subsList), ext.c_str(), [](const char *a, const char *b) { return strcmp(a, b) < 0; })) subs = file; - if (video.empty() && search(std::begin(videoList), std::end(videoList), ext)) + if (video.empty() && search(video_formats, ext)) video = file; - if (audio.empty() && search(std::begin(audioList), std::end(audioList), ext)) + if (audio.empty() && search(audio_formats, ext)) audio = file; } diff --git a/src/provider_file_formats.cpp b/src/provider_file_formats.cpp new file mode 100644 index 0000000000..d84500403f --- /dev/null +++ b/src/provider_file_formats.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2026, Aegisub contributors +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted. + +#include "provider_file_formats.h" + +std::string MakeWildcard(std::vector const& extensions) { + std::string result; + for (auto const& extension : extensions) { + if (!result.empty()) result += ';'; + result += '*' + extension; + } + return result; +} diff --git a/src/provider_file_formats.h b/src/provider_file_formats.h new file mode 100644 index 0000000000..6742d29e5a --- /dev/null +++ b/src/provider_file_formats.h @@ -0,0 +1,11 @@ +// Copyright (c) 2026, Aegisub contributors +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted. + +#pragma once + +#include +#include + +/// Turn an extension list into the pattern portion of a wx file-dialog filter. +std::string MakeWildcard(std::vector const& extensions); diff --git a/src/video_provider_manager.cpp b/src/video_provider_manager.cpp index dffefd444e..97983f50a0 100644 --- a/src/video_provider_manager.cpp +++ b/src/video_provider_manager.cpp @@ -27,6 +27,9 @@ #include +#include +#include + std::unique_ptr CreateDummyVideoProvider(agi::fs::path const&, agi::ycbcr::Header, agi::BackgroundRunner *); std::unique_ptr CreateYUV4MPEGVideoProvider(agi::fs::path const&, agi::ycbcr::Header, agi::BackgroundRunner *); std::unique_ptr CreateFFmpegSourceVideoProvider(agi::fs::path const&, agi::ycbcr::Header, agi::BackgroundRunner *); @@ -39,16 +42,19 @@ namespace { const char *name; std::unique_ptr (*create)(agi::fs::path const&, agi::ycbcr::Header, agi::BackgroundRunner *); bool hidden; + std::vector extensions; }; const std::initializer_list providers = { - {"Dummy", CreateDummyVideoProvider, true}, - {"YUV4MPEG", CreateYUV4MPEGVideoProvider, true}, + {"Dummy", CreateDummyVideoProvider, true, {}}, + {"YUV4MPEG", CreateYUV4MPEGVideoProvider, true, {".y4m", ".yuv"}}, #ifdef WITH_FFMS2 - {"FFmpegSource", CreateFFmpegSourceVideoProvider, false}, + {"FFmpegSource", CreateFFmpegSourceVideoProvider, false, + {".asf", ".avi", ".avs", ".d2v", ".h264", ".hevc", ".m2ts", ".m4v", ".mkv", ".mov", ".mp4", + ".mpeg", ".mpg", ".ogm", ".ts", ".webm", ".wmv"}}, #endif #ifdef WITH_AVISYNTH - {"Avisynth", CreateAvisynthVideoProvider, false}, + {"Avisynth", CreateAvisynthVideoProvider, false, {".avi", ".avs", ".d2v"}}, #endif }; } @@ -57,6 +63,15 @@ std::vector VideoProviderFactory::GetClasses() { return ::GetClasses(providers); } +std::vector VideoProviderFactory::GetFileExtensions() { + std::vector extensions; + for (auto const& provider : providers) + extensions.insert(extensions.end(), provider.extensions.begin(), provider.extensions.end()); + std::sort(extensions.begin(), extensions.end()); + extensions.erase(std::unique(extensions.begin(), extensions.end()), extensions.end()); + return extensions; +} + std::unique_ptr VideoProviderFactory::GetProvider(agi::fs::path const& filename, agi::ycbcr::Header colormatrix, agi::BackgroundRunner *br) { auto preferred = OPT_GET("Video/Provider")->GetString(); auto sorted = GetSorted(providers, preferred); diff --git a/src/video_provider_manager.h b/src/video_provider_manager.h index 4c5c4bc750..3b1e86ea2e 100644 --- a/src/video_provider_manager.h +++ b/src/video_provider_manager.h @@ -25,5 +25,6 @@ namespace agi { class BackgroundRunner; } struct VideoProviderFactory { static std::vector GetClasses(); + static std::vector GetFileExtensions(); static std::unique_ptr GetProvider(agi::fs::path const& video_file, agi::ycbcr::Header colormatrix, agi::BackgroundRunner *br); };