diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 778c3efe..1ca5b761 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -267,9 +267,17 @@ jobs: $FFMPEG_ROOT_PATH/bin/ffmpeg -version | head -n 1 $FFMPEG_ROOT_PATH/bin/ffmpeg -encoders 2>/dev/null | grep -E "libx264|libx265" || echo "Warning: x264/x265 not found" - - name: Checkout BMF repository(specific branch) + - name: Checkout BMF repository and prepare python support run: | git clone https://github.com/OpenConverterLab/bmf.git + sed -i '' '77 i\ + -DPython_ROOT_DIR="$HOME/Library/Application Support/OpenConverter/Python.framework" \\\ + -DPython_FIND_FRAMEWORK=ONLY \\\ + ' ./bmf/build_osx.sh + + mkdir -p "$HOME/Library/Application Support/OpenConverter/Python.framework" + wget https://github.com/astral-sh/python-build-standalone/releases/download/20251031/cpython-3.9.25+20251031-aarch64-apple-darwin-install_only.tar.gz + tar xzvf cpython-3.9.25+20251031-aarch64-apple-darwin-install_only.tar.gz --strip-components=1 -C "$HOME/Library/Application Support/OpenConverter/Python.framework" # wget https://invisible-island.net/archives/ncurses/ncurses-6.5.tar.gz # wget https://ftp.gnu.org/gnu/binutils/binutils-2.43.1.tar.bz2 diff --git a/src/builder/include/open_converter.h b/src/builder/include/open_converter.h index 7b396d10..0177a928 100644 --- a/src/builder/include/open_converter.h +++ b/src/builder/include/open_converter.h @@ -89,7 +89,6 @@ class OpenConverter : public QMainWindow, public ProcessObserver { private slots: void SlotLanguageChanged(QAction *action); void SlotTranscoderChanged(QAction *action); - void SlotPythonChanged(QAction *action); void OnNavigationButtonClicked(int pageIndex); void OnQueueButtonClicked(); @@ -109,8 +108,6 @@ private slots: QMessageBox *displayResult; QActionGroup *transcoderGroup; QActionGroup *languageGroup; - QActionGroup *pythonGroup; - QString customPythonPath; // Navigation and page management QButtonGroup *navButtonGroup; @@ -139,12 +136,6 @@ private slots: // Get current transcoder name QString GetCurrentTranscoderName() const; - - // Get current Python path setting - QString GetPythonSitePackagesPath() const; - - // Static method for transcoder_bmf to get Python path - static QString GetStoredPythonPath(); }; #endif // OPEN_CONVERTER_H diff --git a/src/builder/src/open_converter.cpp b/src/builder/src/open_converter.cpp index 8485c577..f993c0d6 100644 --- a/src/builder/src/open_converter.cpp +++ b/src/builder/src/open_converter.cpp @@ -133,27 +133,6 @@ OpenConverter::OpenConverter(QWidget *parent) languageGroup->addAction(action); } - // Setup Python menu - pythonGroup = new QActionGroup(this); - pythonGroup->setExclusive(true); - QList pythonActions = ui->menuPython->actions(); - for (QAction* action : pythonActions) { - action->setCheckable(true); - pythonGroup->addAction(action); - } - - // Load saved Python setting or default to App Python - QSettings settings("OpenConverter", "OpenConverter"); - QString savedPython = settings.value("python/mode", "pythonAppSupport").toString(); - customPythonPath = settings.value("python/customPath", "").toString(); - - for (QAction* action : pythonActions) { - if (action->objectName() == savedPython) { - action->setChecked(true); - break; - } - } - // Initialize language - default to English (no translation file needed) m_currLang = "english"; m_langPath = ":/"; @@ -190,9 +169,6 @@ OpenConverter::OpenConverter(QWidget *parent) connect(ui->menuTranscoder, SIGNAL(triggered(QAction *)), this, SLOT(SlotTranscoderChanged(QAction *))); - - connect(ui->menuPython, SIGNAL(triggered(QAction *)), this, - SLOT(SlotPythonChanged(QAction *))); } void OpenConverter::dragEnterEvent(QDragEnterEvent *event) { @@ -255,48 +231,6 @@ void OpenConverter::SlotTranscoderChanged(QAction *action) { } } -// Called every time, when a menu entry of the Python menu is called -void OpenConverter::SlotPythonChanged(QAction *action) { - if (!action) return; - - QString pythonMode = action->objectName(); - QSettings settings("OpenConverter", "OpenConverter"); - - if (pythonMode == "pythonCustom") { - // Show file dialog to select site-packages path - QString dir = QFileDialog::getExistingDirectory( - this, - tr("Select Python site-packages Directory"), - customPythonPath.isEmpty() ? "/opt/homebrew/lib/python3.9/site-packages" : customPythonPath, - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks - ); - - if (!dir.isEmpty()) { - customPythonPath = dir; - settings.setValue("python/mode", pythonMode); - settings.setValue("python/customPath", customPythonPath); - ui->statusBar->showMessage( - tr("Python path set to: %1").arg(customPythonPath)); - } else { - // User cancelled, revert to previous selection - QString savedPython = settings.value("python/mode", "pythonAppSupport").toString(); - QList pythonActions = ui->menuPython->actions(); - for (QAction* act : pythonActions) { - if (act->objectName() == savedPython) { - act->setChecked(true); - break; - } - } - return; - } - } else { - settings.setValue("python/mode", pythonMode); - if (pythonMode == "pythonAppSupport") { - ui->statusBar->showMessage(tr("Using App Python")); - } - } -} - // Called every time, when a menu entry of the language menu is called void OpenConverter::SlotLanguageChanged(QAction *action) { if (0 != action) { @@ -567,35 +501,4 @@ QString OpenConverter::GetCurrentTranscoderName() const { return "FFMPEG"; } -QString OpenConverter::GetPythonSitePackagesPath() const { - QAction *checkedAction = pythonGroup->checkedAction(); - if (checkedAction) { - QString mode = checkedAction->objectName(); - if (mode == "pythonCustom" && !customPythonPath.isEmpty()) { - return customPythonPath; - } else if (mode == "pythonAppSupport") { - // Python installed in ~/Library/Application Support/OpenConverter/ - QString appSupportPath = QDir::homePath() + - "/Library/Application Support/OpenConverter/Python.framework/lib/python3.9/site-packages"; - return appSupportPath; - } - } - // Default: Bundled or empty (transcoder will use bundled) - return QString(); -} - -QString OpenConverter::GetStoredPythonPath() { - // Static method that can be called from transcoder_bmf without GUI instance - QSettings settings("OpenConverter", "OpenConverter"); - QString pythonMode = settings.value("python/mode", "pythonAppSupport").toString(); - - if (pythonMode == "pythonCustom") { - return settings.value("python/customPath", "").toString(); - } - // Default to App Python (~/Library/Application Support/OpenConverter/) - QString appSupportPath = QDir::homePath() + - "/Library/Application Support/OpenConverter/Python.framework/lib/python3.9/site-packages"; - return appSupportPath; -} - #include "open_converter.moc" diff --git a/src/builder/src/open_converter.ui b/src/builder/src/open_converter.ui index 62c10ffd..1756e9af 100644 --- a/src/builder/src/open_converter.ui +++ b/src/builder/src/open_converter.ui @@ -146,16 +146,8 @@ QLabel { Transcoder - - - Python - - - - - @@ -174,23 +166,6 @@ QLabel { Chinese - - - - pythonAppSupport - - - App Python - - - - - pythonCustom - - - Custom Path... - - diff --git a/src/builder/src/python_manager.cpp b/src/builder/src/python_manager.cpp index 734e715e..8ebbc332 100644 --- a/src/builder/src/python_manager.cpp +++ b/src/builder/src/python_manager.cpp @@ -117,12 +117,14 @@ QString PythonManager::GetAppBundlePath() { QString PythonManager::GetPythonFrameworkPath() { // Install embedded Python into Application Support directory so - // the app bundle/installation remains immutable. Use QStandardPaths to get - // the per-user Application Support directory for the app. + // the app bundle/installation remains immutable. + // Use explicit app name "OpenConverter" to avoid issues with wrapper launchers + // (e.g., OpenConverter.real on macOS) affecting the path. // macOS: ~/Library/Application Support/OpenConverter/Python.framework // Linux: ~/.local/share/OpenConverter/Python.framework // Windows: C:/Users//AppData/Local/OpenConverter/Python.framework - QString appSupportBase = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + QString genericDataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + QString appSupportBase = genericDataLocation + "/OpenConverter"; // Ensure directory exists QDir().mkpath(appSupportBase); return appSupportBase + "/Python.framework"; diff --git a/src/resources/lang_chinese.qm b/src/resources/lang_chinese.qm index 74f6bee9..b3fe4737 100644 Binary files a/src/resources/lang_chinese.qm and b/src/resources/lang_chinese.qm differ diff --git a/src/resources/lang_chinese.ts b/src/resources/lang_chinese.ts index caeda3ce..fc87fc68 100644 --- a/src/resources/lang_chinese.ts +++ b/src/resources/lang_chinese.ts @@ -1230,30 +1230,6 @@ AI Processing AI 处理 - - Select Python site-packages Directory - 选择 Python site-packages 目录 - - - Python path set to: %1 - Python 路径设置为:%1 - - - Using App Python - 使用应用内置 Python - - - App Python - 应用内置 Python - - - Custom Path... - 自定义路径... - - - Python - Python - QObject diff --git a/src/resources/macos_launcher.sh b/src/resources/macos_launcher.sh new file mode 100644 index 00000000..d22a8c39 --- /dev/null +++ b/src/resources/macos_launcher.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Wrapper launcher for OpenConverter +# Sets up library paths before launching the real executable + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +# Set DYLD_LIBRARY_PATH for Python framework +export DYLD_LIBRARY_PATH="$HOME/Library/Application Support/OpenConverter/Python.framework/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" + +# Execute the real binary +exec "$SCRIPT_DIR/OpenConverter.real" "$@" + diff --git a/src/transcoder/src/transcoder_bmf.cpp b/src/transcoder/src/transcoder_bmf.cpp index de0e01d6..4bcb1bae 100644 --- a/src/transcoder/src/transcoder_bmf.cpp +++ b/src/transcoder/src/transcoder_bmf.cpp @@ -17,13 +17,6 @@ #include #include -#ifdef ENABLE_GUI -#include -#include -#include -#include -#endif - #ifdef __APPLE__ #include #include @@ -37,6 +30,16 @@ TranscoderBMF::TranscoderBMF(ProcessParameter *process_parameter, } bool TranscoderBMF::setup_python_environment() { +#ifdef __APPLE__ + // Set PYTHONHOME for macOS (both debug and release use App Python from Application Support) + const char* home_env = std::getenv("HOME"); + if (home_env) { + std::string python_home = std::string(home_env) + "/Library/Application Support/OpenConverter/Python.framework"; + setenv("PYTHONHOME", python_home.c_str(), 1); + BMFLOG(BMF_INFO) << "Set PYTHONHOME: " << python_home; + } +#endif + // In Debug mode, use system PYTHONPATH from environment (set by developer/CMake) // In Release mode, set up PYTHONPATH for bundled BMF and external Python (App Support or Custom) #ifndef NDEBUG @@ -102,110 +105,14 @@ bool TranscoderBMF::setup_python_environment() { #endif #endif - // Release mode: Set up PYTHONPATH for bundled BMF libraries and external Python + // Release mode: Set up PYTHONPATH for bundled BMF libraries and App Python std::string bmf_lib_path; std::string bmf_output_path; std::string bmf_config_path; std::string python_site_packages; - -#ifdef ENABLE_GUI - // Get Python path from QSettings (default to App Python in Application Support) - QSettings settings("OpenConverter", "OpenConverter"); - QString pythonMode = settings.value("python/mode", "pythonAppSupport").toString(); std::string python_bin_path; std::string python_lib_path; - if (pythonMode == "pythonCustom") { - QString customPath = settings.value("python/customPath", "").toString(); - if (!customPath.isEmpty()) { - python_site_packages = customPath.toStdString(); - BMFLOG(BMF_INFO) << "Using custom Python site-packages: " << python_site_packages; - - // Derive bin and lib paths from custom site-packages path - // site-packages is typically at: /path/to/python/lib/python3.x/site-packages - // We need: bin at /path/to/python/bin, lib at /path/to/python/lib - std::filesystem::path site_pkg_path(python_site_packages); - // Go up: site-packages -> python3.x -> lib -> python_root - std::filesystem::path python_root = site_pkg_path.parent_path().parent_path().parent_path(); - python_bin_path = (python_root / "bin").string(); - python_lib_path = (python_root / "lib").string(); - BMFLOG(BMF_INFO) << "Custom Python bin path: " << python_bin_path; - BMFLOG(BMF_INFO) << "Custom Python lib path: " << python_lib_path; - } - } - - // Default to App Python if not custom - if (python_site_packages.empty()) { - // Use QStandardPaths for cross-platform app data location - // macOS: ~/Library/Application Support/OpenConverter - // Linux: ~/.local/share/OpenConverter - // Windows: C:/Users//AppData/Local/OpenConverter - QString appSupportDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QString pythonFramework = appSupportDir + "/Python.framework"; - QString sitePackages = pythonFramework + "/lib/python3.9/site-packages"; - - // Check if App Python exists - NO FALLBACK, App Python is required - if (QDir(sitePackages).exists()) { - python_site_packages = sitePackages.toStdString(); - python_bin_path = (pythonFramework + "/bin").toStdString(); - python_lib_path = (pythonFramework + "/lib").toStdString(); - BMFLOG(BMF_INFO) << "Using App Python site-packages: " << python_site_packages; - BMFLOG(BMF_INFO) << "App Python bin path: " << python_bin_path; - BMFLOG(BMF_INFO) << "App Python lib path: " << python_lib_path; - } else { - BMFLOG(BMF_ERROR) << "App Python not found at: " << sitePackages.toStdString(); - BMFLOG(BMF_ERROR) << "Please install App Python via Python menu -> Install Python"; - return false; // Don't continue without App Python - no fallback to system Python - } - } - - // Set PATH and LD_LIBRARY_PATH/DYLD_LIBRARY_PATH for Python - if (!python_bin_path.empty()) { - // Prepend Python bin to PATH - std::string current_path; - const char* existing_path = std::getenv("PATH"); - if (existing_path) { - current_path = existing_path; - } - std::string new_path = python_bin_path; - if (!current_path.empty()) { - new_path += ":" + current_path; - } - setenv("PATH", new_path.c_str(), 1); - BMFLOG(BMF_INFO) << "Set PATH: " << new_path; - } - - if (!python_lib_path.empty()) { -#ifdef __APPLE__ - // On macOS, set DYLD_LIBRARY_PATH - std::string current_dyld; - const char* existing_dyld = std::getenv("DYLD_LIBRARY_PATH"); - if (existing_dyld) { - current_dyld = existing_dyld; - } - std::string new_dyld = python_lib_path; - if (!current_dyld.empty()) { - new_dyld += ":" + current_dyld; - } - setenv("DYLD_LIBRARY_PATH", new_dyld.c_str(), 1); - BMFLOG(BMF_INFO) << "Set DYLD_LIBRARY_PATH: " << new_dyld; -#else - // On Linux, set LD_LIBRARY_PATH - std::string current_ld; - const char* existing_ld = std::getenv("LD_LIBRARY_PATH"); - if (existing_ld) { - current_ld = existing_ld; - } - std::string new_ld = python_lib_path; - if (!current_ld.empty()) { - new_ld += ":" + current_ld; - } - setenv("LD_LIBRARY_PATH", new_ld.c_str(), 1); - BMFLOG(BMF_INFO) << "Set LD_LIBRARY_PATH: " << new_ld; -#endif - } -#endif - #ifdef __APPLE__ // Check if running from app bundle char exe_path[1024]; diff --git a/tool/fix_macos_libs.sh b/tool/fix_macos_libs.sh index 17409447..8f8589c6 100755 --- a/tool/fix_macos_libs.sh +++ b/tool/fix_macos_libs.sh @@ -110,12 +110,6 @@ if [ -n "$BMF_ROOT_PATH" ] && [ -d "$BMF_ROOT_PATH" ]; then echo " Copied: BUILTIN_CONFIG.json" fi - # Bundle BMF Python package to Resources/bmf/ (named 'bmf' for Python import) - echo " Copying BMF Python package to Resources/bmf/..." - rm -rf "$APP_DIR/Contents/Resources/bmf" - cp -R "$BMF_ROOT_PATH" "$APP_DIR/Contents/Resources/bmf" 2>/dev/null || true - echo " Copied BMF Python package as 'bmf'" - echo -e "${GREEN}BMF libraries bundled successfully${NC}" fi @@ -259,9 +253,86 @@ codesign --force --deep --sign - "$APP_DIR" 2>&1 || { } echo "" -echo -e "${YELLOW}Step 6: Verifying library paths...${NC}" +echo -e "${YELLOW}Step 6: Copying BMF Python package to Resources...${NC}" +if [ -n "$BMF_ROOT_PATH" ] && [ -d "$BMF_ROOT_PATH" ]; then + # Bundle BMF Python package to Resources/bmf/ (named 'bmf' for Python import) + echo " Copying BMF Python package to Resources/bmf/..." + rm -rf "$APP_DIR/Contents/Resources/bmf" + cp -R "$BMF_ROOT_PATH" "$APP_DIR/Contents/Resources/bmf" 2>/dev/null || true + echo " Copied BMF Python package as 'bmf'" + + # Override libs in Resources/bmf/lib with already-fixed libs from Frameworks + echo " Overriding libraries with fixed versions from Frameworks/..." + BMF_RESOURCES_LIB="$APP_DIR/Contents/Resources/bmf/lib" + if [ -d "$BMF_RESOURCES_LIB" ]; then + for lib_path in "$BMF_RESOURCES_LIB"/*.dylib "$BMF_RESOURCES_LIB"/*.so; do + [ -f "$lib_path" ] || continue + lib_name=$(basename "$lib_path") + + # Check if this library exists in Frameworks (already fixed) + if [ -f "$APP_FRAMEWORKS/$lib_name" ]; then + echo " Overriding: $lib_name" + cp "$APP_FRAMEWORKS/$lib_name" "$lib_path" + fi + done + cp "$APP_FRAMEWORKS/lib/*" $BMF_RESOURCES_LIB/ 2>/dev/null || true + fi + + echo -e "${GREEN}BMF Python package copied successfully${NC}" +else + echo -e "${YELLOW}BMF_ROOT_PATH not set, skipping BMF Python package${NC}" +fi + +echo "" +echo -e "${YELLOW}Step 7: Creating wrapper launcher...${NC}" +APP_MACOS_DIR="$APP_DIR/Contents/MacOS" +REAL_EXECUTABLE="$APP_MACOS_DIR/OpenConverter.real" + +# Find the launcher template script +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +LAUNCHER_TEMPLATE="" +for path in "$SCRIPT_DIR/../src/resources/macos_launcher.sh" \ + "$SCRIPT_DIR/src/resources/macos_launcher.sh" \ + "../src/resources/macos_launcher.sh" \ + "src/resources/macos_launcher.sh"; do + if [ -f "$path" ]; then + LAUNCHER_TEMPLATE="$path" + break + fi +done + +# Rename the original executable and install wrapper +if [ -f "$APP_EXECUTABLE" ] && [ ! -f "$REAL_EXECUTABLE" ]; then + echo " Renaming OpenConverter -> OpenConverter.real" + mv "$APP_EXECUTABLE" "$REAL_EXECUTABLE" + + # Copy wrapper script from template + if [ -n "$LAUNCHER_TEMPLATE" ] && [ -f "$LAUNCHER_TEMPLATE" ]; then + echo " Copying wrapper launcher from: $LAUNCHER_TEMPLATE" + cp "$LAUNCHER_TEMPLATE" "$APP_EXECUTABLE" + else + echo " Creating wrapper launcher script..." + cat > "$APP_EXECUTABLE" << 'EOF' +#!/bin/sh +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +export DYLD_LIBRARY_PATH="$HOME/Library/Application Support/OpenConverter/Python.framework/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}" +exec "$SCRIPT_DIR/OpenConverter.real" "$@" +EOF + fi + chmod +x "$APP_EXECUTABLE" + echo -e "${GREEN}Wrapper launcher created successfully${NC}" +else + if [ -f "$REAL_EXECUTABLE" ]; then + echo -e "${YELLOW}Wrapper already exists (OpenConverter.real found)${NC}" + else + echo -e "${RED}Error: Executable not found at $APP_EXECUTABLE${NC}" + fi +fi + +echo "" +echo -e "${YELLOW}Step 8: Verifying library paths...${NC}" echo -e "${GREEN}Main executable dependencies:${NC}" -otool -L "$APP_EXECUTABLE" | grep -E "libav|libsw|libx264|libx265|libvpx|libopus" || echo " (No FFmpeg/codec libraries directly linked)" +otool -L "$REAL_EXECUTABLE" | grep -E "libav|libsw|libx264|libx265|libvpx|libopus" || echo " (No FFmpeg/codec libraries directly linked)" echo "" echo -e "${GREEN}Sample FFmpeg library dependencies (libavcodec):${NC}" @@ -280,5 +351,5 @@ echo " macdeployqt OpenConverter.app -dmg" echo " # DMG will be created as OpenConverter.dmg" echo "" echo -e "${YELLOW}To verify all dependencies are bundled:${NC}" -echo " otool -L OpenConverter.app/Contents/MacOS/OpenConverter" +echo " otool -L OpenConverter.app/Contents/MacOS/OpenConverter.real" echo " # All paths should start with @executable_path or @rpath"