diff --git a/src/audio_provider_vs.cpp b/src/audio_provider_vs.cpp index 02d469b51..e66779502 100644 --- a/src/audio_provider_vs.cpp +++ b/src/audio_provider_vs.cpp @@ -28,13 +28,13 @@ #include #include -#include #include #include #include #include "vapoursynth_wrap.h" +#include "vapoursynth_common.h" #include "VSScript4.h" namespace { @@ -62,7 +62,7 @@ VapoursynthAudioProvider::VapoursynthAudioProvider(agi::fs::path const& filename throw VapoursynthError("Error creating script API"); } vs.GetScriptAPI()->evalSetWorkingDir(script, 1); - if (vs.GetScriptAPI()->evaluateFile(script, filename.string().c_str())) { + if (OpenScriptOrVideo(vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Audio/VapourSynth/Default Script")->GetString())) { std::string msg = agi::format("Error executing VapourSynth script: %s", vs.GetScriptAPI()->getError(script)); vs.GetScriptAPI()->freeScript(script); throw VapoursynthError(msg); diff --git a/src/libresrc/default_config.json b/src/libresrc/default_config.json index a530abb17..3e76c8e06 100644 --- a/src/libresrc/default_config.json +++ b/src/libresrc/default_config.json @@ -336,6 +336,9 @@ "BestSource": { "Max Cache Size" : 100, "Aegisub Cache" : true + }, + "VapourSynth" : { + "Default Script" : "import vapoursynth as vs\nvs.core.bas.Source(source=filename).set_output()" } }, "Avisynth" : { @@ -362,6 +365,9 @@ "Max Cache Size" : 1024, "Threads" : 0, "Seek Preroll" : 12 + }, + "VapourSynth" : { + "Default Script" : "import vapoursynth as vs\nvs.core.lsmas.LWLibavSource(source=filename).set_output()" } } }, diff --git a/src/libresrc/osx/default_config.json b/src/libresrc/osx/default_config.json index a857a6bf0..61a9e965a 100644 --- a/src/libresrc/osx/default_config.json +++ b/src/libresrc/osx/default_config.json @@ -336,6 +336,9 @@ "BestSource": { "Max Cache Size" : 100, "Aegisub Cache" : true + }, + "VapourSynth" : { + "Default Script" : "import vapoursynth as vs\nvs.core.bas.Source(source=filename).set_output()" } }, "Avisynth" : { @@ -362,6 +365,9 @@ "Max Cache Size" : 1024, "Threads" : 0, "Seek Preroll" : 12 + }, + "VapourSynth" : { + "Default Script" : "import vapoursynth as vs\nvs.core.lsmas.LWLibavSource(source=filename).set_output()" } } }, diff --git a/src/meson.build b/src/meson.build index 4f339ae47..0f73d662b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -240,6 +240,7 @@ opt_src = [ 'video_provider_bestsource.cpp', 'bestsource_common.cpp']], ['VapourSynth', ['vapoursynth_wrap.cpp', + 'vapoursynth_common.cpp', 'audio_provider_vs.cpp', 'video_provider_vs.cpp']], diff --git a/src/preferences.cpp b/src/preferences.cpp index 1945055e0..d97acb88c 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -469,6 +469,32 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) { p->SetSizerAndFit(p->sizer); } +void VapourSynth(wxTreebook *book, Preferences *parent) { +#ifdef WITH_VAPOURSYNTH + auto p = new OptionPage(book, parent, _("VapourSynth"), OptionPage::PAGE_SUB); + auto video = p->PageSizer(_("Default Video Script")); + + auto vhint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load video files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file.")); + p->sizer->Fit(p); + vhint->Wrap(400); + video->Add(vhint, 0, wxALL, 5); + video->AddSpacer(16); + + p->OptionAddMultiline(video, "Provider/Video/VapourSynth/Default Script"); + + auto audio = p->PageSizer(_("Default Audio Script")); + auto ahint = new wxStaticText(p, wxID_ANY, _("This script will be executed to load audio files that aren't\nVapourSynth scripts (i.e. end in .py or .vpy).\nThe filename variable stores the path to the file.")); + p->sizer->Fit(p); + ahint->Wrap(400); + audio->Add(ahint, 0, wxALL, 5); + audio->AddSpacer(16); + + p->OptionAddMultiline(audio, "Provider/Audio/VapourSynth/Default Script"); + + p->SetSizerAndFit(p->sizer); +#endif +} + /// wxDataViewIconTextRenderer with command name autocompletion class CommandRenderer final : public wxDataViewCustomRenderer { wxArrayString autocomplete; @@ -724,6 +750,7 @@ Preferences::Preferences(wxWindow *parent): wxDialog(parent, -1, _("Preferences" Advanced(book, this); Advanced_Audio(book, this); Advanced_Video(book, this); + VapourSynth(book, this); book->Fit(); diff --git a/src/preferences_base.cpp b/src/preferences_base.cpp index cb2c832d7..b0c345c2f 100644 --- a/src/preferences_base.cpp +++ b/src/preferences_base.cpp @@ -156,6 +156,20 @@ wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, co } } +wxControl *OptionPage::OptionAddMultiline(wxSizer *sizer, const char *opt_name) { + parent->AddChangeableOption(opt_name); + const auto opt = OPT_GET(opt_name); + + if (opt->GetType() != agi::OptionType::String) { + throw agi::InternalError("Unsupported type for multiline option"); + } + + auto text = new wxTextCtrl(this, -1, to_wx(opt->GetString()), wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE); + text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent)); + sizer->Add(text, wxSizerFlags().Expand()); + return text; +} + void OptionPage::OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name) { parent->AddChangeableOption(opt_name); const auto opt = OPT_GET(opt_name); diff --git a/src/preferences_base.h b/src/preferences_base.h index d88507f9f..6581f2d0d 100644 --- a/src/preferences_base.h +++ b/src/preferences_base.h @@ -43,6 +43,7 @@ public: void CellSkip(wxFlexGridSizer *flex); wxControl *OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min=0, double max=INT_MAX, double inc=1); + wxControl *OptionAddMultiline(wxSizer *flex, const char *opt_name); void OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name); void OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler = nullptr, bool do_enable = false); void OptionFont(wxSizer *sizer, std::string opt_prefix); diff --git a/src/vapoursynth_common.cpp b/src/vapoursynth_common.cpp new file mode 100644 index 000000000..617849360 --- /dev/null +++ b/src/vapoursynth_common.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2022, arch1t3cht +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#ifdef WITH_VAPOURSYNTH +#include "vapoursynth_common.h" + +#include "options.h" +#include + +#include + +int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script) { + if (agi::fs::HasExtension(filename, "py") || agi::fs::HasExtension(filename, "vpy")) { + return api->evaluateFile(script, filename.string().c_str()); + } else { + std::string fname = filename.string(); + boost::replace_all(fname, "\\", "\\\\"); + boost::replace_all(fname, "'", "\\'"); + std::string vscript = "filename = '" + fname + "'\n" + default_script; + return api->evaluateBuffer(script, vscript.c_str(), "aegisub"); + } +} + +#endif // WITH_VAPOURSYNTH diff --git a/src/vapoursynth_common.h b/src/vapoursynth_common.h new file mode 100644 index 000000000..06479e45e --- /dev/null +++ b/src/vapoursynth_common.h @@ -0,0 +1,24 @@ +// Copyright (c) 2022, arch1t3cht +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#ifdef WITH_VAPOURSYNTH +#include "VSScript4.h" + +#include + +int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script); + +#endif // WITH_VAPOURSYNTH diff --git a/src/video_provider_vs.cpp b/src/video_provider_vs.cpp index 3f9839e38..fa5dfdbf1 100644 --- a/src/video_provider_vs.cpp +++ b/src/video_provider_vs.cpp @@ -22,13 +22,13 @@ #include #include -#include #include #include #include #include "vapoursynth_wrap.h" +#include "vapoursynth_common.h" #include "VSScript4.h" #include "VSHelper4.h" #include "VSConstants4.h" @@ -113,7 +113,7 @@ VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename throw VapoursynthError("Error creating script API"); } vs.GetScriptAPI()->evalSetWorkingDir(script, 1); - if (vs.GetScriptAPI()->evaluateFile(script, filename.string().c_str())) { + if (OpenScriptOrVideo(vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Video/VapourSynth/Default Script")->GetString())) { std::string msg = agi::format("Error executing VapourSynth script: %s", vs.GetScriptAPI()->getError(script)); vs.GetScriptAPI()->freeScript(script); throw VapoursynthError(msg); @@ -279,4 +279,4 @@ namespace agi { class BackgroundRunner; } std::unique_ptr CreateVapoursynthVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *) { return agi::make_unique(path, colormatrix); } -#endif // HAVE_VAPOURSYNTH +#endif // WITH_VAPOURSYNTH