Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ target_sources(raven
editing.h
inspector.h
timeline.h
tools.h
colors.h
widgets.h

app.cpp
editing.cpp
inspector.cpp
timeline.cpp
tools.cpp
colors.cpp
widgets.cpp

Expand Down
93 changes: 93 additions & 0 deletions app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "imgui_internal.h"

#include "widgets.h"
#include "tools.h"

#ifndef EMSCRIPTEN
#include "nfd.h"
Expand Down Expand Up @@ -581,6 +582,14 @@ void MainInit(int argc, char** argv, int initial_width, int initial_height) {

LoadFonts();

// Check for otiotool
if (otiotool_found()) {
appState.otiotool_found = true;
Message("otiotool found, relevant tools have been enabled");
} else {
Message("oitotool not found, relevant tools have been disabled");
}

if (argc > 1) {
LoadFile(argv[1]);
}
Expand Down Expand Up @@ -942,6 +951,24 @@ void MainGui() {
if (appState.show_implot_demo_window) {
ImPlot::ShowDemoWindow();
}

// Handle tool popups
// These modal popups are all triggered by the Tools menu and
// therefore can't have their draw code in the menu code. To
// get around that the menu flags if a modal is to be drawn and
// then we call the relevant ImGui::OpenPopup here. We then call
// our all encompassing DrawToolPopups function.
if (appState.draw_stat_popup) {
ImGui::OpenPopup("Statistics");
appState.draw_stat_popup = false;
}
if (appState.draw_extract_clips) {
ImGui::OpenPopup("Extract Clips");
appState.draw_extract_clips = false;
}
if (GetActiveRoot()) {
DrawToolPopups();
}
}

void SaveTheme() {
Expand Down Expand Up @@ -1066,6 +1093,72 @@ void DrawMenu() {
ImGui::EndMenu();
}

if (ImGui::BeginMenu("Tools", GetActiveRoot() && appState.otiotool_found)) {
std::string current_file = appState.active_tab->file_path;
if (ImGui::MenuItem("Redact OTIO File")) {
if (Redact()) {
Message("Successfully redacted %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to redact %s\n", current_file.c_str());
}
}
if (ImGui::BeginMenu("Extract Track Type")) {
if (ImGui::MenuItem("Video Tracks Only")) {
if (VideoOnly()) {
Message("Sucessfully extracted video tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to extract video tracks from %s\n", current_file.c_str());
}
}
if (ImGui::MenuItem("Audio Tracks Only")) {
if (AudioOnly()) {
Message("Sucessfully extracted audio tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to extract audio tracks from %s\n", current_file.c_str());
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Flatten Tracks")) {
if (ImGui::MenuItem("All")) {
if (FlattenAllTracks()) {
Message("Sucessfully flattened tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to flatten tracks from %s\n", current_file.c_str());
}
}
if (ImGui::MenuItem("Video")) {
if (FlattenVideoTracks()) {
Message("Sucessfully flattened video tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to flatten video tracks from %s\n", current_file.c_str());
}
}
if (ImGui::MenuItem("Audio")) {
if (FlattenAudioTracks()) {
Message("Sucessfully flattened audio tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to flatten audio tracks from %s\n", current_file.c_str());
}
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("Extract Clips")) {
// This option requires user input before the otiotool command
// can be called so we simply flag the popup for drawing here.
appState.draw_extract_clips = true;
}
if (ImGui::MenuItem("Statistics")) {
if (Statistics()) {
Message("Sucessfully returned statistics from %s\n", current_file.c_str());
appState.draw_stat_popup = true;
} else {
ErrorMessage("Failed to return statistics from %s\n", current_file.c_str());
}
}
ImGui::EndMenu();
}

if (ImGui::BeginMenu("View")) {
bool showTimecodeOnClips = appState.track_height >= appState.default_track_height * 2;
if (ImGui::MenuItem(
Expand Down
8 changes: 8 additions & 0 deletions app.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ struct AppState {
bool show_demo_window = false;
bool show_metrics = false;
bool show_implot_demo_window = false;

// Was otiotool found?
bool otiotool_found = false;

// otiotool data for popup windows
std::string otiotool_return_value; // Value returned by call to otiotool
bool draw_stat_popup = false; // Draw statistics popup
bool draw_extract_clips = false; // Draw clip extraction popup
};

extern AppState appState;
Expand Down
2 changes: 1 addition & 1 deletion inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ void DrawInspector() {
}

// Set the active media ref key based on user selection
if (ImGui::Combo("", &appState.selected_reference_index, reference_names.data(), num_references)) {
if (ImGui::Combo("##", &appState.selected_reference_index, reference_names.data(), num_references)) {
if (appState.selected_reference_index >= 0 && appState.selected_reference_index < num_references) {
clip->set_active_media_reference_key(reference_names[appState.selected_reference_index]);
}
Expand Down
220 changes: 220 additions & 0 deletions tools.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Tools
#include "tools.h"

#include "app.h"

#include <map>
#include <filesystem>
#include <cstdio>

std::string run_subprocess(const std::string cmd, int& return_val)
{
#ifdef _WIN32
auto pipe = _popen(cmd.c_str(), "r");
#else
auto pipe = popen(cmd.c_str(), "r");
#endif

if (pipe == nullptr) {
std::cout << "Failed to open pipe" << std::endl;
return_val = 1;
return std::string();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to set return_val in this early exit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks that is a good point

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 1 a suitable return value here or would -1 be better to indicate it isn't returning the value from the subprocess but a prior error?

}

char buffer[8192];
std::string result;
while (fgets(buffer, sizeof buffer, pipe) != NULL){
result += buffer;
}

#ifdef _WIN32
return_val = _pclose(pipe);
#else
return_val = pclose(pipe);
#endif

return result;
}

bool otiotool_found()
{
int result;

run_subprocess("otiotool -h", result);

return !result;
}

std::string run_otiotool_command(std::string options, bool output = true, bool debug = false)
{
// Write the current root to a temp json file
std::filesystem::path file = std::filesystem::temp_directory_path();
file.replace_filename(std::tmpnam(nullptr));
file.replace_extension("otio");
if (debug) {
std::cout << file << std::endl;
}
GetActiveRoot()->to_json_file(file.generic_string());

// Build command, the file path is wrapped in quotation marks in case of spaces
std::string command = "otiotool --input \"" + file.generic_string() + "\" " + options;

// Output otio file?
if (output) {
command += " --output -";
}

if (debug) {
std::cout << command << std::endl;
}

// Run subproces
int return_val = 0;
std::string result = run_subprocess(command, return_val);

// Clean up temp file
std::remove(file.generic_string().c_str());

// Load new otio file
if (!result.empty() && return_val == 0) {
return result;
} else {
ErrorMessage("Error trying to run otiotool command, see console for details");
return "";
}
}

bool load_otio_file_from_otiotool_command(std::string options,bool output = true, bool debug = false)
{
std::string result = run_otiotool_command(options, output, debug);

// Load new otio file
if (!result.empty()) {
LoadString(result);
return true;
} else {
ErrorMessage("Error trying to run otiotool command, see console for details");
return false;
}
}

bool Redact() {
return load_otio_file_from_otiotool_command("--redact");
}

bool VideoOnly() {
return load_otio_file_from_otiotool_command("--video-only");
}

bool AudioOnly() {
return load_otio_file_from_otiotool_command("--audio-only");
}

bool FlattenAllTracks() {
return load_otio_file_from_otiotool_command("--flatten all");
}

bool FlattenVideoTracks() {
return load_otio_file_from_otiotool_command("--flatten video");
}

bool FlattenAudioTracks() {
return load_otio_file_from_otiotool_command("--flatten audio");
}

bool Statistics() {
std::string result = run_otiotool_command("--stats", false, false);

if (!result.empty()) {
appState.otiotool_return_value = result;
return true;
} else {
ErrorMessage("Error trying to run otiotool command, see console for details");
return false;
}
}

void DrawStatisticsPopup()
{
ImGui::Text("Statistics for %s", appState.active_tab->file_path.c_str());
ImGui::Separator();

ImGui::Text("%s", appState.otiotool_return_value.c_str());

if (ImGui::Button("OK", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
appState.otiotool_return_value = "";
}

ImGui::EndPopup();
}

void DrawExtractClipsPopup()
{
static bool use_regex = false;

ImGui::BeginDisabled(use_regex);
static char clip_input[1024];
ImGui::InputText("Clip Name(s)", clip_input, IM_ARRAYSIZE(clip_input));
ImGui::EndDisabled();

ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::Checkbox("Use regex", &use_regex);
ImGui::PopStyleVar();

ImGui::BeginDisabled(!use_regex);
static char regex_input[1024];
ImGui::InputText("Regex", regex_input, IM_ARRAYSIZE(regex_input));
ImGui::EndDisabled();

if (ImGui::Button("OK", ImVec2(120, 0))) {
std::string current_file = appState.active_tab->file_path;
if (!use_regex) {
std::string command = "--only-clips-with-name " + std::string(clip_input);
if (load_otio_file_from_otiotool_command(command, true, true)) {
Message("Sucessfully extracted clips from %s", current_file.c_str());
} else {
ErrorMessage("Failed to extract clips from %s", current_file.c_str());
}
strcpy(clip_input, "");
} else {
std::string command = "--only-clips-with-name-regex " + std::string(regex_input);
if (load_otio_file_from_otiotool_command(command, true, true)) {
Message("Sucessfully extracted clips from %s", current_file.c_str());
} else {
ErrorMessage("Failed to extract clips from %s", current_file.c_str());
}
strcpy(regex_input, "");
}
ImGui::CloseCurrentPopup();
}

ImGui::SetItemDefaultFocus();
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
strcpy(clip_input, "");
strcpy(regex_input, "");
ImGui::CloseCurrentPopup();
}

ImGui::EndPopup();
}

void DrawToolPopups()
{
// Always center this window when appearing
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));

bool close;

// Statistics popup
if (ImGui::BeginPopupModal("Statistics", &close, ImGuiWindowFlags_AlwaysAutoResize)) {
DrawStatisticsPopup();
}

// Clip extraction popup
if (ImGui::BeginPopupModal("Extract Clips", &close, ImGuiWindowFlags_AlwaysAutoResize)) {
DrawExtractClipsPopup();
}
}
Loading