commit a9fb6d78ab3a87bda1c556e9ec0500716bfbe922 Author: tkmxqrdxddd Date: Wed Oct 16 19:42:02 2024 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4c1104 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Prerequisites +*.d +.vscode +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +.output diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..94da212 --- /dev/null +++ b/Makefile @@ -0,0 +1,100 @@ +# +# 'make' build executable file 'main' +# 'make clean' removes all .o and executable files +# + +# define the Cpp compiler to use +CXX = g++ + +# define any compile-time flags +CXXFLAGS := -std=c++17 -Wall -Wextra -g $(shell pkg-config --cflags gtk+-3.0) + +# define library paths in addition to /usr/lib +# if I wanted to include libraries not in /usr/lib I'd specify +# their path using -Lpath, something like: +LFLAGS = + +# define output directory +OUTPUT := output + +# define source directory +SRC := src + +# define include directory +INCLUDE := include + +# define lib directory +LIB := lib + +ifeq ($(OS),Windows_NT) +MAIN := main.exe +SOURCEDIRS := $(SRC) +INCLUDEDIRS := $(INCLUDE) +LIBDIRS := $(LIB) +FIXPATH = $(subst /,\,$1) +RM := del /q /f +MD := mkdir +else +MAIN := main +SOURCEDIRS := $(shell find $(SRC) -type d) +INCLUDEDIRS := $(shell find $(INCLUDE) -type d) +LIBDIRS := $(shell find $(LIB) -type d) +FIXPATH = $1 +RM = rm -f +MD := mkdir -p +endif + +# define any directories containing header files other than /usr/include +INCLUDES := $(patsubst %,-I%, $(INCLUDEDIRS:%/=%)) + +# define the C libs +LIBS := $(patsubst %,-L%, $(LIBDIRS:%/=%)) +LIBS += $(shell pkg-config --libs gtk+-3.0) + +# define the C source files +SOURCES := $(wildcard $(patsubst %,%/*.cpp, $(SOURCEDIRS))) + +# define the C object files +OBJECTS := $(SOURCES:.cpp=.o) + +# define the dependency output files +DEPS := $(OBJECTS:.o=.d) + +# +# The following part of the makefile is generic; it can be used to +# build any executable just by changing the definitions above and by +# deleting dependencies appended to the file from 'make depend' +# + +OUTPUTMAIN := $(call FIXPATH,$(OUTPUT)/$(MAIN)) + +all: $(OUTPUT) $(MAIN) + @echo Executing 'all' complete! + +$(OUTPUT): + $(MD) $(OUTPUT) + +$(MAIN): $(OBJECTS) + $(CXX) $(CXXFLAGS) $(INCLUDES) -o $(OUTPUTMAIN) $(OBJECTS) $(LFLAGS) $(LIBS) + +# include all .d files +-include $(DEPS) + +# this is a suffix replacement rule for building .o's and .d's from .c's +# it uses automatic variables $<: the name of the prerequisite of +# the rule(a .c file) and $@: the name of the target of the rule (a .o file) +# -MMD generates dependency output files same name as the .o file +# (see the gnu make manual section about automatic variables) +.cpp.o: + $(CXX) $(CXXFLAGS) $(INCLUDES) -c -MMD $< -o $@ + +.PHONY: clean +clean: + $(RM) $(OUTPUTMAIN) + $(RM) $(call FIXPATH,$(OBJECTS)) + $(RM) $(call FIXPATH,$(DEPS)) + @echo Cleanup complete! + +run: all + ./$(OUTPUTMAIN) + @echo Executing 'run: all' complete! diff --git a/output/main b/output/main new file mode 100755 index 0000000..ab9a34c Binary files /dev/null and b/output/main differ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..75de2bb --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include + +class VideoConverter { +private: + std::string input_file; + std::string output_file; + std::atomic conversion_in_progress; + GtkWidget *window; + GtkWidget *input_entry; + GtkWidget *output_entry; + GtkWidget *convert_button; + GtkWidget *progress_bar; + + static void on_convert_clicked(GtkWidget *widget, gpointer data) { + auto *converter = static_cast(data); + converter->start_conversion(); + } + + static void on_window_destroy(GtkWidget *widget, gpointer data) { + gtk_main_quit(); + } + + void start_conversion() { + if (conversion_in_progress) { + show_error("Conversion already in progress"); + return; + } + + input_file = gtk_entry_get_text(GTK_ENTRY(input_entry)); + output_file = gtk_entry_get_text(GTK_ENTRY(output_entry)); + + if (input_file.empty() || output_file.empty()) { + show_error("Please enter both input and output file names"); + return; + } + + conversion_in_progress = true; + gtk_widget_set_sensitive(convert_button, FALSE); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), 0.0); + + std::thread conversion_thread(&VideoConverter::convert, this); + conversion_thread.detach(); + } + + void convert() { + std::string command = "ffmpeg -i " + input_file + + " -c:v dnxhd -profile:v dnxhr_hq -pix_fmt yuv422p -c:a alac " + + output_file + " -progress pipe:1"; + + FILE* pipe = popen(command.c_str(), "r"); + if (!pipe) { + gdk_threads_add_idle(G_SOURCE_FUNC(show_error_wrapper), + const_cast("Failed to start FFmpeg process")); + conversion_in_progress = false; + return; + } + + char buffer[128]; + std::string result = ""; + while (!feof(pipe)) { + if (fgets(buffer, 128, pipe) != NULL) { + result += buffer; + if (result.find("progress=end") != std::string::npos) { + gdk_threads_add_idle(G_SOURCE_FUNC(update_progress_wrapper), + const_cast(static_cast(this))); + break; + } + } + } + + int status = pclose(pipe); + conversion_in_progress = false; + + if (status == 0) { + gdk_threads_add_idle(G_SOURCE_FUNC(show_success_wrapper), + const_cast("Video conversion completed successfully")); + } else { + gdk_threads_add_idle(G_SOURCE_FUNC(show_error_wrapper), + const_cast("Error occurred during video conversion")); + } + } + + static gboolean update_progress_wrapper(gpointer data) { + auto *converter = static_cast(data); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(converter->progress_bar), 1.0); + gtk_widget_set_sensitive(converter->convert_button, TRUE); + return FALSE; + } + + static gboolean show_error_wrapper(gpointer data) { + auto *message = static_cast(data); + VideoConverter::show_message_dialog(GTK_MESSAGE_ERROR, message); + return FALSE; + } + + static gboolean show_success_wrapper(gpointer data) { + auto *message = static_cast(data); + VideoConverter::show_message_dialog(GTK_MESSAGE_INFO, message); + return FALSE; + } + + static void show_message_dialog(GtkMessageType type, const char* message) { + GtkWidget *dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_MODAL, + type, + GTK_BUTTONS_OK, + "%s", message); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } + + void show_error(const char* message) { + show_message_dialog(GTK_MESSAGE_ERROR, message); + } + +public: + VideoConverter() : conversion_in_progress(false) {} + + void create_window() { + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "Video Converter"); + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + GtkWidget *grid = gtk_grid_new(); + gtk_container_add(GTK_CONTAINER(window), grid); + + GtkWidget *input_label = gtk_label_new("Input File:"); + gtk_grid_attach(GTK_GRID(grid), input_label, 0, 0, 1, 1); + + input_entry = gtk_entry_new(); + gtk_grid_attach(GTK_GRID(grid), input_entry, 1, 0, 1, 1); + + GtkWidget *output_label = gtk_label_new("Output File:"); + gtk_grid_attach(GTK_GRID(grid), output_label, 0, 1, 1, 1); + + output_entry = gtk_entry_new(); + gtk_grid_attach(GTK_GRID(grid), output_entry, 1, 1, 1, 1); + + convert_button = gtk_button_new_with_label("Convert"); + g_signal_connect(convert_button, "clicked", G_CALLBACK(on_convert_clicked), this); + gtk_grid_attach(GTK_GRID(grid), convert_button, 0, 2, 2, 1); + + progress_bar = gtk_progress_bar_new(); + gtk_grid_attach(GTK_GRID(grid), progress_bar, 0, 3, 2, 1); + + g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL); + + gtk_widget_show_all(window); + } +}; + +int main(int argc, char *argv[]) { + gtk_init(&argc, &argv); + + VideoConverter converter; + converter.create_window(); + + gtk_main(); + + return 0; +}