Added subtitle settings editor, moved around some settings and dialogs, etc
This commit is contained in:
parent
20616c4fa6
commit
06f793f4f4
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,3 +21,5 @@ make_deb.sh
|
|||||||
make_release.sh
|
make_release.sh
|
||||||
gtk2-media-player-v0.7.1-linux.zip
|
gtk2-media-player-v0.7.1-linux.zip
|
||||||
GTK2-template
|
GTK2-template
|
||||||
|
tools/kino_9.1_amd64.deb
|
||||||
|
tools
|
||||||
48
install.sh
48
install.sh
@ -1,48 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Simple installation script for Kino
|
|
||||||
|
|
||||||
if [ "$EUID" -ne 0 ]; then
|
|
||||||
echo "Please run as root (use sudo)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
INSTALL_DIR="/usr/local/lib/Kino"
|
|
||||||
BIN_LINK="/usr/bin/kino"
|
|
||||||
BIN_LINK_LOCAL="/usr/local/bin/kino"
|
|
||||||
DESKTOP_FILE="/usr/share/applications/kino.desktop"
|
|
||||||
|
|
||||||
echo "Installing to $INSTALL_DIR..."
|
|
||||||
|
|
||||||
# Create directory
|
|
||||||
mkdir -p "$INSTALL_DIR"
|
|
||||||
|
|
||||||
# Copy binary and metadata using 'install' to handle running processes correctly
|
|
||||||
install -m 755 Kino "$INSTALL_DIR/"
|
|
||||||
install -m 644 assets/icon.png "$INSTALL_DIR/"
|
|
||||||
|
|
||||||
# For directory copy, we use cp but remove destination first to ensure clean update
|
|
||||||
rm -rf "$INSTALL_DIR/lib"
|
|
||||||
cp -r lib "$INSTALL_DIR/"
|
|
||||||
|
|
||||||
# Create wrapper scripts
|
|
||||||
cat <<EOF > "$BIN_LINK"
|
|
||||||
#!/bin/bash
|
|
||||||
cd "$INSTALL_DIR"
|
|
||||||
./Kino "\$@"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cp "$BIN_LINK" "$BIN_LINK_LOCAL"
|
|
||||||
chmod +x "$BIN_LINK" "$BIN_LINK_LOCAL"
|
|
||||||
|
|
||||||
# Install desktop file
|
|
||||||
cp assets/kino.desktop "$DESKTOP_FILE"
|
|
||||||
# Update path in desktop file if needed
|
|
||||||
|
|
||||||
# Update MIME database
|
|
||||||
echo "Updating MIME database..."
|
|
||||||
update-desktop-database
|
|
||||||
|
|
||||||
echo "Installation complete!"
|
|
||||||
echo "Installed version timestamp: $(date -r "$INSTALL_DIR/Kino")"
|
|
||||||
echo "You can now run 'kino' or find it in your application menu."
|
|
||||||
BIN
lib/libmpv.so.2
BIN
lib/libmpv.so.2
Binary file not shown.
81
setup.sh
81
setup.sh
@ -1,81 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Universal Setup Script for GTK2 MPV Player
|
|
||||||
# Identifies the Linux distribution and installs required build dependencies.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
PRINT_INFO() { echo -e "\e[34m[INFO]\e[0m $1"; }
|
|
||||||
PRINT_SUCCESS() { echo -e "\e[32m[SUCCESS]\e[0m $1"; }
|
|
||||||
PRINT_ERROR() { echo -e "\e[31m[ERROR]\e[0m $1"; }
|
|
||||||
PRINT_WARNING() { echo -e "\e[33m[WARNING]\e[0m $1"; }
|
|
||||||
|
|
||||||
OS_TYPE=""
|
|
||||||
if [ -f /etc/os-release ]; then
|
|
||||||
. /etc/os-release
|
|
||||||
OS_TYPE=$ID
|
|
||||||
else
|
|
||||||
PRINT_ERROR "Could not detect operating system type. /etc/os-release missing."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
PRINT_INFO "Detected operating system: $OS_TYPE"
|
|
||||||
|
|
||||||
install_debian_deps() {
|
|
||||||
PRINT_INFO "Installing dependencies for Debian/Ubuntu/Mint..."
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y build-essential pkg-config libgtk2.0-dev libmpv-dev
|
|
||||||
}
|
|
||||||
|
|
||||||
install_fedora_deps() {
|
|
||||||
PRINT_INFO "Installing dependencies for Fedora/RHEL/CentOS..."
|
|
||||||
sudo dnf groupinstall -y "Development Tools"
|
|
||||||
sudo dnf install -y pkg-config gtk2-devel mpv-devel
|
|
||||||
}
|
|
||||||
|
|
||||||
install_arch_deps() {
|
|
||||||
PRINT_INFO "Installing dependencies for Arch/Manjaro..."
|
|
||||||
sudo pacman -S --needed base-devel pkg-config gtk2 mpv
|
|
||||||
}
|
|
||||||
|
|
||||||
install_opensuse_deps() {
|
|
||||||
PRINT_INFO "Installing dependencies for openSUSE..."
|
|
||||||
sudo zypper install -t pattern devel_C_C++
|
|
||||||
sudo zypper install -y pkg-config gtk2-devel mpv-devel
|
|
||||||
}
|
|
||||||
|
|
||||||
install_slackware_deps() {
|
|
||||||
PRINT_INFO "Checking dependencies for Slackware..."
|
|
||||||
PRINT_WARNING "Slackware dependency management varies. Please ensure the following are installed:"
|
|
||||||
echo " - gtk+2"
|
|
||||||
echo " - mpv (with client library support)"
|
|
||||||
echo " - pkg-config"
|
|
||||||
echo ""
|
|
||||||
PRINT_INFO "If you have slackpkg+ or similar, you can try searching for 'gtk+2' and 'mpv'."
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$OS_TYPE" in
|
|
||||||
ubuntu|debian|linuxmint|pop|kali|raspbian)
|
|
||||||
install_debian_deps
|
|
||||||
;;
|
|
||||||
fedora|centos|rhel|almalinux|rocky)
|
|
||||||
install_fedora_deps
|
|
||||||
;;
|
|
||||||
arch|manjaro)
|
|
||||||
install_arch_deps
|
|
||||||
;;
|
|
||||||
opensuse*|suse)
|
|
||||||
install_opensuse_deps
|
|
||||||
;;
|
|
||||||
slackware)
|
|
||||||
install_slackware_deps
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
PRINT_WARNING "Unsupported or unknown distribution: $OS_TYPE"
|
|
||||||
PRINT_INFO "Please manually install the development files for: gtk2 and mpv."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
PRINT_SUCCESS "Dependency installation step complete."
|
|
||||||
PRINT_INFO "You can now build the player using 'make'."
|
|
||||||
463
src/dialogs.c
463
src/dialogs.c
@ -1,4 +1,5 @@
|
|||||||
#include "dialogs.h"
|
#include "dialogs.h"
|
||||||
|
#include <pango/pango.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -293,6 +294,33 @@ char* dialogs_save_screenshot(GtkWindow *parent)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dialogs_show_keybinds(GtkWindow *parent)
|
||||||
|
{
|
||||||
|
GtkWidget *dialog = gtk_dialog_new_with_buttons("Keyboard Shortcuts",
|
||||||
|
parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
|
||||||
|
|
||||||
|
gtk_window_set_default_size(GTK_WINDOW(dialog), 350, -1);
|
||||||
|
GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
||||||
|
|
||||||
|
GtkWidget *label = gtk_label_new(
|
||||||
|
"<b>Space</b>\t\t\tPlay/Pause\n"
|
||||||
|
"<b>F / F11</b>\t\t\tFullscreen\n"
|
||||||
|
"<b>Left / Right</b>\tSeek Backward/Forward (5s)\n"
|
||||||
|
"<b>Up / Down</b>\t\tVolume Up/Down\n"
|
||||||
|
"<b>M</b>\t\t\t\tMute\n"
|
||||||
|
"<b>Page Up/Down</b>\tPrevious/Next Chapter\n"
|
||||||
|
"<b>Esc</b>\t\t\t\tExit Fullscreen"
|
||||||
|
);
|
||||||
|
gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(label), 20);
|
||||||
|
gtk_box_pack_start(GTK_BOX(content_area), label, TRUE, TRUE, 0);
|
||||||
|
|
||||||
|
gtk_widget_show_all(dialog);
|
||||||
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
void dialogs_show_about(GtkWindow *parent)
|
void dialogs_show_about(GtkWindow *parent)
|
||||||
{
|
{
|
||||||
GtkWidget *dialog = gtk_dialog_new_with_buttons("About Kino",
|
GtkWidget *dialog = gtk_dialog_new_with_buttons("About Kino",
|
||||||
@ -323,10 +351,10 @@ void dialogs_show_about(GtkWindow *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GtkWidget *title_label = gtk_label_new(NULL);
|
GtkWidget *title_label = gtk_label_new(NULL);
|
||||||
gtk_label_set_markup(GTK_LABEL(title_label), "<span size='xx-large' weight='bold'>Kino</span>");
|
gtk_label_set_markup(GTK_LABEL(title_label), "<span size='x-large' weight='bold'>Kino v9.1</span>");
|
||||||
gtk_box_pack_start(GTK_BOX(about_vbox), title_label, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(about_vbox), title_label, FALSE, FALSE, 0);
|
||||||
|
|
||||||
GtkWidget *version_label = gtk_label_new("Version 9.0");
|
GtkWidget *version_label = gtk_label_new("Version 9.1");
|
||||||
gtk_box_pack_start(GTK_BOX(about_vbox), version_label, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(about_vbox), version_label, FALSE, FALSE, 0);
|
||||||
|
|
||||||
GtkWidget *comment_label = gtk_label_new("A lightweight GTK2 media player frontend (Kino) using libmpv.");
|
GtkWidget *comment_label = gtk_label_new("A lightweight GTK2 media player frontend (Kino) using libmpv.");
|
||||||
@ -557,23 +585,22 @@ static void on_install_script_clicked(GtkButton *button, gpointer user_data)
|
|||||||
gtk_widget_destroy(chooser);
|
gtk_widget_destroy(chooser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dialogs_show_script_manager(GtkWindow *parent, Player *player)
|
static void on_script_manager_widget_destroy(GtkWidget *widget, gpointer data)
|
||||||
|
{
|
||||||
|
(void)widget;
|
||||||
|
ScriptManagerData *sm_data = (ScriptManagerData*)data;
|
||||||
|
if (sm_data->list_store) g_object_unref(sm_data->list_store);
|
||||||
|
g_free(sm_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget* dialogs_create_script_manager_widget(Player *player)
|
||||||
{
|
{
|
||||||
ScriptManagerData *data = g_new0(ScriptManagerData, 1);
|
ScriptManagerData *data = g_new0(ScriptManagerData, 1);
|
||||||
data->player = player;
|
data->player = player;
|
||||||
|
|
||||||
GtkWidget *dialog = gtk_dialog_new_with_buttons("Script Manager",
|
|
||||||
parent,
|
|
||||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
||||||
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
|
|
||||||
NULL);
|
|
||||||
data->dialog = dialog;
|
|
||||||
gtk_window_set_default_size(GTK_WINDOW(dialog), 450, 400);
|
|
||||||
|
|
||||||
GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
||||||
GtkWidget *vbox = gtk_vbox_new(FALSE, 10);
|
GtkWidget *vbox = gtk_vbox_new(FALSE, 10);
|
||||||
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
|
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
|
||||||
gtk_box_pack_start(GTK_BOX(content_area), vbox, TRUE, TRUE, 0);
|
g_signal_connect(vbox, "destroy", G_CALLBACK(on_script_manager_widget_destroy), data);
|
||||||
|
|
||||||
GtkWidget *label = gtk_label_new(NULL);
|
GtkWidget *label = gtk_label_new(NULL);
|
||||||
gtk_label_set_markup(GTK_LABEL(label), "<b>Manage MPV Scripts</b>\nEnabled scripts are loaded automatically on startup.");
|
gtk_label_set_markup(GTK_LABEL(label), "<b>Manage MPV Scripts</b>\nEnabled scripts are loaded automatically on startup.");
|
||||||
@ -586,10 +613,10 @@ void dialogs_show_script_manager(GtkWindow *parent, Player *player)
|
|||||||
gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
|
||||||
|
|
||||||
data->list_store = gtk_list_store_new(NUM_SCRIPT_COLS,
|
data->list_store = gtk_list_store_new(NUM_SCRIPT_COLS,
|
||||||
G_TYPE_STRING, /* Status text (optional display) */
|
G_TYPE_STRING, /* Status text */
|
||||||
G_TYPE_STRING, /* Name */
|
G_TYPE_STRING, /* Name */
|
||||||
G_TYPE_STRING, /* Internal Path */
|
G_TYPE_STRING, /* Internal Path */
|
||||||
G_TYPE_BOOLEAN /* Enabled (for toggle) */
|
G_TYPE_BOOLEAN /* Enabled */
|
||||||
);
|
);
|
||||||
|
|
||||||
GtkWidget *tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(data->list_store));
|
GtkWidget *tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(data->list_store));
|
||||||
@ -631,22 +658,39 @@ void dialogs_show_script_manager(GtkWindow *parent, Player *player)
|
|||||||
gtk_container_add(GTK_CONTAINER(btn_folder), icon_dir);
|
gtk_container_add(GTK_CONTAINER(btn_folder), icon_dir);
|
||||||
gtk_widget_set_tooltip_text(btn_folder, "Open Scripts Folder");
|
gtk_widget_set_tooltip_text(btn_folder, "Open Scripts Folder");
|
||||||
|
|
||||||
|
/* Setup xdg-open command */
|
||||||
const char *cfg_dir = g_get_user_config_dir();
|
const char *cfg_dir = g_get_user_config_dir();
|
||||||
char *s_dir = g_build_filename(cfg_dir, "gtk2-media-player", "scripts", NULL);
|
char *s_dir = g_build_filename(cfg_dir, "gtk2-media-player", "scripts", NULL);
|
||||||
|
/* We don't want to free cmd every time, but here we can just attach it to the button lifecycle */
|
||||||
char *cmd = g_strdup_printf("xdg-open '%s'", s_dir);
|
char *cmd = g_strdup_printf("xdg-open '%s'", s_dir);
|
||||||
g_signal_connect_swapped(btn_folder, "clicked", G_CALLBACK(system), cmd);
|
g_signal_connect_swapped(btn_folder, "clicked", G_CALLBACK(system), cmd);
|
||||||
|
/* Ensure cmd is freed when button is destroyed */
|
||||||
|
g_object_set_data_full(G_OBJECT(btn_folder), "folder-cmd", cmd, g_free);
|
||||||
|
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), btn_folder, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), btn_folder, FALSE, FALSE, 0);
|
||||||
|
|
||||||
refresh_scripts_list(data);
|
refresh_scripts_list(data);
|
||||||
|
g_free(s_dir);
|
||||||
|
|
||||||
|
return vbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dialogs_show_script_manager(GtkWindow *parent, Player *player)
|
||||||
|
{
|
||||||
|
GtkWidget *dialog = gtk_dialog_new_with_buttons("Script Manager",
|
||||||
|
parent,
|
||||||
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
|
||||||
|
NULL);
|
||||||
|
gtk_window_set_default_size(GTK_WINDOW(dialog), 450, 400);
|
||||||
|
|
||||||
|
GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
||||||
|
GtkWidget *sm_widget = dialogs_create_script_manager_widget(player);
|
||||||
|
gtk_box_pack_start(GTK_BOX(content_area), sm_widget, TRUE, TRUE, 0);
|
||||||
|
|
||||||
gtk_widget_show_all(dialog);
|
gtk_widget_show_all(dialog);
|
||||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
g_object_unref(data->list_store);
|
|
||||||
g_free(cmd);
|
|
||||||
g_free(s_dir);
|
|
||||||
g_free(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -658,6 +702,33 @@ static char* get_config_path(void)
|
|||||||
return g_build_filename(config_dir, "gtk2-media-player", "config", NULL);
|
return g_build_filename(config_dir, "gtk2-media-player", "config", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Color Conversion Helpers */
|
||||||
|
static char* gdk_color_to_hex(GdkColor *color)
|
||||||
|
{
|
||||||
|
return g_strdup_printf("#%02X%02X%02X",
|
||||||
|
color->red >> 8,
|
||||||
|
color->green >> 8,
|
||||||
|
color->blue >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hex_to_gdk_color(const char *hex, GdkColor *color)
|
||||||
|
{
|
||||||
|
if (!hex || hex[0] != '#') {
|
||||||
|
color->red = color->green = color->blue = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int r, g, b;
|
||||||
|
if (sscanf(hex + 1, "%02x%02x%02x", &r, &g, &b) == 3) {
|
||||||
|
color->red = (r << 8) | r;
|
||||||
|
color->green = (g << 8) | g;
|
||||||
|
color->blue = (b << 8) | b;
|
||||||
|
} else {
|
||||||
|
color->red = color->green = color->blue = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Preferences* preferences_new(void)
|
Preferences* preferences_new(void)
|
||||||
{
|
{
|
||||||
Preferences *prefs = g_new0(Preferences, 1);
|
Preferences *prefs = g_new0(Preferences, 1);
|
||||||
@ -674,12 +745,32 @@ Preferences* preferences_new(void)
|
|||||||
prefs->enable_osd = TRUE;
|
prefs->enable_osd = TRUE;
|
||||||
prefs->enable_chapters = TRUE;
|
prefs->enable_chapters = TRUE;
|
||||||
prefs->enable_volume_boost = FALSE;
|
prefs->enable_volume_boost = FALSE;
|
||||||
|
prefs->mouse_wheel_seeks = FALSE;
|
||||||
prefs->last_dir = NULL;
|
prefs->last_dir = NULL;
|
||||||
prefs->screenshot_directory = g_strdup(g_get_home_dir());
|
prefs->screenshot_directory = g_strdup(g_get_home_dir());
|
||||||
prefs->screenshot_format = g_strdup("png");
|
prefs->screenshot_format = g_strdup("png");
|
||||||
|
|
||||||
const char *config_dir = g_get_user_config_dir();
|
const char *config_dir = g_get_user_config_dir();
|
||||||
prefs->mpv_config_path = g_build_filename(config_dir, "gtk2-media-player", "mpv.conf", NULL);
|
prefs->mpv_config_path = g_build_filename(config_dir, "gtk2-media-player", "mpv.conf", NULL);
|
||||||
|
|
||||||
|
/* Subtitle defaults */
|
||||||
|
prefs->sub_delay = 0.0;
|
||||||
|
prefs->sub_pos = 100;
|
||||||
|
prefs->sub_scale = 1.0;
|
||||||
|
prefs->sub_font = g_strdup("sans-serif");
|
||||||
|
prefs->sub_font_size = 55;
|
||||||
|
prefs->sub_color = g_strdup("#FFFFFF");
|
||||||
|
prefs->sub_color_opacity = 100;
|
||||||
|
prefs->sub_border_color = g_strdup("#000000");
|
||||||
|
prefs->sub_border_size = 3.0;
|
||||||
|
prefs->sub_border_opacity = 100;
|
||||||
|
prefs->sub_border_enabled = TRUE;
|
||||||
|
prefs->sub_shadow_color = g_strdup("#000000");
|
||||||
|
prefs->sub_shadow_offset = 0.0;
|
||||||
|
prefs->sub_shadow_opacity = 100;
|
||||||
|
prefs->sub_shadow_enabled = TRUE;
|
||||||
|
prefs->sub_bold = FALSE;
|
||||||
|
|
||||||
return prefs;
|
return prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,6 +781,10 @@ void preferences_free(Preferences *prefs)
|
|||||||
g_free(prefs->screenshot_format);
|
g_free(prefs->screenshot_format);
|
||||||
g_free(prefs->mpv_config_path);
|
g_free(prefs->mpv_config_path);
|
||||||
g_free(prefs->last_dir);
|
g_free(prefs->last_dir);
|
||||||
|
g_free(prefs->sub_font);
|
||||||
|
g_free(prefs->sub_color);
|
||||||
|
g_free(prefs->sub_border_color);
|
||||||
|
g_free(prefs->sub_shadow_color);
|
||||||
g_free(prefs);
|
g_free(prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,6 +824,9 @@ void preferences_load(Preferences *prefs)
|
|||||||
load_bool_pref(keyfile, "enable_osd", &prefs->enable_osd);
|
load_bool_pref(keyfile, "enable_osd", &prefs->enable_osd);
|
||||||
load_bool_pref(keyfile, "enable_chapters", &prefs->enable_chapters);
|
load_bool_pref(keyfile, "enable_chapters", &prefs->enable_chapters);
|
||||||
load_bool_pref(keyfile, "enable_volume_boost", &prefs->enable_volume_boost);
|
load_bool_pref(keyfile, "enable_volume_boost", &prefs->enable_volume_boost);
|
||||||
|
load_bool_pref(keyfile, "mouse_wheel_seeks", &prefs->mouse_wheel_seeks);
|
||||||
|
load_bool_pref(keyfile, "sub_border_enabled", &prefs->sub_border_enabled);
|
||||||
|
load_bool_pref(keyfile, "sub_shadow_enabled", &prefs->sub_shadow_enabled);
|
||||||
|
|
||||||
char *screenshot_dir = g_key_file_get_string(keyfile, "General", "screenshot_directory", &error);
|
char *screenshot_dir = g_key_file_get_string(keyfile, "General", "screenshot_directory", &error);
|
||||||
if (!error && screenshot_dir) {
|
if (!error && screenshot_dir) {
|
||||||
@ -765,6 +863,49 @@ void preferences_load(Preferences *prefs)
|
|||||||
g_clear_error(&error);
|
g_clear_error(&error);
|
||||||
g_free(mpv_cfg);
|
g_free(mpv_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Subtitle settings */
|
||||||
|
prefs->sub_delay = g_key_file_get_double(keyfile, "Subtitles", "sub_delay", &error);
|
||||||
|
if (error) { prefs->sub_delay = 0.0; g_clear_error(&error); }
|
||||||
|
|
||||||
|
prefs->sub_pos = g_key_file_get_integer(keyfile, "Subtitles", "sub_pos", &error);
|
||||||
|
if (error) { prefs->sub_pos = 100; g_clear_error(&error); }
|
||||||
|
|
||||||
|
prefs->sub_scale = g_key_file_get_double(keyfile, "Subtitles", "sub_scale", &error);
|
||||||
|
if (error) { prefs->sub_scale = 1.0; g_clear_error(&error); }
|
||||||
|
|
||||||
|
char *sub_font = g_key_file_get_string(keyfile, "Subtitles", "sub_font", &error);
|
||||||
|
if (!error && sub_font) {
|
||||||
|
g_free(prefs->sub_font);
|
||||||
|
prefs->sub_font = sub_font;
|
||||||
|
} else {
|
||||||
|
g_clear_error(&error);
|
||||||
|
g_free(sub_font);
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs->sub_font_size = g_key_file_get_integer(keyfile, "Subtitles", "sub_font_size", &error);
|
||||||
|
if (error) { prefs->sub_font_size = 55; g_clear_error(&error); }
|
||||||
|
|
||||||
|
prefs->sub_color_opacity = g_key_file_get_integer(keyfile, "Subtitles", "sub_color_opacity", &error);
|
||||||
|
if (error) { prefs->sub_color_opacity = 100; g_clear_error(&error); }
|
||||||
|
|
||||||
|
prefs->sub_border_size = g_key_file_get_double(keyfile, "Subtitles", "sub_border_size", &error);
|
||||||
|
if (error) { prefs->sub_border_size = 3.0; g_clear_error(&error); }
|
||||||
|
|
||||||
|
prefs->sub_shadow_offset = g_key_file_get_double(keyfile, "Subtitles", "sub_shadow_offset", &error);
|
||||||
|
if (error) { prefs->sub_shadow_offset = 0.0; g_clear_error(&error); }
|
||||||
|
|
||||||
|
load_bool_pref(keyfile, "sub_bold", &prefs->sub_bold);
|
||||||
|
load_bool_pref(keyfile, "sub_italic", &prefs->sub_italic);
|
||||||
|
|
||||||
|
char *s_color = g_key_file_get_string(keyfile, "Subtitles", "sub_color", &error);
|
||||||
|
if (!error && s_color) { g_free(prefs->sub_color); prefs->sub_color = s_color; } else { g_clear_error(&error); g_free(s_color); }
|
||||||
|
|
||||||
|
char *bd_color = g_key_file_get_string(keyfile, "Subtitles", "sub_border_color", &error);
|
||||||
|
if (!error && bd_color) { g_free(prefs->sub_border_color); prefs->sub_border_color = bd_color; } else { g_clear_error(&error); g_free(bd_color); }
|
||||||
|
|
||||||
|
char *sh_color = g_key_file_get_string(keyfile, "Subtitles", "sub_shadow_color", &error);
|
||||||
|
if (!error && sh_color) { g_free(prefs->sub_shadow_color); prefs->sub_shadow_color = sh_color; } else { g_clear_error(&error); g_free(sh_color); }
|
||||||
}
|
}
|
||||||
|
|
||||||
g_key_file_free(keyfile);
|
g_key_file_free(keyfile);
|
||||||
@ -798,6 +939,9 @@ void preferences_save(Preferences *prefs)
|
|||||||
{"enable_osd", prefs->enable_osd},
|
{"enable_osd", prefs->enable_osd},
|
||||||
{"enable_chapters", prefs->enable_chapters},
|
{"enable_chapters", prefs->enable_chapters},
|
||||||
{"enable_volume_boost", prefs->enable_volume_boost},
|
{"enable_volume_boost", prefs->enable_volume_boost},
|
||||||
|
{"mouse_wheel_seeks", prefs->mouse_wheel_seeks},
|
||||||
|
{"sub_border_enabled", prefs->sub_border_enabled},
|
||||||
|
{"sub_shadow_enabled", prefs->sub_shadow_enabled},
|
||||||
{NULL, FALSE}
|
{NULL, FALSE}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -819,6 +963,23 @@ void preferences_save(Preferences *prefs)
|
|||||||
g_key_file_set_string(keyfile, "General", "mpv_config_path", prefs->mpv_config_path);
|
g_key_file_set_string(keyfile, "General", "mpv_config_path", prefs->mpv_config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Subtitles */
|
||||||
|
g_key_file_set_double(keyfile, "Subtitles", "sub_delay", prefs->sub_delay);
|
||||||
|
g_key_file_set_integer(keyfile, "Subtitles", "sub_pos", prefs->sub_pos);
|
||||||
|
g_key_file_set_double(keyfile, "Subtitles", "sub_scale", prefs->sub_scale);
|
||||||
|
if (prefs->sub_font) {
|
||||||
|
g_key_file_set_string(keyfile, "Subtitles", "sub_font", prefs->sub_font);
|
||||||
|
}
|
||||||
|
g_key_file_set_integer(keyfile, "Subtitles", "sub_font_size", prefs->sub_font_size);
|
||||||
|
g_key_file_set_string(keyfile, "Subtitles", "sub_color", prefs->sub_color);
|
||||||
|
g_key_file_set_integer(keyfile, "Subtitles", "sub_color_opacity", prefs->sub_color_opacity);
|
||||||
|
g_key_file_set_string(keyfile, "Subtitles", "sub_border_color", prefs->sub_border_color);
|
||||||
|
g_key_file_set_double(keyfile, "Subtitles", "sub_border_size", prefs->sub_border_size);
|
||||||
|
g_key_file_set_string(keyfile, "Subtitles", "sub_shadow_color", prefs->sub_shadow_color);
|
||||||
|
g_key_file_set_double(keyfile, "Subtitles", "sub_shadow_offset", prefs->sub_shadow_offset);
|
||||||
|
g_key_file_set_boolean(keyfile, "Subtitles", "sub_bold", prefs->sub_bold);
|
||||||
|
g_key_file_set_boolean(keyfile, "Subtitles", "sub_italic", prefs->sub_italic);
|
||||||
|
|
||||||
gsize length;
|
gsize length;
|
||||||
char *data = g_key_file_to_data(keyfile, &length, NULL);
|
char *data = g_key_file_to_data(keyfile, &length, NULL);
|
||||||
if (data) {
|
if (data) {
|
||||||
@ -872,7 +1033,43 @@ static GtkWidget* create_pref_check(GtkWidget *box, const char *label, gboolean
|
|||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginManager *pm)
|
static void on_sub_font_browse_clicked(GtkButton *btn, gpointer data)
|
||||||
|
{
|
||||||
|
GtkWindow *parent = GTK_WINDOW(data);
|
||||||
|
GtkWidget *entry = g_object_get_data(G_OBJECT(btn), "entry");
|
||||||
|
GtkWidget *font_dialog = gtk_font_selection_dialog_new("Select Subtitle Font");
|
||||||
|
gtk_window_set_transient_for(GTK_WINDOW(font_dialog), parent);
|
||||||
|
|
||||||
|
/* Set current font if possible */
|
||||||
|
const char* current = gtk_entry_get_text(GTK_ENTRY(entry));
|
||||||
|
if (current && strlen(current) > 0) {
|
||||||
|
gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(font_dialog), current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gtk_dialog_run(GTK_DIALOG(font_dialog)) == GTK_RESPONSE_OK) {
|
||||||
|
char *fontname = gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(font_dialog));
|
||||||
|
|
||||||
|
/* MPV sub-font property expects only the font family name, not the full Pango description.
|
||||||
|
* For example, "Arial Bold 12" should be just "Arial". */
|
||||||
|
PangoFontDescription *desc = pango_font_description_from_string(fontname);
|
||||||
|
if (desc) {
|
||||||
|
const char *family = pango_font_description_get_family(desc);
|
||||||
|
if (family) {
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), family);
|
||||||
|
} else {
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), fontname);
|
||||||
|
}
|
||||||
|
pango_font_description_free(desc);
|
||||||
|
} else {
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), fontname);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(fontname);
|
||||||
|
}
|
||||||
|
gtk_widget_destroy(font_dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginManager *pm, int initial_tab, PreferencesApplyCallback apply_cb, gpointer user_data)
|
||||||
{
|
{
|
||||||
if (!prefs) return FALSE;
|
if (!prefs) return FALSE;
|
||||||
|
|
||||||
@ -881,6 +1078,7 @@ gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginM
|
|||||||
parent,
|
parent,
|
||||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||||
|
GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
|
||||||
GTK_STOCK_OK, GTK_RESPONSE_OK,
|
GTK_STOCK_OK, GTK_RESPONSE_OK,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
@ -907,33 +1105,130 @@ gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginM
|
|||||||
|
|
||||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), interface_vbox, gtk_label_new("Interface"));
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), interface_vbox, gtk_label_new("Interface"));
|
||||||
|
|
||||||
/* 2. Config File Tab */
|
/* 2. Subtitles Tab */
|
||||||
GtkWidget *config_vbox = gtk_vbox_new(FALSE, 10);
|
GtkWidget *sub_vbox = gtk_vbox_new(FALSE, 10);
|
||||||
gtk_container_set_border_width(GTK_CONTAINER(config_vbox), 10);
|
gtk_container_set_border_width(GTK_CONTAINER(sub_vbox), 10);
|
||||||
|
|
||||||
GtkWidget *config_info_label = gtk_label_new(NULL);
|
/* Delay */
|
||||||
gtk_label_set_markup(GTK_LABEL(config_info_label), "<b>mpv Configuration File</b>\n"
|
GtkWidget *delay_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
"Specify an external mpv.conf file to load custom settings, shaders, and advanced options.\n"
|
gtk_box_pack_start(GTK_BOX(delay_hbox), gtk_label_new("Subtitle Delay (seconds):"), FALSE, FALSE, 0);
|
||||||
"<i>Note: Standard MPV syntax is supported.</i>");
|
GtkAdjustment *delay_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_delay, -100.0, 100.0, 0.1, 1.0, 0));
|
||||||
gtk_label_set_line_wrap(GTK_LABEL(config_info_label), TRUE);
|
GtkWidget *delay_spin = gtk_spin_button_new(delay_adj, 0.1, 1);
|
||||||
gtk_box_pack_start(GTK_BOX(config_vbox), config_info_label, FALSE, FALSE, 5);
|
gtk_box_pack_start(GTK_BOX(delay_hbox), delay_spin, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), delay_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
GtkWidget *cfg_hbox = gtk_hbox_new(FALSE, 10);
|
/* Position */
|
||||||
GtkWidget *cfg_entry = gtk_entry_new();
|
GtkWidget *subpos_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
gtk_entry_set_text(GTK_ENTRY(cfg_entry), prefs->mpv_config_path ? prefs->mpv_config_path : "");
|
gtk_box_pack_start(GTK_BOX(subpos_hbox), gtk_label_new("Vertical Position (0-100):"), FALSE, FALSE, 0);
|
||||||
gtk_box_pack_start(GTK_BOX(cfg_hbox), cfg_entry, TRUE, TRUE, 0);
|
GtkAdjustment *subpos_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_pos, 0, 100, 1, 10, 0));
|
||||||
|
GtkWidget *subpos_spin = gtk_spin_button_new(subpos_adj, 1, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(subpos_hbox), subpos_spin, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), subpos_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
GtkWidget *cfg_browse_btn = gtk_button_new_with_label("Browse...");
|
/* Scale */
|
||||||
g_object_set_data(G_OBJECT(cfg_browse_btn), "entry", cfg_entry);
|
GtkWidget *scale_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
g_signal_connect(cfg_browse_btn, "clicked", G_CALLBACK(on_cfg_browse_clicked), dialog);
|
gtk_box_pack_start(GTK_BOX(scale_hbox), gtk_label_new("Font Scale:"), FALSE, FALSE, 0);
|
||||||
gtk_box_pack_start(GTK_BOX(cfg_hbox), cfg_browse_btn, FALSE, FALSE, 0);
|
GtkAdjustment *scale_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_scale, 0.1, 10.0, 0.1, 1.0, 0));
|
||||||
gtk_box_pack_start(GTK_BOX(config_vbox), cfg_hbox, FALSE, FALSE, 5);
|
GtkWidget *scale_spin = gtk_spin_button_new(scale_adj, 0.1, 1);
|
||||||
|
gtk_box_pack_start(GTK_BOX(scale_hbox), scale_spin, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), scale_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
GtkWidget *cfg_reset_btn = gtk_button_new_with_label("Reset to Default");
|
/* Font family */
|
||||||
g_signal_connect(cfg_reset_btn, "clicked", G_CALLBACK(on_cfg_reset_clicked), cfg_entry);
|
GtkWidget *font_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
gtk_box_pack_start(GTK_BOX(config_vbox), cfg_reset_btn, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(font_hbox), gtk_label_new("Font Family:"), FALSE, FALSE, 0);
|
||||||
|
GtkWidget *sub_font_entry = gtk_entry_new();
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(sub_font_entry), prefs->sub_font ? prefs->sub_font : "sans-serif");
|
||||||
|
gtk_box_pack_start(GTK_BOX(font_hbox), sub_font_entry, TRUE, TRUE, 0);
|
||||||
|
|
||||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), config_vbox, gtk_label_new("Config File"));
|
GtkWidget *sub_font_btn = gtk_button_new_with_label("Browse...");
|
||||||
|
g_object_set_data(G_OBJECT(sub_font_btn), "entry", sub_font_entry);
|
||||||
|
g_signal_connect(sub_font_btn, "clicked", G_CALLBACK(on_sub_font_browse_clicked), dialog);
|
||||||
|
gtk_box_pack_start(GTK_BOX(font_hbox), sub_font_btn, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), font_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
/* Font Size & Styles */
|
||||||
|
GtkWidget *font_style_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
gtk_box_pack_start(GTK_BOX(font_style_hbox), gtk_label_new("Font Size (px):"), FALSE, FALSE, 0);
|
||||||
|
GtkAdjustment *sub_size_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_font_size, 1, 300, 1, 10, 0));
|
||||||
|
GtkWidget *sub_font_size_spin = gtk_spin_button_new(sub_size_adj, 1, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(font_style_hbox), sub_font_size_spin, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
GtkWidget *sub_bold_check = gtk_check_button_new_with_label("Bold");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sub_bold_check), prefs->sub_bold);
|
||||||
|
gtk_box_pack_start(GTK_BOX(font_style_hbox), sub_bold_check, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
GtkWidget *sub_italic_check = gtk_check_button_new_with_label("Italic");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sub_italic_check), prefs->sub_italic);
|
||||||
|
gtk_box_pack_start(GTK_BOX(font_style_hbox), sub_italic_check, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), font_style_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
/* Primary Color & Opacity */
|
||||||
|
GtkWidget *color_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
gtk_box_pack_start(GTK_BOX(color_hbox), gtk_label_new("Font Color (Primary):"), FALSE, FALSE, 0);
|
||||||
|
GtkWidget *sub_color_btn = gtk_color_button_new();
|
||||||
|
GdkColor sc; hex_to_gdk_color(prefs->sub_color, &sc);
|
||||||
|
gtk_color_button_set_color(GTK_COLOR_BUTTON(sub_color_btn), &sc);
|
||||||
|
gtk_box_pack_start(GTK_BOX(color_hbox), sub_color_btn, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(color_hbox), gtk_label_new("Opacity (%):"), FALSE, FALSE, 0);
|
||||||
|
GtkAdjustment *color_alpha_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_color_opacity, 0, 100, 1, 10, 0));
|
||||||
|
GtkWidget *sub_color_opacity_spin = gtk_spin_button_new(color_alpha_adj, 1, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(color_hbox), sub_color_opacity_spin, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), color_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
/* Border Row */
|
||||||
|
GtkWidget *border_row_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_row_hbox), gtk_label_new("Border:"), FALSE, FALSE, 0);
|
||||||
|
GtkWidget *sub_border_color_btn = gtk_color_button_new();
|
||||||
|
GdkColor bdc; hex_to_gdk_color(prefs->sub_border_color, &bdc);
|
||||||
|
gtk_color_button_set_color(GTK_COLOR_BUTTON(sub_border_color_btn), &bdc);
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_row_hbox), sub_border_color_btn, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_row_hbox), gtk_label_new("Opacity (%):"), FALSE, FALSE, 0);
|
||||||
|
GtkAdjustment *border_op_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_border_opacity, 0, 100, 1, 10, 0));
|
||||||
|
GtkWidget *sub_border_opacity_spin = gtk_spin_button_new(border_op_adj, 1, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_row_hbox), sub_border_opacity_spin, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
GtkWidget *border_check = gtk_check_button_new_with_label("Enabled");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(border_check), prefs->sub_border_enabled);
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_row_hbox), border_check, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), border_row_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
/* Border Size */
|
||||||
|
GtkWidget *border_size_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_size_hbox), gtk_label_new("Border Size:"), FALSE, FALSE, 0);
|
||||||
|
GtkAdjustment *border_size_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_border_size, 0, 10, 0.5, 1, 0));
|
||||||
|
GtkWidget *sub_border_size_spin = gtk_spin_button_new(border_size_adj, 0.5, 1);
|
||||||
|
gtk_box_pack_start(GTK_BOX(border_size_hbox), sub_border_size_spin, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), border_size_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
/* Shadow Row */
|
||||||
|
GtkWidget *shadow_row_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_row_hbox), gtk_label_new("Shadow:"), FALSE, FALSE, 0);
|
||||||
|
GtkWidget *sub_shadow_color_btn = gtk_color_button_new();
|
||||||
|
GdkColor shc; hex_to_gdk_color(prefs->sub_shadow_color, &shc);
|
||||||
|
gtk_color_button_set_color(GTK_COLOR_BUTTON(sub_shadow_color_btn), &shc);
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_row_hbox), sub_shadow_color_btn, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_row_hbox), gtk_label_new("Opacity (%):"), FALSE, FALSE, 0);
|
||||||
|
GtkAdjustment *shadow_op_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_shadow_opacity, 0, 100, 1, 10, 0));
|
||||||
|
GtkWidget *sub_shadow_opacity_spin = gtk_spin_button_new(shadow_op_adj, 1, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_row_hbox), sub_shadow_opacity_spin, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
GtkWidget *shadow_check = gtk_check_button_new_with_label("Enabled");
|
||||||
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shadow_check), prefs->sub_shadow_enabled);
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_row_hbox), shadow_check, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), shadow_row_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
/* Shadow Offset */
|
||||||
|
GtkWidget *shadow_off_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_off_hbox), gtk_label_new("Shadow Offset:"), FALSE, FALSE, 0);
|
||||||
|
GtkAdjustment *shadow_offset_adj = GTK_ADJUSTMENT(gtk_adjustment_new(prefs->sub_shadow_offset, 0, 10, 0.5, 1, 0));
|
||||||
|
GtkWidget *sub_shadow_offset_spin = gtk_spin_button_new(shadow_offset_adj, 0.5, 1);
|
||||||
|
gtk_box_pack_start(GTK_BOX(shadow_off_hbox), sub_shadow_offset_spin, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(sub_vbox), shadow_off_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), sub_vbox, gtk_label_new("Subtitles"));
|
||||||
|
|
||||||
/* 3. Miscellaneous Tab */
|
/* 3. Miscellaneous Tab */
|
||||||
GtkWidget *misc_vbox = gtk_vbox_new(FALSE, 10);
|
GtkWidget *misc_vbox = gtk_vbox_new(FALSE, 10);
|
||||||
@ -947,6 +1242,7 @@ gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginM
|
|||||||
gtk_box_pack_start(GTK_BOX(misc_vbox), vol_hbox, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(misc_vbox), vol_hbox, FALSE, FALSE, 0);
|
||||||
|
|
||||||
GtkWidget *vol_boost_check = create_pref_check(misc_vbox, "Enable volume boost (up to 150%)", prefs->enable_volume_boost);
|
GtkWidget *vol_boost_check = create_pref_check(misc_vbox, "Enable volume boost (up to 150%)", prefs->enable_volume_boost);
|
||||||
|
GtkWidget *mouse_wheel_check = create_pref_check(misc_vbox, "Mouse wheel seeks through video instead of adjusting volume", prefs->mouse_wheel_seeks);
|
||||||
GtkWidget *position_check = create_pref_check(misc_vbox, "Remember playback position", prefs->remember_position);
|
GtkWidget *position_check = create_pref_check(misc_vbox, "Remember playback position", prefs->remember_position);
|
||||||
GtkWidget *dir_check = create_pref_check(misc_vbox, "Remember last folder in file chooser", prefs->remember_last_dir);
|
GtkWidget *dir_check = create_pref_check(misc_vbox, "Remember last folder in file chooser", prefs->remember_last_dir);
|
||||||
GtkWidget *mpris_check = create_pref_check(misc_vbox, "Enable MPRIS support (D-Bus)", prefs->use_mpris);
|
GtkWidget *mpris_check = create_pref_check(misc_vbox, "Enable MPRIS support (D-Bus)", prefs->use_mpris);
|
||||||
@ -980,10 +1276,49 @@ gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginM
|
|||||||
GtkWidget *plugins_vbox = plugin_manager_create_config_widget(pm);
|
GtkWidget *plugins_vbox = plugin_manager_create_config_widget(pm);
|
||||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), plugins_vbox, gtk_label_new("Plugins"));
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), plugins_vbox, gtk_label_new("Plugins"));
|
||||||
|
|
||||||
|
/* 5. Scripts Tab */
|
||||||
|
GtkWidget *scripts_vbox = dialogs_create_script_manager_widget(plugin_manager_get_player(pm));
|
||||||
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scripts_vbox, gtk_label_new("Scripts"));
|
||||||
|
|
||||||
|
/* 6. Config File Tab */
|
||||||
|
GtkWidget *config_vbox = gtk_vbox_new(FALSE, 10);
|
||||||
|
gtk_container_set_border_width(GTK_CONTAINER(config_vbox), 10);
|
||||||
|
|
||||||
|
GtkWidget *config_info_label = gtk_label_new(NULL);
|
||||||
|
gtk_label_set_markup(GTK_LABEL(config_info_label), "<b>mpv Configuration File</b>\n"
|
||||||
|
"Specify an external mpv.conf file to load custom settings, shaders, and advanced options.\n"
|
||||||
|
"<i>Note: Standard MPV syntax is supported.</i>");
|
||||||
|
gtk_label_set_line_wrap(GTK_LABEL(config_info_label), TRUE);
|
||||||
|
gtk_box_pack_start(GTK_BOX(config_vbox), config_info_label, FALSE, FALSE, 5);
|
||||||
|
|
||||||
|
GtkWidget *cfg_hbox = gtk_hbox_new(FALSE, 10);
|
||||||
|
GtkWidget *cfg_entry = gtk_entry_new();
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(cfg_entry), prefs->mpv_config_path ? prefs->mpv_config_path : "");
|
||||||
|
gtk_box_pack_start(GTK_BOX(cfg_hbox), cfg_entry, TRUE, TRUE, 0);
|
||||||
|
|
||||||
|
GtkWidget *cfg_browse_btn = gtk_button_new_with_label("Browse...");
|
||||||
|
g_object_set_data(G_OBJECT(cfg_browse_btn), "entry", cfg_entry);
|
||||||
|
g_signal_connect(cfg_browse_btn, "clicked", G_CALLBACK(on_cfg_browse_clicked), dialog);
|
||||||
|
gtk_box_pack_start(GTK_BOX(cfg_hbox), cfg_browse_btn, FALSE, FALSE, 0);
|
||||||
|
gtk_box_pack_start(GTK_BOX(config_vbox), cfg_hbox, FALSE, FALSE, 5);
|
||||||
|
|
||||||
|
GtkWidget *cfg_reset_btn = gtk_button_new_with_label("Reset to Default");
|
||||||
|
g_signal_connect(cfg_reset_btn, "clicked", G_CALLBACK(on_cfg_reset_clicked), cfg_entry);
|
||||||
|
gtk_box_pack_start(GTK_BOX(config_vbox), cfg_reset_btn, FALSE, FALSE, 0);
|
||||||
|
|
||||||
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), config_vbox, gtk_label_new("Config File"));
|
||||||
|
|
||||||
|
if (initial_tab >= 0 && initial_tab < gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook))) {
|
||||||
|
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), initial_tab);
|
||||||
|
}
|
||||||
|
|
||||||
gtk_widget_show_all(dialog);
|
gtk_widget_show_all(dialog);
|
||||||
|
|
||||||
gboolean result = FALSE;
|
int response;
|
||||||
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
|
gboolean changed = FALSE;
|
||||||
|
do {
|
||||||
|
response = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||||
|
if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY) {
|
||||||
prefs->default_volume = gtk_spin_button_get_value(GTK_SPIN_BUTTON(vol_spin));
|
prefs->default_volume = gtk_spin_button_get_value(GTK_SPIN_BUTTON(vol_spin));
|
||||||
prefs->remember_position = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(position_check));
|
prefs->remember_position = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(position_check));
|
||||||
prefs->show_playlist = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_check));
|
prefs->show_playlist = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_check));
|
||||||
@ -997,6 +1332,38 @@ gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginM
|
|||||||
prefs->enable_osd = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(osd_check));
|
prefs->enable_osd = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(osd_check));
|
||||||
prefs->enable_chapters = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chapters_check));
|
prefs->enable_chapters = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chapters_check));
|
||||||
prefs->enable_volume_boost = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(vol_boost_check));
|
prefs->enable_volume_boost = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(vol_boost_check));
|
||||||
|
prefs->mouse_wheel_seeks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mouse_wheel_check));
|
||||||
|
|
||||||
|
/* Subtitles */
|
||||||
|
prefs->sub_delay = gtk_spin_button_get_value(GTK_SPIN_BUTTON(delay_spin));
|
||||||
|
prefs->sub_pos = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(subpos_spin));
|
||||||
|
prefs->sub_scale = gtk_spin_button_get_value(GTK_SPIN_BUTTON(scale_spin));
|
||||||
|
g_free(prefs->sub_font);
|
||||||
|
prefs->sub_font = g_strdup(gtk_entry_get_text(GTK_ENTRY(sub_font_entry)));
|
||||||
|
|
||||||
|
prefs->sub_font_size = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sub_font_size_spin));
|
||||||
|
prefs->sub_bold = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sub_bold_check));
|
||||||
|
prefs->sub_italic = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sub_italic_check));
|
||||||
|
prefs->sub_border_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(border_check));
|
||||||
|
prefs->sub_shadow_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shadow_check));
|
||||||
|
|
||||||
|
GdkColor c;
|
||||||
|
gtk_color_button_get_color(GTK_COLOR_BUTTON(sub_color_btn), &c);
|
||||||
|
g_free(prefs->sub_color);
|
||||||
|
prefs->sub_color = gdk_color_to_hex(&c);
|
||||||
|
prefs->sub_color_opacity = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sub_color_opacity_spin));
|
||||||
|
|
||||||
|
gtk_color_button_get_color(GTK_COLOR_BUTTON(sub_border_color_btn), &c);
|
||||||
|
g_free(prefs->sub_border_color);
|
||||||
|
prefs->sub_border_color = gdk_color_to_hex(&c);
|
||||||
|
prefs->sub_border_size = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sub_border_size_spin));
|
||||||
|
prefs->sub_border_opacity = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sub_border_opacity_spin));
|
||||||
|
|
||||||
|
gtk_color_button_get_color(GTK_COLOR_BUTTON(sub_shadow_color_btn), &c);
|
||||||
|
g_free(prefs->sub_shadow_color);
|
||||||
|
prefs->sub_shadow_color = gdk_color_to_hex(&c);
|
||||||
|
prefs->sub_shadow_offset = gtk_spin_button_get_value(GTK_SPIN_BUTTON(sub_shadow_offset_spin));
|
||||||
|
prefs->sub_shadow_opacity = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(sub_shadow_opacity_spin));
|
||||||
|
|
||||||
g_free(prefs->screenshot_directory);
|
g_free(prefs->screenshot_directory);
|
||||||
prefs->screenshot_directory = g_strdup(gtk_entry_get_text(GTK_ENTRY(ss_entry)));
|
prefs->screenshot_directory = g_strdup(gtk_entry_get_text(GTK_ENTRY(ss_entry)));
|
||||||
@ -1008,11 +1375,13 @@ gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, PluginM
|
|||||||
prefs->mpv_config_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(cfg_entry)));
|
prefs->mpv_config_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(cfg_entry)));
|
||||||
|
|
||||||
preferences_save(prefs);
|
preferences_save(prefs);
|
||||||
result = TRUE;
|
if (apply_cb) apply_cb(user_data);
|
||||||
|
changed = TRUE;
|
||||||
}
|
}
|
||||||
|
} while (response == GTK_RESPONSE_APPLY);
|
||||||
|
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
return result;
|
return (response == GTK_RESPONSE_OK || (response == GTK_RESPONSE_CANCEL && changed));
|
||||||
}
|
}
|
||||||
|
|
||||||
double dialogs_goto_time(GtkWindow *parent, double current_time, double duration)
|
double dialogs_goto_time(GtkWindow *parent, double current_time, double duration)
|
||||||
|
|||||||
@ -23,17 +23,37 @@ typedef struct {
|
|||||||
gboolean enable_osd;
|
gboolean enable_osd;
|
||||||
gboolean enable_chapters;
|
gboolean enable_chapters;
|
||||||
gboolean enable_volume_boost;
|
gboolean enable_volume_boost;
|
||||||
|
gboolean mouse_wheel_seeks;
|
||||||
char *last_dir;
|
char *last_dir;
|
||||||
char *screenshot_directory;
|
char *screenshot_directory;
|
||||||
char *screenshot_format;
|
char *screenshot_format;
|
||||||
char *mpv_config_path;
|
char *mpv_config_path;
|
||||||
|
/* Subtitle settings */
|
||||||
|
double sub_delay;
|
||||||
|
int sub_pos;
|
||||||
|
double sub_scale;
|
||||||
|
char *sub_font;
|
||||||
|
int sub_font_size;
|
||||||
|
char *sub_color;
|
||||||
|
int sub_color_opacity;
|
||||||
|
char *sub_border_color;
|
||||||
|
double sub_border_size;
|
||||||
|
int sub_border_opacity;
|
||||||
|
gboolean sub_border_enabled;
|
||||||
|
char *sub_shadow_color;
|
||||||
|
double sub_shadow_offset;
|
||||||
|
int sub_shadow_opacity;
|
||||||
|
gboolean sub_shadow_enabled;
|
||||||
|
gboolean sub_bold;
|
||||||
|
gboolean sub_italic;
|
||||||
} Preferences;
|
} Preferences;
|
||||||
|
|
||||||
Preferences* preferences_new(void);
|
Preferences* preferences_new(void);
|
||||||
void preferences_free(Preferences *prefs);
|
void preferences_free(Preferences *prefs);
|
||||||
void preferences_load(Preferences *prefs);
|
void preferences_load(Preferences *prefs);
|
||||||
void preferences_save(Preferences *prefs);
|
void preferences_save(Preferences *prefs);
|
||||||
gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, struct PluginManager *pm);
|
typedef void (*PreferencesApplyCallback)(gpointer data);
|
||||||
|
gboolean dialogs_show_preferences(GtkWindow *parent, Preferences *prefs, struct PluginManager *pm, int initial_tab, PreferencesApplyCallback apply_cb, gpointer user_data);
|
||||||
|
|
||||||
/* File dialogs */
|
/* File dialogs */
|
||||||
GSList* dialogs_open_files(GtkWindow *parent, Preferences *prefs);
|
GSList* dialogs_open_files(GtkWindow *parent, Preferences *prefs);
|
||||||
@ -43,8 +63,9 @@ char* dialogs_open_subtitle(GtkWindow *parent, Preferences *prefs);
|
|||||||
char* dialogs_save_playlist(GtkWindow *parent, Preferences *prefs);
|
char* dialogs_save_playlist(GtkWindow *parent, Preferences *prefs);
|
||||||
char* dialogs_save_screenshot(GtkWindow *parent);
|
char* dialogs_save_screenshot(GtkWindow *parent);
|
||||||
|
|
||||||
/* About dialog */
|
/* About and Keybinds dialogs */
|
||||||
void dialogs_show_about(GtkWindow *parent);
|
void dialogs_show_about(GtkWindow *parent);
|
||||||
|
void dialogs_show_keybinds(GtkWindow *parent);
|
||||||
|
|
||||||
/* Go to time dialog */
|
/* Go to time dialog */
|
||||||
double dialogs_goto_time(GtkWindow *parent, double current_time, double duration);
|
double dialogs_goto_time(GtkWindow *parent, double current_time, double duration);
|
||||||
@ -54,5 +75,6 @@ double dialogs_select_speed(GtkWindow *parent, double current_speed);
|
|||||||
|
|
||||||
/* Script Manager dialog */
|
/* Script Manager dialog */
|
||||||
void dialogs_show_script_manager(GtkWindow *parent, Player *player);
|
void dialogs_show_script_manager(GtkWindow *parent, Player *player);
|
||||||
|
GtkWidget* dialogs_create_script_manager_widget(Player *player);
|
||||||
|
|
||||||
#endif /* DIALOGS_H */
|
#endif /* DIALOGS_H */
|
||||||
|
|||||||
@ -83,7 +83,7 @@ int main(int argc, char *argv[])
|
|||||||
/* Basic command line parsing */
|
/* Basic command line parsing */
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
||||||
printf("Media Player v0.7\n");
|
printf("Kino v9.1\n");
|
||||||
printf("Usage: %s [options] [files...]\n\n", argv[0]);
|
printf("Usage: %s [options] [files...]\n\n", argv[0]);
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -h, --help Show this help message\n");
|
printf(" -h, --help Show this help message\n");
|
||||||
@ -99,7 +99,7 @@ int main(int argc, char *argv[])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
|
if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
|
||||||
printf("Kino v9.0\n");
|
printf("Kino v9.1\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ int load_mpv_library(void)
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(lib_names) / sizeof(lib_names[0]); i++) {
|
for (size_t i = 0; i < sizeof(lib_names) / sizeof(lib_names[0]); i++) {
|
||||||
lib_handle = dlopen(lib_names[i], RTLD_LAZY);
|
lib_handle = dlopen(lib_names[i], RTLD_NOW | RTLD_GLOBAL);
|
||||||
if (lib_handle) {
|
if (lib_handle) {
|
||||||
printf("Loaded mpv library from: %s\n", lib_names[i]);
|
printf("Loaded mpv library from: %s\n", lib_names[i]);
|
||||||
break;
|
break;
|
||||||
|
|||||||
137
src/player.c
137
src/player.c
@ -569,6 +569,143 @@ void player_set_subtitle_track(Player *player, int track)
|
|||||||
check_error(mpv_set_property_string(player->mpv, "sid", sid_str));
|
check_error(mpv_set_property_string(player->mpv, "sid", sid_str));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_delay(Player *player, double delay)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char val[32];
|
||||||
|
snprintf(val, sizeof(val), "%f", delay);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-delay", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_pos(Player *player, int pos)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char val[16];
|
||||||
|
snprintf(val, sizeof(val), "%d", pos);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-pos", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_scale(Player *player, double scale)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char val[32];
|
||||||
|
snprintf(val, sizeof(val), "%f", scale);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-scale", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_font(Player *player, const char *font)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
|
||||||
|
if (font && strlen(font) > 0) {
|
||||||
|
mpv_set_property_string(player->mpv, "sub-font", font);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ALWAYS Force override subtitles to use our selected style/colors/box */
|
||||||
|
mpv_set_property_string(player->mpv, "sub-ass-override", "force");
|
||||||
|
mpv_set_property_string(player->mpv, "sub-style-override", "force");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* compose_color_with_alpha(const char *hex_color, int opacity)
|
||||||
|
{
|
||||||
|
if (!hex_color || hex_color[0] != '#') return g_strdup("#ffffffff");
|
||||||
|
|
||||||
|
/* Ensure opacity is within 0-100 */
|
||||||
|
if (opacity < 0) opacity = 0;
|
||||||
|
if (opacity > 100) opacity = 100;
|
||||||
|
|
||||||
|
if (opacity == 100) {
|
||||||
|
/* Standard #RRGGBB is most compatible when opaque */
|
||||||
|
return g_strdup(hex_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
int alpha = (opacity * 255) / 100;
|
||||||
|
unsigned int r, g, b;
|
||||||
|
if (sscanf(hex_color + 1, "%02x%02x%02x", &r, &g, &b) == 3) {
|
||||||
|
/* MPV expects #AARRGGBB for colors with alpha */
|
||||||
|
return g_strdup_printf("#%02x%02x%02x%02x", alpha, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_strdup("#ffffffff");
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_font_size(Player *player, int size)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char val[16];
|
||||||
|
snprintf(val, sizeof(val), "%d", size);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-font-size", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_color(Player *player, const char *hex_color, int opacity)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char *full_color = compose_color_with_alpha(hex_color, opacity);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-color", full_color);
|
||||||
|
g_free(full_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_border_color(Player *player, const char *hex_color, int opacity)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char *full_color = compose_color_with_alpha(hex_color, opacity);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-border-color", full_color);
|
||||||
|
g_free(full_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_border_size(Player *player, double size)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char val[16];
|
||||||
|
snprintf(val, sizeof(val), "%.1f", size);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-border-size", val);
|
||||||
|
}
|
||||||
|
void player_set_subtitle_border_enabled(Player *player, gboolean enabled)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
/* In mpv, setting sub-border-size to 0 disables the border */
|
||||||
|
if (!enabled) {
|
||||||
|
mpv_set_property_string(player->mpv, "sub-border-size", "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_shadow_color(Player *player, const char *hex_color, int opacity)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char *full_color = compose_color_with_alpha(hex_color, opacity);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-shadow-color", full_color);
|
||||||
|
g_free(full_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_shadow_offset(Player *player, double offset)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
char val[16];
|
||||||
|
snprintf(val, sizeof(val), "%.1f", offset);
|
||||||
|
mpv_set_property_string(player->mpv, "sub-shadow-offset", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_shadow_enabled(Player *player, gboolean enabled)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
/* In mpv, setting sub-shadow-offset to 0 effectively disables the shadow */
|
||||||
|
if (!enabled) {
|
||||||
|
mpv_set_property_string(player->mpv, "sub-shadow-offset", "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_bold(Player *player, gboolean bold)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
mpv_set_property_string(player->mpv, "sub-bold", bold ? "yes" : "no");
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_set_subtitle_italic(Player *player, gboolean italic)
|
||||||
|
{
|
||||||
|
if (!player || !player->mpv) return;
|
||||||
|
mpv_set_property_string(player->mpv, "sub-italic", italic ? "yes" : "no");
|
||||||
|
}
|
||||||
|
|
||||||
void player_load_subtitle(Player *player, const char *path)
|
void player_load_subtitle(Player *player, const char *path)
|
||||||
{
|
{
|
||||||
if (!player || !player->mpv || !path) return;
|
if (!player || !player->mpv || !path) return;
|
||||||
|
|||||||
14
src/player.h
14
src/player.h
@ -60,6 +60,20 @@ int player_get_current_subtitle_track(Player *player);
|
|||||||
void player_set_audio_track(Player *player, int track);
|
void player_set_audio_track(Player *player, int track);
|
||||||
void player_set_subtitle_track(Player *player, int track);
|
void player_set_subtitle_track(Player *player, int track);
|
||||||
void player_load_subtitle(Player *player, const char *path);
|
void player_load_subtitle(Player *player, const char *path);
|
||||||
|
void player_set_subtitle_delay(Player *player, double delay);
|
||||||
|
void player_set_subtitle_pos(Player *player, int pos);
|
||||||
|
void player_set_subtitle_scale(Player *player, double scale);
|
||||||
|
void player_set_subtitle_font(Player *player, const char *font);
|
||||||
|
void player_set_subtitle_font_size(Player *player, int size);
|
||||||
|
void player_set_subtitle_color(Player *player, const char *hex_color, int opacity);
|
||||||
|
void player_set_subtitle_border_color(Player *player, const char *hex_color, int opacity);
|
||||||
|
void player_set_subtitle_border_size(Player *player, double size);
|
||||||
|
void player_set_subtitle_shadow_color(Player *player, const char *hex_color, int opacity);
|
||||||
|
void player_set_subtitle_shadow_offset(Player *player, double offset);
|
||||||
|
void player_set_subtitle_bold(Player *player, gboolean bold);
|
||||||
|
void player_set_subtitle_italic(Player *player, gboolean italic);
|
||||||
|
void player_set_subtitle_border_enabled(Player *player, gboolean enabled);
|
||||||
|
void player_set_subtitle_shadow_enabled(Player *player, gboolean enabled);
|
||||||
|
|
||||||
/* Audio/Video info */
|
/* Audio/Video info */
|
||||||
char** player_get_audio_track_list(Player *player, int *count);
|
char** player_get_audio_track_list(Player *player, int *count);
|
||||||
|
|||||||
@ -153,6 +153,12 @@ void plugin_manager_destroy(PluginManager *pm)
|
|||||||
g_pm = NULL;
|
g_pm = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player* plugin_manager_get_player(PluginManager *pm)
|
||||||
|
{
|
||||||
|
if (!pm || !pm->ui) return NULL;
|
||||||
|
return ui_get_player(pm->ui);
|
||||||
|
}
|
||||||
|
|
||||||
void plugin_manager_set_player_callbacks(PluginManager *pm,
|
void plugin_manager_set_player_callbacks(PluginManager *pm,
|
||||||
void (*play)(void),
|
void (*play)(void),
|
||||||
void (*pause)(void),
|
void (*pause)(void),
|
||||||
@ -617,15 +623,7 @@ GtkWidget* plugin_manager_get_menu(PluginManager *pm)
|
|||||||
|
|
||||||
GtkWidget *install_item = gtk_menu_item_new_with_label("Install Plugin...");
|
GtkWidget *install_item = gtk_menu_item_new_with_label("Install Plugin...");
|
||||||
g_signal_connect(install_item, "activate", G_CALLBACK(on_install_plugin), pm);
|
g_signal_connect(install_item, "activate", G_CALLBACK(on_install_plugin), pm);
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), install_item);
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), install_item); /* Tools / Plugins menu doesn't need script management anymore as it's in Preferences */
|
||||||
|
|
||||||
GtkWidget *manage_scripts_item = gtk_menu_item_new_with_label("Manage Scripts...");
|
|
||||||
g_signal_connect_swapped(manage_scripts_item, "activate", G_CALLBACK(ui_show_script_manager), pm->ui);
|
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), manage_scripts_item);
|
|
||||||
|
|
||||||
GtkWidget *scripts_item = gtk_menu_item_new_with_label("Open Scripts Folder...");
|
|
||||||
g_signal_connect_swapped(scripts_item, "activate", G_CALLBACK(ui_open_scripts_folder), pm->ui);
|
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), scripts_item);
|
|
||||||
|
|
||||||
/* Manage plugins - Removed as per user request (moved to preferences) */
|
/* Manage plugins - Removed as per user request (moved to preferences) */
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
typedef struct PluginManager PluginManager;
|
typedef struct PluginManager PluginManager;
|
||||||
|
typedef struct Player Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loaded plugin instance
|
* Loaded plugin instance
|
||||||
@ -49,6 +50,11 @@ PluginManager* plugin_manager_new(AppUI *ui);
|
|||||||
*/
|
*/
|
||||||
void plugin_manager_destroy(PluginManager *pm);
|
void plugin_manager_destroy(PluginManager *pm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the player instance associated with this manager
|
||||||
|
*/
|
||||||
|
Player* plugin_manager_get_player(PluginManager *pm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set player control callbacks
|
* Set player control callbacks
|
||||||
* Must be called before loading plugins
|
* Must be called before loading plugins
|
||||||
|
|||||||
285
src/ui.c
285
src/ui.c
@ -103,6 +103,9 @@ struct AppUI {
|
|||||||
|
|
||||||
/* Plugin manager */
|
/* Plugin manager */
|
||||||
PluginManager *plugin_manager;
|
PluginManager *plugin_manager;
|
||||||
|
|
||||||
|
/* Subtitle dragging */
|
||||||
|
gboolean sub_dragging;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
@ -118,6 +121,7 @@ static void on_drag_data_received(GtkWidget *widget, GdkDragContext *context, gi
|
|||||||
static gboolean on_video_area_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer data);
|
static gboolean on_video_area_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer data);
|
||||||
static gboolean on_video_area_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data);
|
static gboolean on_video_area_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data);
|
||||||
static gboolean on_video_area_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data);
|
static gboolean on_video_area_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data);
|
||||||
|
static gboolean on_video_area_button_release(GtkWidget *widget, GdkEventButton *event, gpointer data);
|
||||||
static gboolean auto_hide_timer_cb(gpointer data);
|
static gboolean auto_hide_timer_cb(gpointer data);
|
||||||
static void add_path_to_playlist(AppUI *ui, const char *path, gboolean clear);
|
static void add_path_to_playlist(AppUI *ui, const char *path, gboolean clear);
|
||||||
static void on_recent_item_activated(GtkRecentChooser *chooser, gpointer data);
|
static void on_recent_item_activated(GtkRecentChooser *chooser, gpointer data);
|
||||||
@ -146,10 +150,13 @@ static void on_subtitle_disable(GtkMenuItem *item, gpointer data);
|
|||||||
static void on_view_fullscreen(GtkMenuItem *item, gpointer data);
|
static void on_view_fullscreen(GtkMenuItem *item, gpointer data);
|
||||||
static void on_view_playlist(GtkMenuItem *item, gpointer data);
|
static void on_view_playlist(GtkMenuItem *item, gpointer data);
|
||||||
static void on_view_always_on_top(GtkMenuItem *item, gpointer data);
|
static void on_view_always_on_top(GtkMenuItem *item, gpointer data);
|
||||||
|
static void on_always_on_top_toggled(GtkCheckMenuItem *item, gpointer data);
|
||||||
static void on_view_screenshot(GtkMenuItem *item, gpointer data);
|
static void on_view_screenshot(GtkMenuItem *item, gpointer data);
|
||||||
static void on_view_preferences(GtkMenuItem *item, gpointer data);
|
static void on_view_preferences(GtkMenuItem *item, gpointer data);
|
||||||
|
static void on_subtitle_settings_clicked(GtkMenuItem *item, gpointer data);
|
||||||
static void on_aspect_ratio_selected(GtkMenuItem *item, gpointer data);
|
static void on_aspect_ratio_selected(GtkMenuItem *item, gpointer data);
|
||||||
static void on_audio_mute(GtkMenuItem *item, gpointer data);
|
static void on_audio_mute(GtkMenuItem *item, gpointer data);
|
||||||
|
static void on_help_keybinds(GtkMenuItem *item, gpointer data);
|
||||||
static void on_help_about(GtkMenuItem *item, gpointer data);
|
static void on_help_about(GtkMenuItem *item, gpointer data);
|
||||||
|
|
||||||
/* Plugin API wrapper forward declarations */
|
/* Plugin API wrapper forward declarations */
|
||||||
@ -252,6 +259,7 @@ AppUI* ui_new(void)
|
|||||||
g_signal_connect(ui->video_area, "realize", G_CALLBACK(on_video_area_realize), ui);
|
g_signal_connect(ui->video_area, "realize", G_CALLBACK(on_video_area_realize), ui);
|
||||||
g_signal_connect(ui->video_container, "scroll-event", G_CALLBACK(on_video_area_scroll), ui);
|
g_signal_connect(ui->video_container, "scroll-event", G_CALLBACK(on_video_area_scroll), ui);
|
||||||
g_signal_connect(ui->video_container, "button-press-event", G_CALLBACK(on_video_area_button_press), ui);
|
g_signal_connect(ui->video_container, "button-press-event", G_CALLBACK(on_video_area_button_press), ui);
|
||||||
|
g_signal_connect(ui->video_container, "button-release-event", G_CALLBACK(on_video_area_button_release), ui);
|
||||||
g_signal_connect(ui->video_container, "motion-notify-event", G_CALLBACK(on_video_area_motion), ui);
|
g_signal_connect(ui->video_container, "motion-notify-event", G_CALLBACK(on_video_area_motion), ui);
|
||||||
|
|
||||||
/* Enable drag and drop on container as well */
|
/* Enable drag and drop on container as well */
|
||||||
@ -325,6 +333,12 @@ AppUI* ui_new(void)
|
|||||||
mpris_init(ui);
|
mpris_init(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Apply initial subtitle preferences */
|
||||||
|
player_set_subtitle_delay(ui->player, ui->prefs->sub_delay);
|
||||||
|
player_set_subtitle_pos(ui->player, ui->prefs->sub_pos);
|
||||||
|
player_set_subtitle_scale(ui->player, ui->prefs->sub_scale);
|
||||||
|
player_set_subtitle_font(ui->player, ui->prefs->sub_font);
|
||||||
|
|
||||||
return ui;
|
return ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +366,56 @@ void ui_destroy(AppUI *ui)
|
|||||||
g_free(ui);
|
g_free(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ui_apply_subtitle_preferences(AppUI *ui)
|
||||||
|
{
|
||||||
|
if (!ui || !ui->player || !ui->prefs) return;
|
||||||
|
|
||||||
|
player_set_subtitle_delay(ui->player, ui->prefs->sub_delay);
|
||||||
|
player_set_subtitle_pos(ui->player, ui->prefs->sub_pos);
|
||||||
|
player_set_subtitle_scale(ui->player, ui->prefs->sub_scale);
|
||||||
|
player_set_subtitle_font(ui->player, ui->prefs->sub_font);
|
||||||
|
player_set_subtitle_font_size(ui->player, ui->prefs->sub_font_size);
|
||||||
|
player_set_subtitle_color(ui->player, ui->prefs->sub_color, ui->prefs->sub_color_opacity);
|
||||||
|
player_set_subtitle_border_color(ui->player, ui->prefs->sub_border_color, ui->prefs->sub_border_opacity);
|
||||||
|
player_set_subtitle_border_size(ui->player, ui->prefs->sub_border_size);
|
||||||
|
player_set_subtitle_border_enabled(ui->player, ui->prefs->sub_border_enabled);
|
||||||
|
player_set_subtitle_shadow_color(ui->player, ui->prefs->sub_shadow_color, ui->prefs->sub_shadow_opacity);
|
||||||
|
player_set_subtitle_shadow_offset(ui->player, ui->prefs->sub_shadow_offset);
|
||||||
|
player_set_subtitle_shadow_enabled(ui->player, ui->prefs->sub_shadow_enabled);
|
||||||
|
player_set_subtitle_bold(ui->player, ui->prefs->sub_bold);
|
||||||
|
player_set_subtitle_italic(ui->player, ui->prefs->sub_italic);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ui_apply_preferences_internal(gpointer data)
|
||||||
|
{
|
||||||
|
AppUI *ui = (AppUI *)data;
|
||||||
|
if (!ui || !ui->player || !ui->prefs) return;
|
||||||
|
|
||||||
|
/* Apply subtitle settings */
|
||||||
|
ui_apply_subtitle_preferences(ui);
|
||||||
|
|
||||||
|
/* Volume settings */
|
||||||
|
player_set_volume(ui->player, ui->prefs->default_volume);
|
||||||
|
player_set_volume_boost(ui->player, ui->prefs->enable_volume_boost);
|
||||||
|
|
||||||
|
/* Mouse cursor hiding in windowed mode */
|
||||||
|
if (!ui->is_fullscreen) {
|
||||||
|
if (ui->prefs->hide_cursor_windowed) {
|
||||||
|
if (ui->auto_hide_timer_id == 0) {
|
||||||
|
ui->auto_hide_timer_id = g_timeout_add(3000, auto_hide_timer_cb, ui);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ui->auto_hide_timer_id > 0) {
|
||||||
|
g_source_remove(ui->auto_hide_timer_id);
|
||||||
|
ui->auto_hide_timer_id = 0;
|
||||||
|
}
|
||||||
|
/* Restore cursor */
|
||||||
|
GdkWindow *window = gtk_widget_get_window(ui->video_area);
|
||||||
|
if (window) gdk_window_set_cursor(window, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ui_set_preferences(AppUI *ui, Preferences *prefs)
|
void ui_set_preferences(AppUI *ui, Preferences *prefs)
|
||||||
{
|
{
|
||||||
if (!ui || !prefs) return;
|
if (!ui || !prefs) return;
|
||||||
@ -370,6 +434,8 @@ void ui_set_preferences(AppUI *ui, Preferences *prefs)
|
|||||||
if (ui->prefs->screenshot_directory) {
|
if (ui->prefs->screenshot_directory) {
|
||||||
player_set_option_string(ui->player, "screenshot-directory", ui->prefs->screenshot_directory);
|
player_set_option_string(ui->player, "screenshot-directory", ui->prefs->screenshot_directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui_apply_subtitle_preferences(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ui->controls) {
|
if (ui->controls) {
|
||||||
@ -821,6 +887,8 @@ static void on_player_event(Player *player, mpv_event *event, gpointer user_data
|
|||||||
case MPV_EVENT_START_FILE:
|
case MPV_EVENT_START_FILE:
|
||||||
ui->last_mpris_pos = 0;
|
ui->last_mpris_pos = 0;
|
||||||
mpris_update_playback_status(ui);
|
mpris_update_playback_status(ui);
|
||||||
|
/* Re-apply subtitle preferences for each new file */
|
||||||
|
ui_apply_subtitle_preferences(ui);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPV_EVENT_FILE_LOADED: {
|
case MPV_EVENT_FILE_LOADED: {
|
||||||
@ -1041,13 +1109,21 @@ static gboolean on_video_area_scroll(GtkWidget *widget, GdkEventScroll *event, g
|
|||||||
{
|
{
|
||||||
(void)widget;
|
(void)widget;
|
||||||
AppUI *ui = (AppUI *)data;
|
AppUI *ui = (AppUI *)data;
|
||||||
double vol = player_get_volume(ui->player);
|
|
||||||
|
|
||||||
|
if (ui->prefs->mouse_wheel_seeks) {
|
||||||
|
if (event->direction == GDK_SCROLL_UP) {
|
||||||
|
player_seek(ui->player, 5);
|
||||||
|
} else if (event->direction == GDK_SCROLL_DOWN) {
|
||||||
|
player_seek(ui->player, -5);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
double vol = player_get_volume(ui->player);
|
||||||
if (event->direction == GDK_SCROLL_UP) {
|
if (event->direction == GDK_SCROLL_UP) {
|
||||||
player_set_volume(ui->player, vol + 5);
|
player_set_volume(ui->player, vol + 5);
|
||||||
} else if (event->direction == GDK_SCROLL_DOWN) {
|
} else if (event->direction == GDK_SCROLL_DOWN) {
|
||||||
player_set_volume(ui->player, vol - 5);
|
player_set_volume(ui->player, vol - 5);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -1063,11 +1139,39 @@ static gboolean on_video_area_button_press(GtkWidget *widget, GdkEventButton *ev
|
|||||||
} else if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
} else if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
||||||
on_playback_play_pause(NULL, ui);
|
on_playback_play_pause(NULL, ui);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
} else if (event->type == GDK_BUTTON_PRESS && event->button == 1 && (event->state & GDK_CONTROL_MASK)) {
|
||||||
|
ui->sub_dragging = TRUE;
|
||||||
|
/* Update position immediately */
|
||||||
|
GtkAllocation alloc;
|
||||||
|
gtk_widget_get_allocation(ui->video_area, &alloc);
|
||||||
|
if (alloc.height > 0) {
|
||||||
|
int pos = (int)((event->y / alloc.height) * 100);
|
||||||
|
if (pos < 0) pos = 0;
|
||||||
|
if (pos > 100) pos = 100;
|
||||||
|
player_set_subtitle_pos(ui->player, pos);
|
||||||
|
|
||||||
|
/* Show feedback */
|
||||||
|
char msg[32];
|
||||||
|
snprintf(msg, sizeof(msg), "Subtitle Position: %d%%", pos);
|
||||||
|
const char *cmd[] = {"show-text", msg, "1000", NULL};
|
||||||
|
mpv_command_async(player_get_mpv_handle(ui->player), 0, cmd);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean on_video_area_button_release(GtkWidget *widget, GdkEventButton *event, gpointer data)
|
||||||
|
{
|
||||||
|
(void)widget;
|
||||||
|
AppUI *ui = (AppUI *)data;
|
||||||
|
if (event->button == 1) {
|
||||||
|
ui->sub_dragging = FALSE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean auto_hide_timer_cb(gpointer data)
|
static gboolean auto_hide_timer_cb(gpointer data)
|
||||||
{
|
{
|
||||||
AppUI *ui = (AppUI *)data;
|
AppUI *ui = (AppUI *)data;
|
||||||
@ -1098,9 +1202,29 @@ static gboolean auto_hide_timer_cb(gpointer data)
|
|||||||
static gboolean on_video_area_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data)
|
static gboolean on_video_area_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data)
|
||||||
{
|
{
|
||||||
(void)widget;
|
(void)widget;
|
||||||
(void)event;
|
|
||||||
AppUI *ui = (AppUI *)data;
|
AppUI *ui = (AppUI *)data;
|
||||||
|
|
||||||
|
if (ui->sub_dragging) {
|
||||||
|
GtkAllocation alloc;
|
||||||
|
gtk_widget_get_allocation(ui->video_area, &alloc);
|
||||||
|
if (alloc.height > 0) {
|
||||||
|
int pos = (int)((event->y / alloc.height) * 100);
|
||||||
|
if (pos < 0) pos = 0;
|
||||||
|
if (pos > 100) pos = 100;
|
||||||
|
player_set_subtitle_pos(ui->player, pos);
|
||||||
|
|
||||||
|
/* Update UI preferences state so it survives restart/dialog */
|
||||||
|
if (ui->prefs) ui->prefs->sub_pos = pos;
|
||||||
|
|
||||||
|
/* Show feedback */
|
||||||
|
char msg[32];
|
||||||
|
snprintf(msg, sizeof(msg), "Subtitle Position: %d%%", pos);
|
||||||
|
const char *cmd[] = {"show-text", msg, "500", NULL};
|
||||||
|
mpv_command_async(player_get_mpv_handle(ui->player), 0, cmd);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
if (ui->is_fullscreen || ui->prefs->hide_cursor_windowed) {
|
if (ui->is_fullscreen || ui->prefs->hide_cursor_windowed) {
|
||||||
/* Show controls */
|
/* Show controls */
|
||||||
if (ui->is_fullscreen && ui->fs_window) {
|
if (ui->is_fullscreen && ui->fs_window) {
|
||||||
@ -1200,6 +1324,13 @@ void update_track_menus(AppUI *ui)
|
|||||||
/* Update Subtitle Menu */
|
/* Update Subtitle Menu */
|
||||||
clear_menu(ui->subtitle_menu);
|
clear_menu(ui->subtitle_menu);
|
||||||
|
|
||||||
|
GtkWidget *sub_settings_item = gtk_menu_item_new_with_label("Subtitle Settings...");
|
||||||
|
g_signal_connect(sub_settings_item, "activate", G_CALLBACK(on_subtitle_settings_clicked), ui);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(ui->subtitle_menu), sub_settings_item);
|
||||||
|
|
||||||
|
GtkWidget *sub_sep = gtk_separator_menu_item_new();
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(ui->subtitle_menu), sub_sep);
|
||||||
|
|
||||||
int sub_count = 0;
|
int sub_count = 0;
|
||||||
char **sub_tracks = player_get_subtitle_track_list(ui->player, &sub_count);
|
char **sub_tracks = player_get_subtitle_track_list(ui->player, &sub_count);
|
||||||
|
|
||||||
@ -1398,6 +1529,13 @@ static void create_menubar(AppUI *ui)
|
|||||||
GtkWidget *subtitle_item = gtk_menu_item_new_with_mnemonic("_Subtitles");
|
GtkWidget *subtitle_item = gtk_menu_item_new_with_mnemonic("_Subtitles");
|
||||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(subtitle_item), subtitle_root_menu);
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(subtitle_item), subtitle_root_menu);
|
||||||
|
|
||||||
|
GtkWidget *sub_settings_item = gtk_menu_item_new_with_label("Subtitle Settings...");
|
||||||
|
g_signal_connect(sub_settings_item, "activate", G_CALLBACK(on_subtitle_settings_clicked), ui);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(subtitle_root_menu), sub_settings_item);
|
||||||
|
|
||||||
|
GtkWidget *sub_sep = gtk_separator_menu_item_new();
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(subtitle_root_menu), sub_sep);
|
||||||
|
|
||||||
GtkWidget *sub_tracks_item = gtk_menu_item_new_with_mnemonic("_Select Track");
|
GtkWidget *sub_tracks_item = gtk_menu_item_new_with_mnemonic("_Select Track");
|
||||||
ui->subtitle_menu = gtk_menu_new();
|
ui->subtitle_menu = gtk_menu_new();
|
||||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(sub_tracks_item), ui->subtitle_menu);
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(sub_tracks_item), ui->subtitle_menu);
|
||||||
@ -1501,6 +1639,10 @@ static void create_menubar(AppUI *ui)
|
|||||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_menu);
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_menu);
|
||||||
gtk_menu_item_set_right_justified(GTK_MENU_ITEM(help_item), FALSE);
|
gtk_menu_item_set_right_justified(GTK_MENU_ITEM(help_item), FALSE);
|
||||||
|
|
||||||
|
GtkWidget *key_item = gtk_menu_item_new_with_label("Keyboard Shortcuts");
|
||||||
|
g_signal_connect(key_item, "activate", G_CALLBACK(on_help_keybinds), ui);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), key_item);
|
||||||
|
|
||||||
GtkWidget *about_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
|
GtkWidget *about_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
|
||||||
g_signal_connect(about_item, "activate", G_CALLBACK(on_help_about), ui);
|
g_signal_connect(about_item, "activate", G_CALLBACK(on_help_about), ui);
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), about_item);
|
gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), about_item);
|
||||||
@ -1792,123 +1934,6 @@ static void on_view_screenshot(GtkMenuItem *item, gpointer data)
|
|||||||
player_screenshot(ui->player);
|
player_screenshot(ui->player);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_view_preferences(GtkMenuItem *item, gpointer data)
|
|
||||||
{
|
|
||||||
(void)item;
|
|
||||||
AppUI *ui = (AppUI *)data;
|
|
||||||
if (dialogs_show_preferences(GTK_WINDOW(ui->window), ui->prefs, ui->plugin_manager)) {
|
|
||||||
/* Apply changes immediately */
|
|
||||||
if (!ui->is_fullscreen) {
|
|
||||||
if (ui->prefs->hide_cursor_windowed) {
|
|
||||||
if (ui->auto_hide_timer_id == 0) {
|
|
||||||
ui->auto_hide_timer_id = g_timeout_add(3000, auto_hide_timer_cb, ui);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (ui->auto_hide_timer_id > 0) {
|
|
||||||
g_source_remove(ui->auto_hide_timer_id);
|
|
||||||
ui->auto_hide_timer_id = 0;
|
|
||||||
}
|
|
||||||
/* Restore cursor and controls */
|
|
||||||
GdkWindow *window = gtk_widget_get_window(ui->video_area);
|
|
||||||
if (window) gdk_window_set_cursor(window, NULL);
|
|
||||||
gtk_widget_show(controls_get_widget(ui->controls));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply Focus Rect setting (GTK2 specific hack) */
|
|
||||||
if (ui->prefs->hide_focus_rect) {
|
|
||||||
gtk_rc_parse_string("style \"no-focus-rect\" { GtkWidget::focus-line-width = 0 GtkWidget::focus-padding = 0 } widget \"*\" style \"no-focus-rect\"");
|
|
||||||
} else {
|
|
||||||
gtk_rc_parse_string("style \"default-focus-rect\" { GtkWidget::focus-line-width = 1 GtkWidget::focus-padding = 1 } widget \"*\" style \"default-focus-rect\"");
|
|
||||||
}
|
|
||||||
gtk_widget_reset_rc_styles(ui->window);
|
|
||||||
gtk_widget_queue_draw(ui->window);
|
|
||||||
|
|
||||||
/* Apply OSD setting */
|
|
||||||
player_set_option_string(ui->player, "osd-level", ui->prefs->enable_osd ? "1" : "0");
|
|
||||||
player_set_option_string(ui->player, "osd-bar", ui->prefs->enable_osd ? "yes" : "no");
|
|
||||||
|
|
||||||
/* Apply mpv.conf if changed - parse and apply each option */
|
|
||||||
if (ui->prefs->mpv_config_path && g_file_test(ui->prefs->mpv_config_path, G_FILE_TEST_EXISTS)) {
|
|
||||||
/* First, reset common properties to defaults to avoid "bleeding" from previous configs */
|
|
||||||
player_set_option_string(ui->player, "brightness", "0");
|
|
||||||
player_set_option_string(ui->player, "contrast", "0");
|
|
||||||
player_set_option_string(ui->player, "saturation", "0");
|
|
||||||
player_set_option_string(ui->player, "gamma", "0");
|
|
||||||
player_set_option_string(ui->player, "hue", "0");
|
|
||||||
|
|
||||||
player_set_option_string(ui->player, "osd-color", "#FFFFFFFF");
|
|
||||||
player_set_option_string(ui->player, "osd-border-color", "#FF000000");
|
|
||||||
player_set_option_string(ui->player, "osd-shadow-color", "#80000000");
|
|
||||||
player_set_option_string(ui->player, "osd-font-size", "55");
|
|
||||||
player_set_option_string(ui->player, "osd-border-size", "3");
|
|
||||||
player_set_option_string(ui->player, "osd-shadow-offset", "0");
|
|
||||||
|
|
||||||
FILE *fp = fopen(ui->prefs->mpv_config_path, "r");
|
|
||||||
if (fp) {
|
|
||||||
char line[512];
|
|
||||||
int applied = 0;
|
|
||||||
while (fgets(line, sizeof(line), fp)) {
|
|
||||||
/* Strip newline */
|
|
||||||
char *nl = strchr(line, '\n');
|
|
||||||
if (nl) *nl = '\0';
|
|
||||||
|
|
||||||
/* Skip comments and empty lines */
|
|
||||||
char *p = line;
|
|
||||||
while (*p == ' ' || *p == '\t') p++;
|
|
||||||
if (*p == '#' || *p == '\0') continue;
|
|
||||||
|
|
||||||
/* Find '=' separator */
|
|
||||||
char *eq = strchr(p, '=');
|
|
||||||
if (eq) {
|
|
||||||
*eq = '\0';
|
|
||||||
char *key = p;
|
|
||||||
char *value = eq + 1;
|
|
||||||
|
|
||||||
/* Trim trailing spaces from key */
|
|
||||||
char *k_end = key + strlen(key) - 1;
|
|
||||||
while (k_end > key && (*k_end == ' ' || *k_end == '\t')) *k_end-- = '\0';
|
|
||||||
|
|
||||||
/* Trim leading spaces from value */
|
|
||||||
while (*value == ' ' || *value == '\t') value++;
|
|
||||||
|
|
||||||
/* Remove surrounding quotes if present */
|
|
||||||
size_t vlen = strlen(value);
|
|
||||||
if (vlen >= 2 && ((value[0] == '\'' && value[vlen-1] == '\'') ||
|
|
||||||
(value[0] == '"' && value[vlen-1] == '"'))) {
|
|
||||||
value[vlen-1] = '\0';
|
|
||||||
value++;
|
|
||||||
}
|
|
||||||
|
|
||||||
player_set_option_string(ui->player, key, value);
|
|
||||||
applied++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
/* Show feedback with count */
|
|
||||||
char msg[64];
|
|
||||||
snprintf(msg, sizeof(msg), "Applied %d config options", applied);
|
|
||||||
const char *msg_cmd[] = {"show-text", msg, "2000", NULL};
|
|
||||||
mpv_command_async(player_get_mpv_handle(ui->player), 0, msg_cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply Screenshot Format */
|
|
||||||
if (ui->prefs->screenshot_format) {
|
|
||||||
player_set_option_string(ui->player, "screenshot-format", ui->prefs->screenshot_format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply Screenshot Dir */
|
|
||||||
if (ui->prefs->screenshot_directory) {
|
|
||||||
player_set_option_string(ui->player, "screenshot-directory", ui->prefs->screenshot_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show feedback */
|
|
||||||
const char *saved_cmd[] = {"show-text", "Preferences saved", "1500", NULL};
|
|
||||||
mpv_command_async(player_get_mpv_handle(ui->player), 0, saved_cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void on_aspect_ratio_selected(GtkMenuItem *item, gpointer data)
|
static void on_aspect_ratio_selected(GtkMenuItem *item, gpointer data)
|
||||||
{
|
{
|
||||||
@ -1917,6 +1942,28 @@ static void on_aspect_ratio_selected(GtkMenuItem *item, gpointer data)
|
|||||||
player_set_aspect_ratio(ui->player, aspect);
|
player_set_aspect_ratio(ui->player, aspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_view_preferences(GtkMenuItem *item, gpointer data)
|
||||||
|
{
|
||||||
|
(void)item;
|
||||||
|
AppUI *ui = (AppUI *)data;
|
||||||
|
dialogs_show_preferences(GTK_WINDOW(ui->window), ui->prefs, ui->plugin_manager, -1, ui_apply_preferences_internal, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_subtitle_settings_clicked(GtkMenuItem *item, gpointer data)
|
||||||
|
{
|
||||||
|
(void)item;
|
||||||
|
AppUI *ui = (AppUI *)data;
|
||||||
|
/* Open preferences directly on the Subtitles tab (index 1) */
|
||||||
|
dialogs_show_preferences(GTK_WINDOW(ui->window), ui->prefs, ui->plugin_manager, 1, ui_apply_preferences_internal, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_help_keybinds(GtkMenuItem *item, gpointer data)
|
||||||
|
{
|
||||||
|
(void)item;
|
||||||
|
AppUI *ui = (AppUI *)data;
|
||||||
|
dialogs_show_keybinds(GTK_WINDOW(ui->window));
|
||||||
|
}
|
||||||
|
|
||||||
static void on_help_about(GtkMenuItem *item, gpointer data)
|
static void on_help_about(GtkMenuItem *item, gpointer data)
|
||||||
{
|
{
|
||||||
(void)item;
|
(void)item;
|
||||||
|
|||||||
@ -1,94 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Script to create a portable bundle for GTK2 Media Player
|
|
||||||
# Gathering all shared library dependencies into a single folder.
|
|
||||||
|
|
||||||
# Ensure we are in the project root
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
APP_NAME="gtk2-mpv-player"
|
|
||||||
BUNDLE_DIR="${APP_NAME}-portable"
|
|
||||||
|
|
||||||
echo "Creating portable bundle in ${BUNDLE_DIR}..."
|
|
||||||
|
|
||||||
# 1. Clean and Build
|
|
||||||
make clean
|
|
||||||
make
|
|
||||||
|
|
||||||
if [ ! -f "$APP_NAME" ]; then
|
|
||||||
echo "Build failed. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Create directory structure
|
|
||||||
rm -rf "$BUNDLE_DIR"
|
|
||||||
mkdir -p "$BUNDLE_DIR/bin"
|
|
||||||
mkdir -p "$BUNDLE_DIR/lib"
|
|
||||||
|
|
||||||
# 3. Copy executable
|
|
||||||
cp "$APP_NAME" "$BUNDLE_DIR/bin/"
|
|
||||||
|
|
||||||
# 4. Gather dependencies
|
|
||||||
echo "Gathering dependencies (Lean Edition)..."
|
|
||||||
|
|
||||||
# Aggressively exclude everything not strictly required for basic playback.
|
|
||||||
# Pruning: Encoders (x265, rav1e, svt), Math (lapack, blas), TTS (flite),
|
|
||||||
# Niche codecs (codec2, sphinx), and heavy rendering (placebo, caca).
|
|
||||||
EXCLUDES="libc\.so|libm\.so|libdl\.so|librt\.so|libpthread\.so|libutil\.so|libnsl\.so|libresolv\.so|libgcc_s\.so|libX11\.so|libXext\.so|libXrender\.so|libXinerama\.so|libXi\.so|libXrandr\.so|libXcursor\.so|libXcomposite\.so|libXdamage\.so|libXfixes\.so|libXau\.so|libXdmcp\.so|libxcb.*|libz\.so|libGL.*|libEGL\.so|libdrm\.so|libwayland.*|libasound\.so|libpulse.*|libdbus-1\.so|libudev\.so|libsystemd\.so|libgpg-error\.so|libgcrypt\.so|liblzma\.so|libbz2\.so|libexpat\.so|libuuid\.so|libfontconfig\.so|libfreetype\.so|libcap\.so|libflite.*|liblapack.*|libgfortran.*|libcodec2.*|libsphinx.*|libpocketsphinx.*|librav1e.*|libSvtAv1Enc.*|libblas.*|libdav1d.*|libcrypto\.so|libssl\.so|libgnutls\.so|libidn2\.so|libtasn1\.so|libunistring\.so|libgmp\.so|libhogweed\.so|libnettle\.so|libp11-kit\.so|libxml2\.so|libdb-.*\.so|libarchive\.so|libzstd\.so|liblzma\.so|libbz2\.so|libpcre.*|libselinux.*|libmount\.so|libblkid\.so|libffi\.so|libreadline\.so|libtinfo\.so|libncurses.*|libplacebo.*|libcaca.*|libopenmpt.*|libvulkan.*|libOpenCL.*|libvpl.*|libmysofa.*|librsvg.*|libx265.*"
|
|
||||||
|
|
||||||
TEMP_LIBS=$(mktemp)
|
|
||||||
|
|
||||||
# Initial list from the binary and libmpv
|
|
||||||
ldd "$APP_NAME" | grep "=> /" | awk '{print $3}' >> "$TEMP_LIBS"
|
|
||||||
if [ -d "lib" ]; then
|
|
||||||
cp -nv lib/*.so* "$BUNDLE_DIR/lib/" 2>/dev/null
|
|
||||||
find lib -name "*.so*" -exec ldd {} \; | grep "=> /" | awk '{print $3}' >> "$TEMP_LIBS"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Function to filter and copy libraries
|
|
||||||
copy_libs() {
|
|
||||||
sort -u "$1" | while read -r LIB; do
|
|
||||||
if [ -f "$LIB" ]; then
|
|
||||||
LIB_BASE=$(basename "$LIB")
|
|
||||||
if ! echo "$LIB_BASE" | grep -qE "$EXCLUDES"; then
|
|
||||||
cp -nv "$LIB" "$BUNDLE_DIR/lib/" 2>/dev/null
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# First pass
|
|
||||||
copy_libs "$TEMP_LIBS"
|
|
||||||
|
|
||||||
# Second pass: check dependencies of the copied libraries
|
|
||||||
TEMP_LIBS_2=$(mktemp)
|
|
||||||
find "$BUNDLE_DIR/lib" -name "*.so*" -exec ldd {} \; | grep "=> /" | awk '{print $3}' >> "$TEMP_LIBS_2"
|
|
||||||
copy_libs "$TEMP_LIBS_2"
|
|
||||||
|
|
||||||
rm "$TEMP_LIBS" "$TEMP_LIBS_2"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 5. Strip symbols to save space
|
|
||||||
echo "Stripping symbols..."
|
|
||||||
strip --strip-unneeded "$BUNDLE_DIR/bin/$APP_NAME"
|
|
||||||
find "$BUNDLE_DIR/lib" -name "*.so*" -exec strip --strip-unneeded {} \; 2>/dev/null
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 5. Create launch script
|
|
||||||
cat > "$BUNDLE_DIR/launch.sh" <<EOF
|
|
||||||
#!/bin/bash
|
|
||||||
DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
export LD_LIBRARY_PATH="\$DIR/lib:\$LD_LIBRARY_PATH"
|
|
||||||
# Ensure GTK2 can find its modules if needed, though most are included in libs
|
|
||||||
exec "\$DIR/bin/$APP_NAME" "\$@"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$BUNDLE_DIR/launch.sh"
|
|
||||||
|
|
||||||
echo "------------------------------------------------"
|
|
||||||
echo "Portable bundle created in ${BUNDLE_DIR}"
|
|
||||||
echo "To run, use: cd ${BUNDLE_DIR} && ./launch.sh"
|
|
||||||
echo "------------------------------------------------"
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Script to create a SUPER LEAN portable bundle for GTK2 Media Player
|
|
||||||
# This version only bundles essential GTK2 libs and libmpv.
|
|
||||||
# It depends on the host system for video codecs (FFmpeg).
|
|
||||||
|
|
||||||
# Ensure we are in the project root
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
APP_NAME="gtk2-mpv-player"
|
|
||||||
BUNDLE_DIR="${APP_NAME}-lean"
|
|
||||||
|
|
||||||
echo "Creating SUPER LEAN portable bundle in ${BUNDLE_DIR}..."
|
|
||||||
|
|
||||||
# 1. Clean and Build
|
|
||||||
make clean
|
|
||||||
make
|
|
||||||
|
|
||||||
if [ ! -f "$APP_NAME" ]; then
|
|
||||||
echo "Build failed. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Create directory structure
|
|
||||||
rm -rf "$BUNDLE_DIR"
|
|
||||||
mkdir -p "$BUNDLE_DIR/bin"
|
|
||||||
mkdir -p "$BUNDLE_DIR/lib"
|
|
||||||
|
|
||||||
# 3. Copy executable
|
|
||||||
cp "$APP_NAME" "$BUNDLE_DIR/bin/"
|
|
||||||
|
|
||||||
# 4. Gather dependencies
|
|
||||||
echo "Gathering minimal dependencies..."
|
|
||||||
|
|
||||||
# In LEAN mode, we EXCLUDE all FFmpeg/Codec libraries.
|
|
||||||
# This assumes the host has them or that basic playback is enough.
|
|
||||||
EXCLUDES="libavcodec|libavfilter|libavformat|libavutil|libswresample|libswscale|libpostproc|libx264|libx265|libvpx|libopus|libvorbis|libtheora|libaom|libdav1d|librav1e|libSvtAv1Enc|libplacebo|libcrypto|libssl|libgnutls|libxml2|libfontconfig|libfreetype|libdbus|libasound|libpulse|libudev|libsystemd|libc\.so|libm\.so|libdl\.so|librt\.so|libpthread\.so|libX11|libxcb"
|
|
||||||
|
|
||||||
# Use temporary file to track libs
|
|
||||||
TEMP_LIBS=$(mktemp)
|
|
||||||
|
|
||||||
# Get direct dependencies only
|
|
||||||
ldd "$APP_NAME" | grep "=> /" | awk '{print $3}' >> "$TEMP_LIBS"
|
|
||||||
|
|
||||||
# Add libmpv (required)
|
|
||||||
if [ -f "lib/libmpv.so.2" ]; then
|
|
||||||
cp -v "lib/libmpv.so.2" "$BUNDLE_DIR/lib/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy filtered libs
|
|
||||||
sort -u "$TEMP_LIBS" | while read -r LIB; do
|
|
||||||
LIB_BASE=$(basename "$LIB")
|
|
||||||
if ! echo "$LIB_BASE" | grep -qE "$EXCLUDES"; then
|
|
||||||
if [ -f "$LIB" ]; then
|
|
||||||
cp -nv "$LIB" "$BUNDLE_DIR/lib/" 2>/dev/null
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
rm "$TEMP_LIBS"
|
|
||||||
|
|
||||||
# 5. Strip symbols to save space
|
|
||||||
echo "Stripping symbols..."
|
|
||||||
strip --strip-unneeded "$BUNDLE_DIR/bin/$APP_NAME"
|
|
||||||
find "$BUNDLE_DIR/lib" -name "*.so*" -exec strip --strip-unneeded {} \; 2>/dev/null
|
|
||||||
|
|
||||||
# 6. Create launch script
|
|
||||||
cat > "$BUNDLE_DIR/launch.sh" <<EOF
|
|
||||||
#!/bin/bash
|
|
||||||
DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
export LD_LIBRARY_PATH="\$DIR/lib:\$LD_LIBRARY_PATH"
|
|
||||||
exec "\$DIR/bin/$APP_NAME" "\$@"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$BUNDLE_DIR/launch.sh"
|
|
||||||
|
|
||||||
echo "------------------------------------------------"
|
|
||||||
echo "LEAN bundle created in ${BUNDLE_DIR}"
|
|
||||||
echo "To run, use: cd ${BUNDLE_DIR} && ./launch.sh"
|
|
||||||
echo "Note: This version requires system codecs to be present."
|
|
||||||
echo "------------------------------------------------"
|
|
||||||
Loading…
Reference in New Issue
Block a user