vapoursynth: Show logged messages in progress window
This commit is contained in:
parent
097a0f45be
commit
1f2eaaf6e4
10 changed files with 71 additions and 4 deletions
|
@ -150,11 +150,17 @@ def make_keyframes(clip: vs.VideoNode, use_scxvid: bool = False,
|
|||
.format("scxvid" if use_scxvid else "wwxd"))
|
||||
|
||||
keyframes = {}
|
||||
done = 0
|
||||
def _cb(n: int, f: vs.VideoFrame) -> vs.VideoFrame:
|
||||
nonlocal done
|
||||
keyframes[n] = f.props._SceneChangePrev if use_scxvid else f.props.Scenechange # type: ignore
|
||||
done += 1
|
||||
if done % (clip.num_frames // 25) == 0:
|
||||
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "Detecting keyframes... {}% done.\n".format(100 * done // clip.num_frames))
|
||||
return f
|
||||
|
||||
deque(clip.std.ModifyFrame(clip, _cb).frames(close=True), 0)
|
||||
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "Done detecting keyframes.\n")
|
||||
return [n for n in range(clip.num_frames) if keyframes[n]]
|
||||
|
||||
|
||||
|
@ -178,6 +184,7 @@ def get_keyframes(filename: str, clip: vs.VideoNode, **kwargs: Any) -> str:
|
|||
kffilename = make_keyframes_filename(filename)
|
||||
|
||||
if not os.path.exists(kffilename):
|
||||
vs.core.log_message(vs.MESSAGE_TYPE_INFORMATION, "No keyframes file found, detecting keyframes...\n")
|
||||
keyframes = make_keyframes(clip, **kwargs)
|
||||
save_keyframes(kffilename, keyframes)
|
||||
|
||||
|
|
|
@ -46,10 +46,15 @@ namespace agi {
|
|||
|
||||
/// @brief Log a message
|
||||
///
|
||||
/// If any messages are logged then the dialog will not automatically close
|
||||
/// when the task finishes so that the user has the chance to read them.
|
||||
/// If any messages are logged and StayOpen is set (set by default)
|
||||
/// then the dialog will not automatically close when the task finishes
|
||||
/// so that the user has the chance to read them.
|
||||
virtual void Log(std::string const& str)=0;
|
||||
|
||||
/// Set whether the dialog should stay open after the task finishes.
|
||||
/// Defaults to true.
|
||||
virtual void SetStayOpen(bool stayopen)=0;
|
||||
|
||||
/// Has the user asked the task to cancel?
|
||||
virtual bool IsCancelled()=0;
|
||||
};
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace Automation4 {
|
|||
void SetMessage(std::string const& msg) override { impl->SetMessage(msg); }
|
||||
void SetProgress(int64_t cur, int64_t max) override { impl->SetProgress(cur, max); }
|
||||
void Log(std::string const& str) override { impl->Log(str); }
|
||||
void SetStayOpen(bool stayopen) override { impl->SetStayOpen(stayopen); }
|
||||
bool IsCancelled() override { return impl->IsCancelled(); }
|
||||
|
||||
/// Show the passed dialog on the GUI thread, blocking the calling
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace {
|
|||
class DialogProgressSink final : public agi::ProgressSink {
|
||||
DialogProgress *dialog;
|
||||
std::atomic<bool> cancelled{false};
|
||||
std::atomic<bool> stayopen{true};
|
||||
int progress = 0;
|
||||
|
||||
public:
|
||||
|
@ -98,6 +99,14 @@ public:
|
|||
Main().Async([=]{ dialog->pending_log += to_wx(str); });
|
||||
}
|
||||
|
||||
void SetStayOpen(bool b) override {
|
||||
stayopen = b;
|
||||
}
|
||||
|
||||
bool GetStayOpen() {
|
||||
return stayopen;
|
||||
}
|
||||
|
||||
bool IsCancelled() override {
|
||||
return cancelled;
|
||||
}
|
||||
|
@ -169,7 +178,7 @@ void DialogProgress::Run(std::function<void(agi::ProgressSink*)> task) {
|
|||
// so the user can read the debug output and switch the cancel button to a
|
||||
// close button
|
||||
bool cancelled = this->ps->IsCancelled();
|
||||
if (cancelled || (log_output->IsEmpty() && !pending_log))
|
||||
if (cancelled || !this->ps->GetStayOpen() || (log_output->IsEmpty() && !pending_log))
|
||||
EndModal(!cancelled);
|
||||
else {
|
||||
if (!pending_log.empty()) {
|
||||
|
|
|
@ -373,6 +373,7 @@
|
|||
"Seek Preroll" : 12
|
||||
},
|
||||
"VapourSynth" : {
|
||||
"Log Level": "Information",
|
||||
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport aegisub_vs as a\nimport vapoursynth as vs\n\nclip, videoinfo = a.wrap_lwlibavsource(filename, __aegi_vscache)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n# Uncomment this to automatically generate keyframes at scene changes.\n#__aegi_keyframes = a.get_keyframes(filename, clip)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -373,6 +373,7 @@
|
|||
"Seek Preroll" : 12
|
||||
},
|
||||
"VapourSynth" : {
|
||||
"Log Level": "Information",
|
||||
"Default Script" : "# This default script will load a video file using LWLibavSource.\n# It requires the `lsmas` plugin.\n# See ?data/automation/vapoursynth/aegisub_vs.py for more information.\n\nimport aegisub_vs as a\nimport vapoursynth as vs\n\nclip, videoinfo = a.wrap_lwlibavsource(filename, __aegi_vscache)\nclip.set_output()\n__aegi_timecodes = videoinfo[\"timecodes\"]\n__aegi_keyframes = videoinfo[\"keyframes\"]\n# Uncomment this to automatically generate keyframes at scene changes.\n#__aegi_keyframes = a.get_keyframes(filename, clip)\n\n# Check if the file has an audio track. This requires the `bas` plugin.\n__aegi_hasaudio = 1 if a.check_audio(filename) else 0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -472,6 +472,12 @@ void Advanced_Video(wxTreebook *book, Preferences *parent) {
|
|||
void VapourSynth(wxTreebook *book, Preferences *parent) {
|
||||
#ifdef WITH_VAPOURSYNTH
|
||||
auto p = new OptionPage(book, parent, _("VapourSynth"), OptionPage::PAGE_SUB);
|
||||
auto general = p->PageSizer(_("General"));
|
||||
|
||||
const wxString log_levels[] = { "Quiet", "Fatal", "Critical", "Warning", "Information", "Debug" };
|
||||
wxArrayString log_levels_choice(6, log_levels);
|
||||
p->OptionChoice(general, _("Log Level"), log_levels_choice, "Provider/Video/VapourSynth/Log Level");
|
||||
|
||||
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."));
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "vapoursynth_wrap.h"
|
||||
#include "options.h"
|
||||
#include "utils.h"
|
||||
#include <libaegisub/background_runner.h>
|
||||
#include <libaegisub/fs.h>
|
||||
#include <libaegisub/path.h>
|
||||
|
||||
|
@ -60,6 +61,28 @@ int OpenScriptOrVideo(const VSAPI *api, const VSSCRIPTAPI *sapi, VSScript *scrip
|
|||
return result;
|
||||
}
|
||||
|
||||
void VSLogToProgressSink(int msgType, const char *msg, void *userData) {
|
||||
int loglevel = 0;
|
||||
std::string loglevel_str = OPT_GET("Provider/Video/VapourSynth/Log Level")->GetString();
|
||||
if (loglevel_str == "Quiet")
|
||||
loglevel = 5;
|
||||
else if (loglevel_str == "Fatal")
|
||||
loglevel = 4;
|
||||
else if (loglevel_str == "Critical")
|
||||
loglevel = 3;
|
||||
else if (loglevel_str == "Warning")
|
||||
loglevel = 2;
|
||||
else if (loglevel_str == "Information")
|
||||
loglevel = 1;
|
||||
else if (loglevel_str == "Debug")
|
||||
loglevel = 0;
|
||||
|
||||
if (msgType < loglevel)
|
||||
return;
|
||||
|
||||
reinterpret_cast<agi::ProgressSink *>(userData)->Log(msg);
|
||||
}
|
||||
|
||||
void VSCleanCache() {
|
||||
CleanCache(config::path->Decode("?local/vscache/"),
|
||||
"",
|
||||
|
|
|
@ -21,5 +21,6 @@
|
|||
|
||||
int OpenScriptOrVideo(const VSAPI *api, const VSSCRIPTAPI *sapi, VSScript *script, agi::fs::path const& filename, std::string default_script);
|
||||
void VSCleanCache();
|
||||
void VSLogToProgressSink(int msgType, const char *msg, void *userData);
|
||||
|
||||
#endif // WITH_VAPOURSYNTH
|
||||
|
|
|
@ -123,15 +123,28 @@ VapoursynthVideoProvider::VapoursynthVideoProvider(agi::fs::path const& filename
|
|||
VSCleanCache();
|
||||
|
||||
int err1, err2;
|
||||
script = vs.GetScriptAPI()->createScript(nullptr);
|
||||
VSCore *core = vs.GetAPI()->createCore(0);
|
||||
if (core == nullptr) {
|
||||
throw VapoursynthError("Error creating core");
|
||||
}
|
||||
script = vs.GetScriptAPI()->createScript(core);
|
||||
if (script == nullptr) {
|
||||
vs.GetAPI()->freeCore(core);
|
||||
throw VapoursynthError("Error creating script API");
|
||||
}
|
||||
vs.GetScriptAPI()->evalSetWorkingDir(script, 1);
|
||||
br->Run([&](agi::ProgressSink *ps) {
|
||||
ps->SetTitle(from_wx(_("Executing Vapoursynth Script")));
|
||||
ps->SetMessage("");
|
||||
ps->SetIndeterminate();
|
||||
|
||||
VSLogHandle *logger = vs.GetAPI()->addLogHandler(VSLogToProgressSink, nullptr, ps, core);
|
||||
err1 = OpenScriptOrVideo(vs.GetAPI(), vs.GetScriptAPI(), script, filename, OPT_GET("Provider/Video/VapourSynth/Default Script")->GetString());
|
||||
vs.GetAPI()->removeLogHandler(logger, core);
|
||||
|
||||
ps->SetStayOpen(bool(err1));
|
||||
if (err1)
|
||||
ps->SetMessage(from_wx(_("Failed to execute script! Press \"Close\" to continue.")));
|
||||
});
|
||||
if (err1) {
|
||||
std::string msg = agi::format("Error executing VapourSynth script: %s", vs.GetScriptAPI()->getError(script));
|
||||
|
|
Loading…
Reference in a new issue