diff --git a/src/audio_provider_vs.cpp b/src/audio_provider_vs.cpp index b41b46773..55151e896 100644 --- a/src/audio_provider_vs.cpp +++ b/src/audio_provider_vs.cpp @@ -56,12 +56,14 @@ public: VapoursynthAudioProvider::VapoursynthAudioProvider(agi::fs::path const& filename) try { std::lock_guard lock(vs.GetMutex()); + VSCleanCache(); + script = vs.GetScriptAPI()->createScript(nullptr); if (script == nullptr) { throw VapoursynthError("Error creating script API"); } vs.GetScriptAPI()->evalSetWorkingDir(script, 1); - if (OpenScriptOrVideo(vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Audio/VapourSynth/Default Script")->GetString())) { + if (OpenScriptOrVideo(vs.GetAPI(), 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 4e7828d8c..ceb273735 100644 --- a/src/libresrc/default_config.json +++ b/src/libresrc/default_config.json @@ -371,6 +371,12 @@ "Size" : 42 } }, + "VapourSynth" : { + "Cache" : { + "Files" : 500, + "Size" : 1000 + } + }, "Video" : { "Cache" : { "Size" : 32 diff --git a/src/libresrc/osx/default_config.json b/src/libresrc/osx/default_config.json index 5c40c5e52..e03313dbc 100644 --- a/src/libresrc/osx/default_config.json +++ b/src/libresrc/osx/default_config.json @@ -371,6 +371,12 @@ "Size" : 42 } }, + "VapourSynth" : { + "Cache" : { + "Files" : 500, + "Size" : 1000 + } + }, "Video" : { "Cache" : { "Size" : 32 diff --git a/src/vapoursynth_common.cpp b/src/vapoursynth_common.cpp index 52614528c..9a0e3ebe3 100644 --- a/src/vapoursynth_common.cpp +++ b/src/vapoursynth_common.cpp @@ -17,23 +17,54 @@ #ifdef WITH_VAPOURSYNTH #include "vapoursynth_common.h" +#include "vapoursynth_wrap.h" #include "options.h" +#include "utils.h" #include +#include #include -int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script) { +void SetStringVar(const VSAPI *api, VSMap *map, std::string variable, std::string value) { + if (api->mapSetData(map, variable.c_str(), value.c_str(), -1, dtUtf8, 1)) + throw VapoursynthError("Failed to set VSMap entry"); +} + +int OpenScriptOrVideo(const VSAPI *api, const VSSCRIPTAPI *sapi, VSScript *script, agi::fs::path const& filename, std::string default_script) { int result; if (agi::fs::HasExtension(filename, "py") || agi::fs::HasExtension(filename, "vpy")) { - result = api->evaluateFile(script, filename.string().c_str()); + result = sapi->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; - result = api->evaluateBuffer(script, vscript.c_str(), "aegisub"); + VSMap *map = api->createMap(); + if (map == nullptr) + throw VapoursynthError("Failed to create VSMap for script info"); + + SetStringVar(api, map, "filename", filename.string()); + SetStringVar(api, map, "__aegi_vscache", config::path->Decode("?local/vscache").string()); + for (std::string dir : { "data", "dictionary", "local", "script", "temp", "user", }) + // Don't include ?audio and ?video in here since these only hold the paths to the previous audio/video files. + SetStringVar(api, map, "__aegi_" + dir, config::path->Decode("?" + dir).string()); + + if (sapi->setVariables(script, map)) + throw VapoursynthError("Failed to set script info variables"); + + api->freeMap(map); + + std::string vscript; + vscript += "import sys\n"; + vscript += "sys.path.append(f'{__aegi_data}/automation/vapoursynth')\n"; + vscript += "sys.path.append(f'{__aegi_user}/automation/vapoursynth')\n"; + vscript += default_script; + result = sapi->evaluateBuffer(script, vscript.c_str(), "aegisub"); } return result; } +void VSCleanCache() { + CleanCache(config::path->Decode("?local/vscache/"), + "", + OPT_GET("Provider/VapourSynth/Cache/Size")->GetInt(), + OPT_GET("Provider/VapourSynth/Cache/Files")->GetInt()); +} + #endif // WITH_VAPOURSYNTH diff --git a/src/vapoursynth_common.h b/src/vapoursynth_common.h index 06479e45e..af35da3aa 100644 --- a/src/vapoursynth_common.h +++ b/src/vapoursynth_common.h @@ -19,6 +19,7 @@ #include -int OpenScriptOrVideo(const VSSCRIPTAPI *api, VSScript *script, agi::fs::path const& filename, std::string default_script); +int OpenScriptOrVideo(const VSAPI *api, const VSSCRIPTAPI *sapi, VSScript *script, agi::fs::path const& filename, std::string default_script); +void VSCleanCache(); #endif // WITH_VAPOURSYNTH diff --git a/src/video_provider_vs.cpp b/src/video_provider_vs.cpp index 8ecd621bb..7650a5045 100644 --- a/src/video_provider_vs.cpp +++ b/src/video_provider_vs.cpp @@ -111,12 +111,14 @@ void VapoursynthVideoProvider::SetResizeArg(VSMap *args, const VSMap *props, con VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) try { std::lock_guard lock(vs.GetMutex()); + VSCleanCache(); + script = vs.GetScriptAPI()->createScript(nullptr); if (script == nullptr) { throw VapoursynthError("Error creating script API"); } vs.GetScriptAPI()->evalSetWorkingDir(script, 1); - if (OpenScriptOrVideo(vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Video/VapourSynth/Default Script")->GetString())) { + if (OpenScriptOrVideo(vs.GetAPI(), 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); @@ -141,7 +143,13 @@ VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename // Assume constant frame rate, since handling VFR would require going through all frames when loading. // Users can load custom timecodes files to deal with VFR. // Alternatively (TODO) the provider could read timecodes and keyframes from a second output node. - fps = agi::vfr::Framerate(vi->fpsNum, vi->fpsDen); + int fpsNum = vi->fpsNum; + int fpsDen = vi->fpsDen; + if (fpsDen == 0) { + fpsNum = 25; + fpsDen = 1; + } + fps = agi::vfr::Framerate(fpsNum, fpsDen); // Find the first frame to get some info const VSFrame *frame;