├── data └── locale │ └── en-US.ini ├── .github ├── scripts │ ├── build-linux │ ├── build-macos │ ├── package-linux │ ├── package-macos │ ├── utils.zsh │ │ ├── mkcd │ │ ├── log_error │ │ ├── log_warning │ │ ├── log_debug │ │ ├── log_output │ │ ├── log_status │ │ ├── log_info │ │ ├── read_codesign_user │ │ ├── read_codesign_team │ │ ├── read_codesign_installer │ │ ├── read_codesign │ │ ├── log_group │ │ ├── set_loglevel │ │ ├── check_macos │ │ ├── setup_ccache │ │ ├── read_codesign_pass │ │ ├── setup_linux │ │ ├── check_linux │ │ └── check_packages │ ├── .Brewfile │ ├── .Wingetfile │ ├── .Aptfile │ ├── utils.pwsh │ │ ├── Ensure-Location.ps1 │ │ ├── Invoke-External.ps1 │ │ ├── Install-BuildDependencies.ps1 │ │ ├── Expand-ArchiveExt.ps1 │ │ └── Logger.ps1 │ ├── Build-Windows.ps1 │ └── Package-Windows.ps1 ├── workflows │ ├── dispatch.yaml │ ├── check-format.yaml │ ├── pr-pull.yaml │ └── push.yaml └── actions │ ├── run-cmake-format │ └── action.yaml │ ├── run-clang-format │ └── action.yaml │ ├── build-plugin │ └── action.yaml │ ├── package-plugin │ └── action.yaml │ └── setup-macos-codesigning │ └── action.yaml ├── src ├── ui │ ├── registerDock.h │ ├── CMakeLists.txt │ ├── settingsdialog.h │ ├── translatedockwidget.h │ ├── registerDock.cpp │ ├── translatedockwidget.ui │ ├── translatedockwidget.cpp │ ├── settingsdialog.cpp │ └── settingsdialog.ui ├── translation-service │ ├── httpserver.h │ ├── translation.h │ ├── httpserver.cpp │ └── translation.cpp ├── model-utils │ ├── model-downloader.h │ ├── model-downloader-types.h │ ├── model-downloader-ui.h │ └── model-downloader.cpp ├── plugin-support.h ├── plugin-support.c.in ├── plugin-main.c └── utils │ ├── config-data.h │ └── config-data.cpp ├── cmake ├── windows │ ├── defaults.cmake │ ├── buildspec.cmake │ ├── resources │ │ ├── resource.rc.in │ │ └── installer-Windows.iss.in │ ├── compilerconfig.cmake │ └── helpers.cmake ├── macos │ ├── resources │ │ ├── ccache-launcher-c.in │ │ ├── ccache-launcher-cxx.in │ │ ├── distribution.in │ │ └── create-package.cmake.in │ ├── buildspec.cmake │ ├── defaults.cmake │ ├── compilerconfig.cmake │ ├── helpers.cmake │ └── xcode.cmake ├── BuildCppHTTPLib.cmake ├── common │ ├── ccache.cmake │ ├── buildnumber.cmake │ ├── osconfig.cmake │ ├── compiler_common.cmake │ ├── bootstrap.cmake │ ├── helpers_common.cmake │ └── buildspec_common.cmake ├── linux │ ├── toolchains │ │ ├── x86_64-linux-gcc.cmake │ │ ├── aarch64-linux-gcc.cmake │ │ ├── x86_64-linux-clang.cmake │ │ └── aarch64-linux-clang.cmake │ ├── helpers.cmake │ ├── defaults.cmake │ └── compilerconfig.cmake ├── BuildSentencepiece.cmake ├── BuildMyCurl.cmake └── BuildCTranslate2.cmake ├── patch_libobs.diff ├── .gitignore ├── .cmake-format.json ├── CMakeLists.txt ├── .clang-format ├── CMakePresets.json └── README.md /data/locale/en-US.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/scripts/build-linux: -------------------------------------------------------------------------------- 1 | .build.zsh -------------------------------------------------------------------------------- /.github/scripts/build-macos: -------------------------------------------------------------------------------- 1 | .build.zsh -------------------------------------------------------------------------------- /.github/scripts/package-linux: -------------------------------------------------------------------------------- 1 | .package.zsh -------------------------------------------------------------------------------- /.github/scripts/package-macos: -------------------------------------------------------------------------------- 1 | .package.zsh -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/mkcd: -------------------------------------------------------------------------------- 1 | [[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1} 2 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /.github/scripts/.Brewfile: -------------------------------------------------------------------------------- 1 | brew "ccache" 2 | brew "coreutils" 3 | brew "cmake" 4 | brew "git" 5 | brew "jq" 6 | brew "xcbeautify" 7 | -------------------------------------------------------------------------------- /.github/scripts/.Wingetfile: -------------------------------------------------------------------------------- 1 | package 'cmake', path: 'Cmake\bin', bin: 'cmake' 2 | package 'innosetup', path: 'Inno Setup 6', bin: 'iscc' 3 | -------------------------------------------------------------------------------- /.github/scripts/.Aptfile: -------------------------------------------------------------------------------- 1 | package 'cmake' 2 | package 'ccache' 3 | package 'git' 4 | package 'jq' 5 | package 'ninja-build', bin: 'ninja' 6 | package 'pkg-config' 7 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_warning: -------------------------------------------------------------------------------- 1 | if (( _loglevel > 0 )) { 2 | local icon=' =>' 3 | 4 | print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" 5 | } 6 | -------------------------------------------------------------------------------- /src/ui/registerDock.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | void registerDock(); 8 | 9 | #ifdef __cplusplus 10 | } 11 | #endif 12 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_debug: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f" 4 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_output: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon='' 5 | 6 | print -PR " ${(r:5:)icon} ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_status: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' >' 5 | 6 | print -PR "%F{2} ${(r:5:)icon}%f ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_info: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" 7 | } 8 | -------------------------------------------------------------------------------- /cmake/windows/defaults.cmake: -------------------------------------------------------------------------------- 1 | # CMake Windows defaults module 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Enable find_package targets to become globally available targets 6 | set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE) 7 | 8 | include(buildspec) 9 | -------------------------------------------------------------------------------- /src/ui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources( 2 | ${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/registerDock.cpp ${CMAKE_CURRENT_LIST_DIR}/settingsdialog.cpp 3 | ${CMAKE_CURRENT_LIST_DIR}/translatedockwidget.cpp) 4 | -------------------------------------------------------------------------------- /src/translation-service/httpserver.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPSERVER_H 2 | #define HTTPSERVER_H 3 | 4 | // start the http server 5 | void start_http_server(); 6 | 7 | // stop the http server 8 | void stop_http_server(); 9 | 10 | #endif // HTTPSERVER_H 11 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_user: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_USER} )) { 4 | typeset -g CODESIGN_IDENT_USER 5 | log_info 'Setting up Apple ID for notarization...' 6 | read CODESIGN_IDENT_USER'?Apple ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_team: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_TEAM} )) { 4 | typeset -g CODESIGN_TEAM 5 | log_info 'Setting up Apple Developer Team ID for codesigning...' 6 | read CODESIGN_TEAM'?Apple Developer Team ID (leave empty to use Apple Developer ID instead): ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_installer: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_INSTALLER} )) { 4 | typeset -g CODESIGN_IDENT_INSTALLER 5 | log_info 'Setting up Apple Developer Installer ID for installer package codesigning...' 6 | read CODESIGN_IDENT_INSTALLER'?Apple Developer Installer ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT} )) { 4 | typeset -g CODESIGN_IDENT 5 | log_info 'Setting up Apple Developer ID for application codesigning...' 6 | read CODESIGN_IDENT'?Apple Developer Application ID: ' 7 | } 8 | 9 | typeset -g CODESIGN_TEAM=$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p') 10 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_group: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+_log_group} )) typeset -g _log_group=0 4 | 5 | if (( ${+CI} )) { 6 | if (( _log_group )) { 7 | print "::endgroup::" 8 | typeset -g _log_group=0 9 | } 10 | if (( # )) { 11 | print "::group::${@}" 12 | typeset -g _log_group=1 13 | } 14 | } else { 15 | if (( # )) log_info ${@} 16 | } 17 | -------------------------------------------------------------------------------- /patch_libobs.diff: -------------------------------------------------------------------------------- 1 | diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt 2 | index d2e2671..d7797c6 100644 3 | --- a/libobs/CMakeLists.txt 4 | +++ b/libobs/CMakeLists.txt 5 | @@ -287,6 +287,7 @@ set(public_headers 6 | util/base.h 7 | util/bmem.h 8 | util/c99defs.h 9 | + util/config-file.h 10 | util/darray.h 11 | util/profiler.h 12 | util/sse-intrin.h 13 | -------------------------------------------------------------------------------- /src/ui/settingsdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGSDIALOG_H 2 | #define SETTINGSDIALOG_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class SettingsDialog; 8 | } 9 | 10 | class SettingsDialog : public QDialog { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit SettingsDialog(QWidget *parent = nullptr); 15 | ~SettingsDialog(); 16 | 17 | private: 18 | Ui::SettingsDialog *ui; 19 | }; 20 | 21 | #endif // SETTINGSDIALOG_H 22 | -------------------------------------------------------------------------------- /.github/workflows/dispatch.yaml: -------------------------------------------------------------------------------- 1 | name: Dispatch 2 | run-name: Dispatched Repository Actions - ${{ inputs.job }} ⌛️ 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | job: 7 | description: Dispatch job to run 8 | required: true 9 | type: choice 10 | options: 11 | - build 12 | permissions: 13 | contents: write 14 | jobs: 15 | check-and-build: 16 | if: inputs.job == 'build' 17 | uses: ./.github/workflows/build-project.yaml 18 | secrets: inherit 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude everything 2 | /* 3 | 4 | # Except for default project files 5 | !/.github 6 | !/build-aux 7 | !/cmake 8 | !/data 9 | !/src 10 | !.clang-format 11 | !.cmake-format.json 12 | !.gitignore 13 | !buildspec.json 14 | !CMakeLists.txt 15 | !CMakePresets.json 16 | !LICENSE 17 | !README.md 18 | !patch_libobs.diff 19 | 20 | # Exclude lock files 21 | *.lock.json 22 | 23 | # Exclude macOS legacy resource forks 24 | .DS_Store 25 | 26 | # Exclude CMake build number cache 27 | /cmake/.CMakeBuildNumber 28 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/set_loglevel: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_error 2 | 3 | local -r _usage="Usage: %B${0}%b 4 | 5 | Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" 6 | 7 | if (( ! # )); then 8 | log_error 'Called without arguments.' 9 | log_output ${_usage} 10 | return 2 11 | elif (( ${1} >= 4 )); then 12 | log_error 'Called with loglevel > 3.' 13 | log_output ${_usage} 14 | fi 15 | 16 | typeset -g -i -r _loglevel=${1} 17 | log_debug "Log level set to '${1}'" 18 | -------------------------------------------------------------------------------- /src/model-utils/model-downloader.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_DOWNLOADER_H 2 | #define MODEL_DOWNLOADER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "model-downloader-types.h" 8 | 9 | std::string find_model_file(const std::string &model_name); 10 | 11 | // Start the model downloader UI dialog with a callback for when the download is finished 12 | void download_model_with_ui_dialog(const ModelInfo &model_info, 13 | download_finished_callback_t download_finished_callback); 14 | 15 | #endif // MODEL_DOWNLOADER_H 16 | -------------------------------------------------------------------------------- /cmake/macos/resources/ccache-launcher-c.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [[ "$1" == "${CMAKE_C_COMPILER}" ]] ; then 4 | shift 5 | fi 6 | 7 | export CCACHE_CPP2=true 8 | export CCACHE_DEPEND=true 9 | export CCACHE_DIRECT=true 10 | export CCACHE_FILECLONE=true 11 | export CCACHE_INODECACHE=true 12 | export CCACHE_NOCOMPILERCHECK='content' 13 | export CCACHE_SLOPPINESS='include_file_mtime,include_file_ctime,clang_index_store,system_headers' 14 | if [[ "${CI}" ]]; then 15 | export CCACHE_NOHASHDIR=true 16 | fi 17 | exec "${CMAKE_C_COMPILER_LAUNCHER}" "${CMAKE_C_COMPILER}" "$@" 18 | -------------------------------------------------------------------------------- /cmake/macos/resources/ccache-launcher-cxx.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [[ "$1" == "${CMAKE_CXX_COMPILER}" ]] ; then 4 | shift 5 | fi 6 | 7 | export CCACHE_CPP2=true 8 | export CCACHE_NODEPEND=true 9 | export CCACHE_DIRECT=true 10 | export CCACHE_FILECLONE=true 11 | export CCACHE_INODECACHE=true 12 | export CCACHE_NOCOMPILERCHECK='content' 13 | export CCACHE_SLOPPINESS='include_file_mtime,include_file_ctime,clang_index_store,system_headers' 14 | if [[ "${CI}" ]]; then 15 | export CCACHE_NOHASHDIR=true 16 | fi 17 | exec "${CMAKE_CXX_COMPILER_LAUNCHER}" "${CMAKE_CXX_COMPILER}" "$@" 18 | -------------------------------------------------------------------------------- /cmake/BuildCppHTTPLib.cmake: -------------------------------------------------------------------------------- 1 | set(CppHTTPLib_URL "https://github.com/yhirose/cpp-httplib/archive/refs/tags/v0.14.1.tar.gz") 2 | set(CppHTTPLib_HASH SHA256=2D4FB5544DA643E5D0A82585555D8B7502B4137EB321A4ABBB075E21D2F00E96) 3 | 4 | # Download and unpack cpp-httplib at configure time 5 | include(FetchContent) 6 | FetchContent_Declare( 7 | cpphttplib 8 | URL ${CppHTTPLib_URL} 9 | URL_HASH ${CppHTTPLib_HASH}) 10 | FetchContent_MakeAvailable(cpphttplib) 11 | 12 | add_library(cpphttplib INTERFACE EXCLUDE_FROM_ALL) 13 | target_include_directories(cpphttplib INTERFACE ${cpphttplib_SOURCE_DIR}) 14 | -------------------------------------------------------------------------------- /src/translation-service/translation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | int build_translation_context(); 7 | #ifdef __cplusplus 8 | } 9 | #endif 10 | 11 | #ifdef __cplusplus 12 | #include 13 | int translate(const std::string &text, const std::string &source_lang, 14 | const std::string &target_lang, std::string &result); 15 | int translate_from_json(const std::string &body, std::string &result); 16 | #endif 17 | 18 | #define OBS_POLYGLOT_TRANSLATION_INIT_FAIL -1 19 | #define OBS_POLYGLOT_TRANSLATION_INIT_SUCCESS 0 20 | #define OBS_POLYGLOT_TRANSLATION_SUCCESS 0 21 | #define OBS_POLYGLOT_TRANSLATION_FAIL -1 22 | -------------------------------------------------------------------------------- /src/model-utils/model-downloader-types.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_DOWNLOADER_TYPES_H 2 | #define MODEL_DOWNLOADER_TYPES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Information about a model 9 | struct ModelInfo { 10 | std::string name; 11 | std::vector urls; 12 | std::string localPath; 13 | std::string spmUrl; 14 | std::string localSpmPath; 15 | }; 16 | 17 | // Callback for when the download is finished 18 | typedef std::function 19 | download_finished_callback_t; 20 | 21 | extern std::map models_info; 22 | 23 | #endif // MODEL_DOWNLOADER_TYPES_H 24 | -------------------------------------------------------------------------------- /src/ui/translatedockwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSLATEDOCKWIDGET_H 2 | #define TRANSLATEDOCKWIDGET_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class TranslateDockWidget; 8 | } 9 | 10 | class TranslateDockWidget : public QDockWidget { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit TranslateDockWidget(QWidget *parent = nullptr); 15 | ~TranslateDockWidget(); 16 | void updateErrorLabel(const std::string &error_message); 17 | void updateStatusLabel(const std::string &message); 18 | 19 | private slots: 20 | void openSettingsDialog(); 21 | 22 | private: 23 | Ui::TranslateDockWidget *ui; 24 | }; 25 | 26 | #endif // TRANSLATEDOCKWIDGET_H 27 | -------------------------------------------------------------------------------- /.github/workflows/check-format.yaml: -------------------------------------------------------------------------------- 1 | name: Check Code Formatting 🛠️ 2 | on: 3 | workflow_call: 4 | jobs: 5 | clang-format: 6 | runs-on: ubuntu-22.04 7 | steps: 8 | - uses: actions/checkout@v3 9 | with: 10 | fetch-depth: 0 11 | - name: clang-format check 🐉 12 | id: clang-format 13 | uses: ./.github/actions/run-clang-format 14 | with: 15 | failCondition: error 16 | 17 | cmake-format: 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - name: cmake-format check 🎛️ 24 | id: cmake-format 25 | uses: ./.github/actions/run-cmake-format 26 | with: 27 | failCondition: error 28 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_macos: -------------------------------------------------------------------------------- 1 | autoload -Uz is-at-least log_group log_info log_error log_status read_codesign 2 | 3 | local macos_version=$(sw_vers -productVersion) 4 | 5 | log_group 'Install macOS build requirements' 6 | log_info 'Checking macOS version...' 7 | if ! is-at-least 11.0 ${macos_version}; then 8 | log_error "Minimum required macOS version is 11.0, but running on macOS ${macos_version}" 9 | return 2 10 | else 11 | log_status "macOS ${macos_version} is recent" 12 | fi 13 | 14 | log_info 'Checking for Homebrew...' 15 | if (( ! ${+commands[brew]} )) { 16 | log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)' 17 | return 2 18 | } 19 | 20 | brew bundle --no-upgrade --file ${SCRIPT_HOME}/.Brewfile 21 | rehash 22 | log_group 23 | -------------------------------------------------------------------------------- /cmake/common/ccache.cmake: -------------------------------------------------------------------------------- 1 | # CMake ccache module 2 | 3 | include_guard(GLOBAL) 4 | 5 | if(NOT DEFINED CCACHE_PROGRAM) 6 | message(DEBUG "Trying to find ccache on build host...") 7 | find_program(CCACHE_PROGRAM "ccache") 8 | mark_as_advanced(CCACHE_PROGRAM) 9 | endif() 10 | 11 | if(CCACHE_PROGRAM) 12 | message(DEBUG "Ccache found as ${CCACHE_PROGRAM}...") 13 | option(ENABLE_CCACHE "Enable compiler acceleration with ccache" ON) 14 | 15 | if(ENABLE_CCACHE) 16 | set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") 17 | set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") 18 | set(CMAKE_OBJC_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") 19 | set(CMAKE_OBJCXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") 20 | set(CMAKE_CUDA_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") 21 | endif() 22 | endif() 23 | -------------------------------------------------------------------------------- /cmake/linux/toolchains/x86_64-linux-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR x86_64) 3 | set(CMAKE_CROSSCOMPILING TRUE) 4 | 5 | set(CMAKE_C_COMPILER /usr/bin/x86_64-linux-gnu-gcc) 6 | set(CMAKE_CXX_COMPILER /usr/bin/x86_64-linux-gnu-g++) 7 | 8 | set(CMAKE_FIND_ROOT_PATH /usr/x86_64-linux-gnu) 9 | 10 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 11 | 12 | set(PKG_CONFIG_EXECUTABLE 13 | /usr/bin/x86_64-linux-gnu-pkg-config 14 | CACHE FILEPATH "pkg-config executable") 15 | 16 | set(CPACK_READELF_EXECUTABLE /usr/bin/x86_64-linux-gnu-readelf) 17 | set(CPACK_OBJCOPY_EXECUTABLE /usr/bin/x86_64-linux-gnu-objcopy) 18 | set(CPACK_OBJDUMP_EXECUTABLE /usr/bin/x86_64-linux-gnu-objdump) 19 | set(CPACK_PACKAGE_ARCHITECTURE x86_64) 20 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE x86_64) 21 | -------------------------------------------------------------------------------- /cmake/linux/toolchains/aarch64-linux-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 3 | set(CMAKE_CROSSCOMPILING TRUE) 4 | 5 | set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc) 6 | set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++) 7 | 8 | set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) 9 | 10 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 11 | 12 | set(PKG_CONFIG_EXECUTABLE 13 | /usr/bin/aarch64-linux-gnu-pkg-config 14 | CACHE FILEPATH "pkg-config executable") 15 | 16 | set(CPACK_READELF_EXECUTABLE /usr/bin/aarch64-linux-gnu-readelf) 17 | set(CPACK_OBJCOPY_EXECUTABLE /usr/bin/aarch64-linux-gnu-objcopy) 18 | set(CPACK_OBJDUMP_EXECUTABLE /usr/bin/aarch64-linux-gnu-objdump) 19 | set(CPACK_PACKAGE_ARCHITECTURE arm64) 20 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64) 21 | -------------------------------------------------------------------------------- /cmake/common/buildnumber.cmake: -------------------------------------------------------------------------------- 1 | # CMake build number module 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Define build number cache file 6 | set(_BUILD_NUMBER_CACHE 7 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/.CMakeBuildNumber" 8 | CACHE INTERNAL "OBS build number cache file") 9 | 10 | # Read build number from cache file or manual override 11 | if(NOT DEFINED PLUGIN_BUILD_NUMBER AND EXISTS "${_BUILD_NUMBER_CACHE}") 12 | file(READ "${_BUILD_NUMBER_CACHE}" PLUGIN_BUILD_NUMBER) 13 | math(EXPR PLUGIN_BUILD_NUMBER "${PLUGIN_BUILD_NUMBER}+1") 14 | elseif(NOT DEFINED PLUGIN_BUILD_NUMBER) 15 | if($ENV{CI} AND $ENV{GITHUB_RUN_ID}) 16 | set(PLUGIN_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}") 17 | else() 18 | set(PLUGIN_BUILD_NUMBER "1") 19 | endif() 20 | endif() 21 | file(WRITE "${_BUILD_NUMBER_CACHE}" "${PLUGIN_BUILD_NUMBER}") 22 | -------------------------------------------------------------------------------- /.github/workflows/pr-pull.yaml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | run-name: ${{ github.event.pull_request.title }} pull request run 🚀 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | paths-ignore: 7 | - '**.md' 8 | branches: [master, main] 9 | types: [ opened, synchronize, reopened ] 10 | permissions: 11 | contents: read 12 | concurrency: 13 | group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' 14 | cancel-in-progress: true 15 | jobs: 16 | check-format: 17 | name: Check Formatting 🔍 18 | uses: ./.github/workflows/check-format.yaml 19 | permissions: 20 | contents: read 21 | 22 | build-project: 23 | name: Build Project 🧱 24 | uses: ./.github/workflows/build-project.yaml 25 | secrets: inherit 26 | permissions: 27 | contents: read 28 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Ensure-Location.ps1: -------------------------------------------------------------------------------- 1 | function Ensure-Location { 2 | <# 3 | .SYNOPSIS 4 | Ensures current location to be set to specified directory. 5 | .DESCRIPTION 6 | If specified directory exists, switch to it. Otherwise create it, 7 | then switch. 8 | .EXAMPLE 9 | Ensure-Location "My-Directory" 10 | Ensure-Location -Path "Path-To-My-Directory" 11 | #> 12 | 13 | param( 14 | [Parameter(Mandatory)] 15 | [string] $Path 16 | ) 17 | 18 | if ( ! ( Test-Path $Path ) ) { 19 | $_Params = @{ 20 | ItemType = "Directory" 21 | Path = ${Path} 22 | ErrorAction = "SilentlyContinue" 23 | } 24 | 25 | New-Item @_Params | Set-Location 26 | } else { 27 | Set-Location -Path ${Path} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cmake/windows/buildspec.cmake: -------------------------------------------------------------------------------- 1 | # CMake Windows build dependencies module 2 | 3 | include_guard(GLOBAL) 4 | 5 | include(buildspec_common) 6 | 7 | # _check_dependencies_windows: Set up Windows slice for _check_dependencies 8 | function(_check_dependencies_windows) 9 | set(arch ${CMAKE_GENERATOR_PLATFORM}) 10 | set(platform windows-${arch}) 11 | 12 | set(dependencies_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps") 13 | set(prebuilt_filename "windows-deps-VERSION-ARCH-REVISION.zip") 14 | set(prebuilt_destination "obs-deps-VERSION-ARCH") 15 | set(qt6_filename "windows-deps-qt6-VERSION-ARCH-REVISION.zip") 16 | set(qt6_destination "obs-deps-qt6-VERSION-ARCH") 17 | set(obs-studio_filename "VERSION.zip") 18 | set(obs-studio_destination "obs-studio-VERSION") 19 | set(dependencies_list prebuilt qt6 obs-studio) 20 | 21 | _check_dependencies() 22 | endfunction() 23 | 24 | _check_dependencies_windows() 25 | -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": { 3 | "line_width": 120, 4 | "tab_size": 2, 5 | "enable_sort": true, 6 | "autosort": true 7 | }, 8 | "additional_commands": { 9 | "find_qt": { 10 | "flags": [], 11 | "kwargs": { 12 | "COMPONENTS": "+", 13 | "COMPONENTS_WIN": "+", 14 | "COMPONENTS_MACOS": "+", 15 | "COMPONENTS_LINUX": "+" 16 | } 17 | }, 18 | "set_target_properties_obs": { 19 | "pargs": 1, 20 | "flags": [], 21 | "kwargs": { 22 | "PROPERTIES": { 23 | "kwargs": { 24 | "PREFIX": 1, 25 | "OUTPUT_NAME": 1, 26 | "FOLDER": 1, 27 | "VERSION": 1, 28 | "SOVERSION": 1, 29 | "AUTOMOC": 1, 30 | "AUTOUIC": 1, 31 | "AUTORCC": 1, 32 | "AUTOUIC_SEARCH_PATHS": 1, 33 | "BUILD_RPATH": 1, 34 | "INSTALL_RPATH": 1 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cmake/common/osconfig.cmake: -------------------------------------------------------------------------------- 1 | # CMake operating system bootstrap module 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Set minimum CMake version specific to host operating system, add OS-specific module directory to default search paths, 6 | # and set helper variables for OS detection in other CMake list files. 7 | if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") 8 | set(CMAKE_C_EXTENSIONS FALSE) 9 | set(CMAKE_CXX_EXTENSIONS FALSE) 10 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows") 11 | set(OS_WINDOWS TRUE) 12 | elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") 13 | set(CMAKE_C_EXTENSIONS FALSE) 14 | set(CMAKE_CXX_EXTENSIONS FALSE) 15 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos") 16 | set(OS_MACOS TRUE) 17 | elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD|OpenBSD") 18 | set(CMAKE_CXX_EXTENSIONS FALSE) 19 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux") 20 | string(TOUPPER "${CMAKE_HOST_SYSTEM_NAME}" _SYSTEM_NAME_U) 21 | set(OS_${_SYSTEM_NAME_U} TRUE) 22 | endif() 23 | -------------------------------------------------------------------------------- /cmake/windows/resources/resource.rc.in: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 3 | PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 4 | FILEFLAGSMASK 0x0L 5 | #ifdef _DEBUG 6 | FILEFLAGS 0x1L 7 | #else 8 | FILEFLAGS 0x0L 9 | #endif 10 | FILEOS 0x0L 11 | FILETYPE 0x2L 12 | FILESUBTYPE 0x0L 13 | BEGIN 14 | BLOCK "StringFileInfo" 15 | BEGIN 16 | BLOCK "040904b0" 17 | BEGIN 18 | VALUE "CompanyName", "${PLUGIN_AUTHOR}" 19 | VALUE "FileDescription", "${PROJECT_NAME}" 20 | VALUE "FileVersion", "${PROJECT_VERSION}" 21 | VALUE "InternalName", "${PROJECT_NAME}" 22 | VALUE "LegalCopyright", "(C) ${CURRENT_YEAR} ${PLUGIN_AUTHOR}" 23 | VALUE "OriginalFilename", "${PROJECT_NAME}" 24 | VALUE "ProductName", "${PROJECT_NAME}" 25 | VALUE "ProductVersion", "${PROJECT_VERSION}" 26 | END 27 | END 28 | BLOCK "VarFileInfo" 29 | BEGIN 30 | VALUE "Translation", 0x409, 1200 31 | END 32 | END 33 | -------------------------------------------------------------------------------- /src/plugin-support.h: -------------------------------------------------------------------------------- 1 | /* 2 | Plugin Name 3 | Copyright (C) 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program. If not, see 17 | */ 18 | 19 | #pragma once 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | extern const char *PLUGIN_NAME; 31 | extern const char *PLUGIN_VERSION; 32 | 33 | void obs_log(int log_level, const char *format, ...); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /src/ui/registerDock.cpp: -------------------------------------------------------------------------------- 1 | #include "registerDock.h" 2 | #include "translatedockwidget.h" 3 | #include "plugin-support.h" 4 | #include "utils/config-data.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | void registerDock() 14 | { 15 | obs_log(LOG_INFO, "Registering dock..."); 16 | // This function is called when the plugin is loaded. 17 | // It registers a dock widget with OBS. 18 | QMainWindow *parent = (QMainWindow *)obs_frontend_get_main_window(); 19 | // Create the dock 20 | TranslateDockWidget *dock = new TranslateDockWidget((QWidget *)parent); 21 | // set the error callback on the global conetxt 22 | global_context.error_callback = [=](const std::string &error_message) { 23 | global_context.error_message = error_message; 24 | dock->updateErrorLabel(error_message); 25 | }; 26 | global_context.status_callback = [=](const std::string &message) { 27 | global_context.status_message = message; 28 | dock->updateStatusLabel(message); 29 | }; 30 | // Register the dock 31 | obs_frontend_add_dock(dock); 32 | 33 | parent->addDockWidget(Qt::BottomDockWidgetArea, dock); 34 | } 35 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Invoke-External.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-External { 2 | <# 3 | .SYNOPSIS 4 | Invokes a non-PowerShell command. 5 | .DESCRIPTION 6 | Runs a non-PowerShell command, and captures its return code. 7 | Throws an exception if the command returns non-zero. 8 | .EXAMPLE 9 | Invoke-External 7z x $MyArchive 10 | #> 11 | 12 | if ( $args.Count -eq 0 ) { 13 | throw 'Invoke-External called without arguments.' 14 | } 15 | 16 | if ( ! ( Test-Path function:Log-Information ) ) { 17 | . $PSScriptRoot/Logger.ps1 18 | } 19 | 20 | $Command = $args[0] 21 | $CommandArgs = @() 22 | 23 | if ( $args.Count -gt 1) { 24 | $CommandArgs = $args[1..($args.Count - 1)] 25 | } 26 | 27 | $_EAP = $ErrorActionPreference 28 | $ErrorActionPreference = "Continue" 29 | 30 | Log-Debug "Invoke-External: ${Command} ${CommandArgs}" 31 | 32 | & $command $commandArgs 33 | $Result = $LASTEXITCODE 34 | 35 | $ErrorActionPreference = $_EAP 36 | 37 | if ( $Result -ne 0 ) { 38 | throw "${Command} ${CommandArgs} exited with non-zero code ${Result}." 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_ccache: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_warning 2 | 3 | if (( ! ${+project_root} )) { 4 | log_error "'project_root' not set. Please set before running ${0}." 5 | return 2 6 | } 7 | 8 | if (( ${+commands[ccache]} )) { 9 | log_debug "Found ccache at ${commands[ccache]}" 10 | 11 | typeset -gx CCACHE_CONFIGPATH="${project_root}/.ccache.conf" 12 | 13 | ccache --set-config=run_second_cpp=true 14 | ccache --set-config=direct_mode=true 15 | ccache --set-config=inode_cache=true 16 | ccache --set-config=compiler_check=content 17 | ccache --set-config=file_clone=true 18 | 19 | local -a sloppiness=( 20 | include_file_mtime 21 | include_file_ctime 22 | file_stat_matches 23 | system_headers 24 | ) 25 | 26 | if [[ ${host_os} == macos ]] { 27 | sloppiness+=( 28 | modules 29 | clang_index_store 30 | ) 31 | 32 | ccache --set-config=sloppiness=${(j:,:)sloppiness} 33 | } 34 | 35 | if (( ${+CI} )) { 36 | ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache" 37 | ccache --set-config=max_size="${CCACHE_SIZE:-1G}" 38 | ccache -z > /dev/null 39 | } 40 | } else { 41 | log_warning "No ccache found on the system" 42 | } 43 | -------------------------------------------------------------------------------- /cmake/macos/buildspec.cmake: -------------------------------------------------------------------------------- 1 | # CMake macOS build dependencies module 2 | 3 | include_guard(GLOBAL) 4 | 5 | include(buildspec_common) 6 | 7 | # _check_dependencies_macos: Set up macOS slice for _check_dependencies 8 | function(_check_dependencies_macos) 9 | set(arch universal) 10 | set(platform macos) 11 | 12 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/buildspec.json" buildspec) 13 | 14 | set(dependencies_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps") 15 | set(prebuilt_filename "macos-deps-VERSION-ARCH_REVISION.tar.xz") 16 | set(prebuilt_destination "obs-deps-VERSION-ARCH") 17 | set(qt6_filename "macos-deps-qt6-VERSION-ARCH-REVISION.tar.xz") 18 | set(qt6_destination "obs-deps-qt6-VERSION-ARCH") 19 | set(obs-studio_filename "VERSION.tar.gz") 20 | set(obs-studio_destination "obs-studio-VERSION") 21 | set(dependencies_list prebuilt qt6 obs-studio) 22 | 23 | _check_dependencies() 24 | 25 | execute_process(COMMAND "xattr" -r -d com.apple.quarantine "${dependencies_dir}" 26 | RESULT_VARIABLE result COMMAND_ERROR_IS_FATAL ANY) 27 | 28 | list(APPEND CMAKE_FRAMEWORK_PATH "${dependencies_dir}/Frameworks") 29 | set(CMAKE_FRAMEWORK_PATH 30 | ${CMAKE_FRAMEWORK_PATH} 31 | PARENT_SCOPE) 32 | endfunction() 33 | 34 | _check_dependencies_macos() 35 | -------------------------------------------------------------------------------- /src/plugin-support.c.in: -------------------------------------------------------------------------------- 1 | /* 2 | Plugin Name 3 | Copyright (C) 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program. If not, see 17 | */ 18 | 19 | #include 20 | 21 | const char *PLUGIN_NAME = "@CMAKE_PROJECT_NAME@"; 22 | const char *PLUGIN_VERSION = "@CMAKE_PROJECT_VERSION@"; 23 | 24 | extern void blogva(int log_level, const char *format, va_list args); 25 | 26 | void obs_log(int log_level, const char *format, ...) 27 | { 28 | size_t length = 4 + strlen(PLUGIN_NAME) + strlen(format); 29 | 30 | char *template = malloc(length + 1); 31 | 32 | snprintf(template, length, "[%s] %s", PLUGIN_NAME, format); 33 | 34 | va_list(args); 35 | 36 | va_start(args, format); 37 | blogva(log_level, template, args); 38 | va_end(args); 39 | 40 | free(template); 41 | } 42 | -------------------------------------------------------------------------------- /cmake/macos/resources/distribution.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | @CMAKE_PROJECT_NAME@ 10 | 11 | 12 | 13 | 14 | 15 | 16 | #@CMAKE_PROJECT_NAME@.pkg 17 | 18 | 33 | 34 | -------------------------------------------------------------------------------- /cmake/windows/compilerconfig.cmake: -------------------------------------------------------------------------------- 1 | # CMake Windows compiler configuration module 2 | 3 | include_guard(GLOBAL) 4 | 5 | include(compiler_common) 6 | 7 | # CMake 3.24 introduces a bug mistakenly interpreting MSVC as supporting the '-pthread' compiler flag 8 | if(CMAKE_VERSION VERSION_EQUAL 3.24.0) 9 | set(THREADS_HAVE_PTHREAD_ARG FALSE) 10 | endif() 11 | 12 | message(DEBUG "Current Windows API version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") 13 | if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) 14 | message(DEBUG "Maximum Windows API version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM}") 15 | endif() 16 | 17 | if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS 10.0.20348) 18 | message(FATAL_ERROR "OBS requires Windows 10 SDK version 10.0.20348.0 or more recent.\n" 19 | "Please download and install the most recent Windows platform SDK.") 20 | endif() 21 | 22 | add_compile_options( 23 | /W3 /utf-8 "$<$:/MP>" "$<$:/MP>" 24 | "$<$:${_obs_clang_c_options}>" 25 | "$<$:${_obs_clang_cxx_options}>") 26 | 27 | add_compile_definitions(UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS $<$:DEBUG> 28 | $<$:_DEBUG>) 29 | 30 | add_link_options("$<$>:/OPT:REF>" "$<$:/INCREMENTAL:NO>" 31 | "$<$:/INCREMENTAL:NO>" "$<$:/OPT:ICF>") 32 | 33 | if(CMAKE_COMPILE_WARNING_AS_ERROR) 34 | add_link_options(/WX) 35 | endif() 36 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_pass: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Apple Developer credentials necessary: 3 | # 4 | # + Signing for distribution and notarization require an active Apple 5 | # Developer membership 6 | # + An Apple Development identity is needed for code signing 7 | # (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)') 8 | # + Your Apple developer ID is needed for notarization 9 | # + An app-specific password is necessary for notarization from CLI 10 | # + This password will be stored in your macOS keychain under the identifier 11 | # 'OBS-Codesign-Password'with access Apple's 'altool' only. 12 | ############################################################################## 13 | 14 | autoload -Uz read_codesign read_codesign_user log_info log_warning 15 | 16 | if (( ! ${+CODESIGN_IDENT} )) { 17 | read_codesign 18 | } 19 | 20 | if (( ! ${+CODESIGN_IDENT_USER} )) { 21 | read_codesign_user 22 | } 23 | 24 | log_info 'Setting up password for notarization keychain...' 25 | if (( ! ${+CODESIGN_IDENT_PASS} )) { 26 | read -s CODESIGN_IDENT_PASS'?Apple Developer ID password: ' 27 | } 28 | 29 | print '' 30 | log_info 'Setting up notarization keychain...' 31 | log_warning " 32 | + Your Apple ID and an app-specific password is necessary for notarization from CLI 33 | + This password will be stored in your macOS keychain under the identifier 34 | 'OBS-Codesign-Password' with access Apple's 'altool' only. 35 | 36 | " 37 | xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${CODESIGN_TEAM}" --password "${CODESIGN_IDENT_PASS}" 38 | 39 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_error log_status log_info mkcd 2 | 3 | if (( ! ${+project_root} )) { 4 | log_error "'project_root' not set. Please set before running ${0}." 5 | return 2 6 | } 7 | 8 | if (( ! ${+target} )) { 9 | log_error "'target' not set. Please set before running ${0}." 10 | return 2 11 | } 12 | 13 | pushd ${project_root} 14 | 15 | typeset -g QT_VERSION 16 | 17 | local -a apt_args=( 18 | ${CI:+-y} 19 | --no-install-recommends 20 | ) 21 | if (( _loglevel == 0 )) apt_args+=(--quiet) 22 | 23 | if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) { 24 | log_group 'Installing obs-studio build dependencies...' 25 | 26 | local suffix 27 | if [[ ${CPUTYPE} != "${target##*-}" ]] { 28 | local -A arch_mappings=( 29 | aarch64 arm64 30 | x86_64 amd64 31 | ) 32 | 33 | suffix=":${arch_mappings[${target##*-}]}" 34 | 35 | sudo apt-get install ${apt_args} gcc-${${target##*-}//_/-}-linux-gnu g++-${${target##*-}//_/-}-linux-gnu 36 | } 37 | 38 | sudo add-apt-repository --yes ppa:obsproject/obs-studio 39 | sudo apt update 40 | 41 | sudo apt-get install ${apt_args} \ 42 | build-essential \ 43 | libgles2-mesa-dev \ 44 | obs-studio 45 | 46 | local -a _qt_packages=() 47 | 48 | if (( QT_VERSION == 5 )) { 49 | _qt_packages+=( 50 | qtbase5-dev${suffix} 51 | libqt5svg5-dev${suffix} 52 | qtbase5-private-dev${suffix} 53 | libqt5x11extras5-dev${suffix} 54 | ) 55 | } else { 56 | _qt_packages+=( 57 | qt6-base-dev${suffix} 58 | libqt6svg6-dev${suffix} 59 | qt6-base-private-dev${suffix} 60 | ) 61 | } 62 | 63 | sudo apt-get install ${apt_args} ${_qt_packages} 64 | log_group 65 | } 66 | -------------------------------------------------------------------------------- /src/model-utils/model-downloader-ui.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_DOWNLOADER_UI_H 2 | #define MODEL_DOWNLOADER_UI_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "model-downloader-types.h" 13 | 14 | class ModelDownloadWorker : public QObject { 15 | Q_OBJECT 16 | public: 17 | ModelDownloadWorker(const ModelInfo &model_info); 18 | ~ModelDownloadWorker(); 19 | 20 | public slots: 21 | void download_model(); 22 | 23 | signals: 24 | void download_progress(int progress); 25 | void download_finished(const ModelInfo &info); 26 | void download_error(const std::string &reason); 27 | 28 | private: 29 | static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, 30 | curl_off_t ultotal, curl_off_t ulnow); 31 | std::string download_file(CURL *curl, const std::string &url, const std::string &path); 32 | ModelInfo model_info; 33 | }; 34 | 35 | class ModelDownloader : public QDialog { 36 | Q_OBJECT 37 | public: 38 | ModelDownloader(const ModelInfo &model_info, 39 | download_finished_callback_t download_finished_callback, 40 | QWidget *parent = nullptr); 41 | ~ModelDownloader(); 42 | 43 | public slots: 44 | void update_progress(int progress); 45 | void download_finished(const ModelInfo &info); 46 | void show_error(const std::string &reason); 47 | 48 | protected: 49 | void closeEvent(QCloseEvent *e) override; 50 | 51 | private: 52 | QVBoxLayout *layout; 53 | QProgressBar *progress_bar; 54 | QThread *download_thread; 55 | ModelDownloadWorker *download_worker; 56 | // Callback for when the download is finished 57 | download_finished_callback_t download_finished_callback; 58 | bool mPrepareToClose; 59 | void close(); 60 | ModelInfo model_info; 61 | }; 62 | 63 | #endif // MODEL_DOWNLOADER_UI_H 64 | -------------------------------------------------------------------------------- /src/plugin-main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Plugin Name 3 | Copyright (C) 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program. If not, see 17 | */ 18 | 19 | #include 20 | #include 21 | #include "translation-service/translation.h" 22 | #include "ui/registerDock.h" 23 | #include "utils/config-data.h" 24 | 25 | OBS_DECLARE_MODULE() 26 | OBS_MODULE_USE_DEFAULT_LOCALE(PLUGIN_NAME, "en-US") 27 | 28 | bool obs_module_load(void) 29 | { 30 | obs_log(LOG_INFO, "plugin loaded successfully (version %s)", PLUGIN_VERSION); 31 | 32 | resetContext(true); 33 | registerDock(); 34 | 35 | // load plugin settings from config 36 | if (loadConfig() == OBS_POLYGLOT_CONFIG_SUCCESS) { 37 | obs_log(LOG_INFO, "Loaded config from config file"); 38 | } else { 39 | obs_log(LOG_INFO, "Failed to load config from config file"); 40 | } 41 | // build the translation context 42 | if (build_translation_context() != OBS_POLYGLOT_TRANSLATION_INIT_SUCCESS) { 43 | obs_log(LOG_ERROR, "Failed to build translation context"); 44 | } else { 45 | obs_log(LOG_INFO, "Built translation context"); 46 | } 47 | 48 | return true; 49 | } 50 | 51 | void obs_module_unload(void) 52 | { 53 | freeContext(true); 54 | obs_log(LOG_INFO, "plugin unloaded"); 55 | } 56 | -------------------------------------------------------------------------------- /cmake/macos/defaults.cmake: -------------------------------------------------------------------------------- 1 | # CMake macOS defaults module 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Set empty codesigning team if not specified as cache variable 6 | if(NOT CODESIGN_TEAM) 7 | set(CODESIGN_TEAM 8 | "" 9 | CACHE STRING "OBS code signing team for macOS" FORCE) 10 | 11 | # Set ad-hoc codesigning identity if not specified as cache variable 12 | if(NOT CODESIGN_IDENTITY) 13 | set(CODESIGN_IDENTITY 14 | "-" 15 | CACHE STRING "OBS code signing identity for macOS" FORCE) 16 | endif() 17 | endif() 18 | 19 | if(XCODE) 20 | include(xcode) 21 | endif() 22 | 23 | include(buildspec) 24 | 25 | # Set default deployment target to 11.0 if not set and enable selection in GUI up to 13.0 26 | if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) 27 | set(CMAKE_OSX_DEPLOYMENT_TARGET 28 | 11.0 29 | CACHE STRING "Minimum macOS version to target for deployment (at runtime). Newer APIs will be weak-linked." FORCE) 30 | endif() 31 | set_property(CACHE CMAKE_OSX_DEPLOYMENT_TARGET PROPERTY STRINGS 13.0 12.0 11.0) 32 | 33 | # Use Applications directory as default install destination 34 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 35 | set(CMAKE_INSTALL_PREFIX 36 | "$ENV{HOME}/Library/Application Support/obs-studio/plugins" 37 | CACHE STRING "Directory to install OBS after building" FORCE) 38 | endif() 39 | 40 | # Enable find_package targets to become globally available targets 41 | set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE) 42 | # Enable RPATH support for generated binaries 43 | set(CMAKE_MACOSX_RPATH TRUE) 44 | # Use RPATHs from build tree _in_ the build tree 45 | set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 46 | # Do not add default linker search paths to RPATH 47 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) 48 | # Use common bundle-relative RPATH for installed targets 49 | set(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks") 50 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16...3.26) 2 | 3 | include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) 4 | 5 | project(${_name} VERSION ${_version}) 6 | 7 | option(ENABLE_FRONTEND_API "Use obs-frontend-api for UI functionality" OFF) 8 | option(ENABLE_QT "Use Qt functionality" OFF) 9 | 10 | include(compilerconfig) 11 | include(defaults) 12 | include(helpers) 13 | 14 | add_library(${CMAKE_PROJECT_NAME} MODULE) 15 | 16 | find_package(libobs REQUIRED) 17 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::libobs) 18 | 19 | include(cmake/BuildCTranslate2.cmake) 20 | include(cmake/BuildSentencepiece.cmake) 21 | include(cmake/BuildCppHTTPLib.cmake) 22 | include(cmake/BuildMyCurl.cmake) 23 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ct2 sentencepiece cpphttplib libcurl) 24 | 25 | if(ENABLE_FRONTEND_API) 26 | find_package(obs-frontend-api REQUIRED) 27 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::obs-frontend-api) 28 | endif() 29 | 30 | if(ENABLE_QT) 31 | find_qt(COMPONENTS Widgets Core) 32 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt::Core Qt::Widgets) 33 | target_compile_options( 34 | ${CMAKE_PROJECT_NAME} PRIVATE $<$:-Wno-quoted-include-in-framework-header 35 | -Wno-comma>) 36 | set_target_properties( 37 | ${CMAKE_PROJECT_NAME} 38 | PROPERTIES AUTOMOC ON 39 | AUTOUIC ON 40 | AUTORCC ON) 41 | endif() 42 | 43 | add_subdirectory(src/ui) 44 | target_sources( 45 | ${CMAKE_PROJECT_NAME} 46 | PRIVATE src/translation-service/translation.cpp src/plugin-main.c src/utils/config-data.cpp 47 | src/translation-service/httpserver.cpp src/model-utils/model-downloader.cpp 48 | src/model-utils/model-downloader-ui.cpp) 49 | 50 | set_target_properties_plugin(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${_name}) 51 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info log_status log_error log_debug log_warning log_group 2 | 3 | log_group 'Check Linux build requirements' 4 | log_debug 'Checking Linux distribution name and version...' 5 | 6 | # Check for Ubuntu version 22.10 or later, which have srt and librist available via apt-get 7 | typeset -g -i UBUNTU_2210_OR_LATER=0 8 | if [[ -f /etc/os_release ]] { 9 | local dist_name 10 | local dist_version 11 | read -r dist_name dist_version <<< "$(source /etc/os_release; print "${NAME} ${VERSION_ID}")" 12 | 13 | autoload -Uz is-at-least 14 | if [[ ${dist_name} == Ubuntu ]] && is-at-least 22.10 ${dist_version}; then 15 | typeset -g -i UBUNTU_2210_OR_LATER=1 16 | fi 17 | } 18 | 19 | log_debug 'Checking for apt-get...' 20 | if (( ! ${+commands[apt-get]} )) { 21 | log_error 'No apt-get command found. Please install apt' 22 | return 2 23 | } else { 24 | log_debug "Apt-get located at ${commands[apt-get]}" 25 | } 26 | 27 | local -a dependencies=("${(fA)$(<${SCRIPT_HOME}/.Aptfile)}") 28 | local -a install_list 29 | local binary 30 | 31 | sudo apt-get update -qq 32 | 33 | for dependency (${dependencies}) { 34 | local -a tokens=(${=dependency//(,|:|\')/}) 35 | 36 | if [[ ! ${tokens[1]} == 'package' ]] continue 37 | 38 | if [[ ${#tokens} -gt 2 && ${tokens[3]} == 'bin' ]] { 39 | binary=${tokens[4]} 40 | } else { 41 | binary=${tokens[2]} 42 | } 43 | 44 | if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]}) 45 | } 46 | 47 | log_debug "List of dependencies to install: ${install_list}" 48 | if (( ${#install_list} )) { 49 | if (( ! ${+CI} )) log_warning 'Dependency installation via apt may require elevated privileges' 50 | 51 | local -a apt_args=( 52 | ${CI:+-y} 53 | --no-install-recommends 54 | ) 55 | if (( _loglevel == 0 )) apt_args+=(--quiet) 56 | 57 | sudo apt-get ${apt_args} install ${install_list} 58 | } 59 | 60 | rehash 61 | log_group 62 | -------------------------------------------------------------------------------- /cmake/linux/toolchains/x86_64-linux-clang.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR x86_64) 3 | set(CMAKE_CROSSCOMPILING TRUE) 4 | 5 | set(CMAKE_C_COMPILER /usr/bin/clang) 6 | set(CMAKE_CXX_COMPILER /usr/bin/clang++) 7 | 8 | set(CMAKE_C_COMPILER_TARGET x86_64-linux-gnu) 9 | set(CMAKE_CXX_COMPILER_TARGET x86_64-linux-gnu) 10 | 11 | set(CMAKE_FIND_ROOT_PATH /usr/x86_64-linux-gnu) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 13 | set(PKG_CONFIG_EXECUTABLE 14 | /usr/bin/x86_64-linux-gnu-pkg-config 15 | CACHE FILEPATH "pkg-config executable") 16 | 17 | execute_process( 18 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-ranlib 19 | OUTPUT_VARIABLE CMAKE_RANLIB 20 | OUTPUT_STRIP_TRAILING_WHITESPACE) 21 | 22 | execute_process( 23 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-ar 24 | OUTPUT_VARIABLE CMAKE_LLVM_AR 25 | OUTPUT_STRIP_TRAILING_WHITESPACE) 26 | 27 | execute_process( 28 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-readelf 29 | OUTPUT_VARIABLE READELF 30 | OUTPUT_STRIP_TRAILING_WHITESPACE) 31 | 32 | execute_process( 33 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-objcopy 34 | OUTPUT_VARIABLE CMAKE_LLVM_OBJCOPY 35 | OUTPUT_STRIP_TRAILING_WHITESPACE) 36 | 37 | execute_process( 38 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-objdump 39 | OUTPUT_VARIABLE CMAKE_LLVM_OBJDUMP 40 | OUTPUT_STRIP_TRAILING_WHITESPACE) 41 | 42 | set(CMAKE_AR 43 | "${CMAKE_LLVM_AR}" 44 | CACHE INTERNAL "${CMAKE_SYSTEM_NAME} ar" FORCE) 45 | set(CMAKE_OBJCOPY 46 | "${CMAKE_LLVM_OBJCOPY}" 47 | CACHE INTERNAL "${CMAKE_SYSTEM_NAME} objcopy" FORCE) 48 | set(CMAKE_OBJDUMP 49 | "${CMAKE_LLVM_OBJDUMP}" 50 | CACHE INTERNAL "${CMAKE_SYSTEM_NAME} objdump" FORCE) 51 | 52 | set(CPACK_READELF_EXECUTABLE "${READELF}") 53 | set(CPACK_OBJCOPY_EXECUTABLE "${CMAKE_LLVM_OBJCOPY}") 54 | set(CPACK_OBJDUMP_EXECUTABLE "${CMAKE_LLVM_OBJDUMP}") 55 | set(CPACK_PACKAGE_ARCHITECTURE x86_64) 56 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE x86_64) 57 | -------------------------------------------------------------------------------- /cmake/linux/toolchains/aarch64-linux-clang.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 3 | set(CMAKE_CROSSCOMPILING TRUE) 4 | 5 | set(CMAKE_C_COMPILER /usr/bin/clang) 6 | set(CMAKE_CXX_COMPILER /usr/bin/clang++) 7 | 8 | set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu) 9 | set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu) 10 | 11 | set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 13 | set(PKG_CONFIG_EXECUTABLE 14 | /usr/bin/aarch64-linux-gnu-pkg-config 15 | CACHE FILEPATH "pkg-config executable") 16 | 17 | execute_process( 18 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-ranlib 19 | OUTPUT_VARIABLE CMAKE_RANLIB 20 | OUTPUT_STRIP_TRAILING_WHITESPACE) 21 | 22 | execute_process( 23 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-ar 24 | OUTPUT_VARIABLE CMAKE_LLVM_AR 25 | OUTPUT_STRIP_TRAILING_WHITESPACE) 26 | 27 | execute_process( 28 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-readelf 29 | OUTPUT_VARIABLE READELF 30 | OUTPUT_STRIP_TRAILING_WHITESPACE) 31 | 32 | execute_process( 33 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-objcopy 34 | OUTPUT_VARIABLE CMAKE_LLVM_OBJCOPY 35 | OUTPUT_STRIP_TRAILING_WHITESPACE) 36 | 37 | execute_process( 38 | COMMAND ${CMAKE_C_COMPILER} -print-prog-name=llvm-objdump 39 | OUTPUT_VARIABLE CMAKE_LLVM_OBJDUMP 40 | OUTPUT_STRIP_TRAILING_WHITESPACE) 41 | 42 | set(CMAKE_AR 43 | "${CMAKE_LLVM_AR}" 44 | CACHE INTERNAL "${CMAKE_SYSTEM_NAME} ar" FORCE) 45 | set(CMAKE_OBJCOPY 46 | "${CMAKE_LLVM_OBJCOPY}" 47 | CACHE INTERNAL "${CMAKE_SYSTEM_NAME} objcopy" FORCE) 48 | set(CMAKE_OBJDUMP 49 | "${CMAKE_LLVM_OBJDUMP}" 50 | CACHE INTERNAL "${CMAKE_SYSTEM_NAME} objdump" FORCE) 51 | 52 | set(CPACK_READELF_EXECUTABLE "${READELF}") 53 | set(CPACK_OBJCOPY_EXECUTABLE "${CMAKE_LLVM_OBJCOPY}") 54 | set(CPACK_OBJDUMP_EXECUTABLE "${CMAKE_LLVM_OBJDUMP}") 55 | set(CPACK_PACKAGE_ARCHITECTURE arm64) 56 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE arm64) 57 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_packages: -------------------------------------------------------------------------------- 1 | if (( ! ${+commands[packagesbuild]} )) { 2 | autoload -Uz log_group log_info log_status mkcd 3 | 4 | if (( ! ${+commands[curl]} )) { 5 | log_error 'curl not found. Please install curl.' 6 | return 2 7 | } 8 | 9 | if (( ! ${+project_root} )) { 10 | log_error "'project_root' not set. Please set before running ${0}." 11 | return 2 12 | } 13 | 14 | local -a curl_opts=() 15 | if (( ${+CI} )) { 16 | curl_opts+=(--show-error --silent) 17 | } else { 18 | curl_opts+=(--progress-bar) 19 | } 20 | curl_opts+=(--location -O) 21 | 22 | log_group 'Installing Packages.app...' 23 | 24 | local version 25 | local base_url 26 | local hash 27 | IFS=';' read -r version base_url hash <<< \ 28 | "$(jq -r '.tools.packages | {version, baseUrl, hash} | join(";")' buildspec.json)" 29 | 30 | mkdir -p ${project_root}/.deps && pushd ${project_root}/.deps 31 | curl ${curl_opts} "${base_url}/Packages.dmg" 32 | 33 | local checksum="$(sha256sum Packages.dmg | cut -d " " -f 1)" 34 | 35 | if [[ ${hash} != ${checksum} ]] { 36 | log_error "Checksum mismatch of Packages.dmg download. 37 | Expected : ${hash} 38 | Actual : ${checksum}" 39 | return 2 40 | } 41 | 42 | hdiutil attach -readonly -noverify -noautoopen -plist Packages.dmg > .result.plist 43 | local -i num_entities=$(( $(plutil -extract system-entities raw -- .result.plist) - 1 )) 44 | local keys 45 | local mount_point 46 | for i ({0..${num_entities}}) { 47 | keys=($(plutil -extract system-entities.${i} raw -- .result.plist)) 48 | if [[ ${keys} == *mount-point* ]] { 49 | mount_point=$(plutil -extract system-entities.${i}.mount-point raw -- .result.plist) 50 | break 51 | } 52 | } 53 | rm .result.plist 54 | 55 | log_status 'Installing Packages.app requires elevated privileges!' 56 | 57 | sudo installer -pkg ${mount_point}/packages/Packages.pkg -target / && rehash 58 | hdiutil detach ${mount_point} &> /dev/null && log_status 'Packages.dmg image unmounted.' 59 | popd 60 | 61 | log_group 62 | } 63 | -------------------------------------------------------------------------------- /src/ui/translatedockwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | TranslateDockWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 443 10 | 440 11 | 12 | 13 | 14 | PolyGlot Translation 15 | 16 | 17 | 18 | 19 | 20 | 21 | Setting 22 | 23 | 24 | 25 | 26 | 27 | 28 | Start/Stop HTTP Server 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | Status Label 39 | 40 | 41 | 42 | 43 | 44 | 45 | Error Label 46 | 47 | 48 | true 49 | 50 | 51 | 52 | 53 | 54 | 55 | Qt::Vertical 56 | 57 | 58 | 59 | 20 60 | 40 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /cmake/macos/resources/create-package.cmake.in: -------------------------------------------------------------------------------- 1 | make_directory("$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/package/Library/Application Support/obs-studio/plugins") 2 | 3 | if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.plugin" AND NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.plugin") 4 | file(INSTALL DESTINATION "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/package/Library/Application Support/obs-studio/plugins" 5 | TYPE DIRECTORY FILES "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.plugin" USE_SOURCE_PERMISSIONS) 6 | 7 | if(CMAKE_INSTALL_CONFIG_NAME MATCHES "^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$" OR CMAKE_INSTALL_CONFIG_NAME MATCHES "^([Mm][Ii][Nn][Ss][Ii][Zz][Ee][Rr][Ee][Ll])$") 8 | if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.plugin.dSYM" AND NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.plugin.dSYM") 9 | file(INSTALL DESTINATION "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/package/Library/Application Support/obs-studio/plugins" TYPE DIRECTORY FILES "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.plugin.dSYM" USE_SOURCE_PERMISSIONS) 10 | endif() 11 | endif() 12 | endif() 13 | 14 | make_directory("$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/temp") 15 | 16 | execute_process( 17 | COMMAND /usr/bin/pkgbuild 18 | --identifier '@MACOS_BUNDLEID@' 19 | --version '@CMAKE_PROJECT_VERSION@' 20 | --root "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/package" 21 | "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/temp/@CMAKE_PROJECT_NAME@.pkg" 22 | COMMAND_ERROR_IS_FATAL ANY 23 | ) 24 | 25 | execute_process( 26 | COMMAND /usr/bin/productbuild 27 | --distribution "@CMAKE_CURRENT_BINARY_DIR@/distribution" 28 | --package-path "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/temp" 29 | "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.pkg" 30 | COMMAND_ERROR_IS_FATAL ANY) 31 | 32 | if(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/@CMAKE_PROJECT_NAME@.pkg") 33 | file(REMOVE_RECURSE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/temp") 34 | file(REMOVE_RECURSE "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/package") 35 | endif() 36 | -------------------------------------------------------------------------------- /src/ui/translatedockwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "translatedockwidget.h" 2 | #include "ui_translatedockwidget.h" 3 | #include "settingsdialog.h" 4 | #include "translation-service/httpserver.h" 5 | #include "utils/config-data.h" 6 | 7 | TranslateDockWidget::TranslateDockWidget(QWidget *parent) 8 | : QDockWidget(parent), ui(new Ui::TranslateDockWidget) 9 | { 10 | ui->setupUi(this); 11 | 12 | ui->errorLabel->hide(); 13 | ui->label_status->hide(); 14 | this->updateErrorLabel(global_context.error_message); 15 | 16 | // connect the settings button to the settings dialog 17 | connect(ui->settings, &QPushButton::clicked, this, 18 | &TranslateDockWidget::openSettingsDialog); 19 | // connect the startHTTPServer button to the startHTTPServer function 20 | connect(ui->startStopHTTPServer, &QPushButton::clicked, this, [=]() { 21 | // if the button is not checked, start the server 22 | if (this->ui->startStopHTTPServer->isChecked()) 23 | start_http_server(); 24 | // otherwise, stop the server 25 | else 26 | stop_http_server(); 27 | }); 28 | } 29 | 30 | TranslateDockWidget::~TranslateDockWidget() 31 | { 32 | delete ui; 33 | } 34 | 35 | void TranslateDockWidget::openSettingsDialog() 36 | { 37 | // create the settings dialog 38 | SettingsDialog *settingsDialog = new SettingsDialog(this); 39 | // show the settings dialog 40 | settingsDialog->show(); 41 | } 42 | 43 | void TranslateDockWidget::updateStatusLabel(const std::string &message) 44 | { 45 | if (message.empty()) { 46 | ui->label_status->hide(); 47 | return; 48 | } 49 | ui->label_status->setText(QString::fromStdString(message)); 50 | } 51 | 52 | void TranslateDockWidget::updateErrorLabel(const std::string &error_message) 53 | { 54 | // if there is an error message, show the error label 55 | if (!error_message.empty()) { 56 | // write the error text in red 57 | ui->errorLabel->setText(QString("%1") 58 | .arg(QString::fromStdString(error_message))); 59 | // show the error label 60 | ui->errorLabel->show(); 61 | // disable the start/stop http server button 62 | ui->startStopHTTPServer->setEnabled(false); 63 | } else { 64 | ui->errorLabel->hide(); 65 | // enable the start/stop http server button 66 | ui->startStopHTTPServer->setEnabled(true); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/utils/config-data.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYGLOT_CONFIG_DATA_H 2 | #define POLYGLOT_CONFIG_DATA_H 3 | 4 | #ifdef __cplusplus 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | struct polyglot_config_data { 11 | // model selection (0: none, 1: custom, 2+ preset models) 12 | int model_selection; 13 | // local model path 14 | std::string local_model_path; 15 | // local spm path 16 | std::string local_spm_path; 17 | 18 | // max output tokens 19 | uint16_t max_output_tokens; 20 | 21 | // http server port 22 | uint16_t http_server_port; 23 | 24 | // For Future Use: 25 | // local or cloud 26 | bool local; 27 | // cloud model name 28 | std::string cloud_model_name; 29 | // cloud API key 30 | std::string cloud_api_key; 31 | }; 32 | 33 | // forward declaration 34 | namespace ctranslate2 { 35 | class TranslationOptions; 36 | class Translator; 37 | } 38 | namespace sentencepiece { 39 | class SentencePieceProcessor; 40 | } 41 | namespace httplib { 42 | class Server; 43 | } 44 | 45 | struct polyglot_global_context { 46 | // error message 47 | std::string error_message; 48 | // status message 49 | std::string status_message; 50 | // ctranslate2 options 51 | ctranslate2::TranslationOptions *options; 52 | // ctranslate2 translator 53 | ctranslate2::Translator *translator; 54 | // sentencepiece processor 55 | sentencepiece::SentencePieceProcessor *processor; 56 | // tokenizer 57 | std::function(const std::string &)> tokenizer; 58 | // detokenizer 59 | std::function &)> detokenizer; 60 | // error callback 61 | std::function error_callback; 62 | // status callback 63 | std::function status_callback; 64 | // http server 65 | httplib::Server *svr; 66 | }; 67 | 68 | extern polyglot_config_data global_config; 69 | extern polyglot_global_context global_context; 70 | 71 | #endif // __cplusplus 72 | 73 | #define OBS_POLYGLOT_CONFIG_FAIL -1 74 | #define OBS_POLYGLOT_CONFIG_SUCCESS 0 75 | 76 | #ifdef __cplusplus 77 | extern "C" { 78 | #endif 79 | 80 | void resetContext(bool resetCallbacks); 81 | void freeContext(bool resetCallbacks); 82 | int saveConfig(bool create_if_not_exist); 83 | int loadConfig(); 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | 89 | #endif // POLYGLOT_CONFIG_DATA_H 90 | -------------------------------------------------------------------------------- /.github/actions/run-cmake-format/action.yaml: -------------------------------------------------------------------------------- 1 | name: Run cmake-format 2 | description: Runs cmake-format and checks for any changes introduced by it 3 | inputs: 4 | failCondition: 5 | description: Controls whether failed checks also fail the workflow run 6 | required: false 7 | default: 'never' 8 | workingDirectory: 9 | description: Working directory for checks 10 | required: false 11 | default: ${{ github.workspace }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Check Runner Operating System 🏃‍♂️ 16 | if: runner.os == 'Windows' 17 | shell: bash 18 | run: | 19 | : Check Runner Operating System 🏃‍♂️ 20 | echo "::notice::run-cmake-format action requires a macOS-based or Linux-based runner." 21 | exit 2 22 | 23 | - name: Install Dependencies 🛍️ 24 | if: runner.os == 'Linux' 25 | shell: bash 26 | run: | 27 | : Install Dependencies 🛍️ 28 | echo ::group::Install Dependencies 29 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 30 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 31 | brew install --quiet zsh 32 | echo ::endgroup:: 33 | 34 | - name: Run cmake-format 🎛️ 35 | id: result 36 | shell: zsh --no-rcs --errexit --pipefail {0} 37 | working-directory: ${{ github.workspace }} 38 | env: 39 | GITHUB_EVENT_FORCED: ${{ github.event.forced }} 40 | GITHUB_REF_BEFORE: ${{ github.event.before }} 41 | run: | 42 | : Run cmake-format 🎛️ 43 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 44 | 45 | local -a changes=($(git diff --name-only HEAD~1 HEAD)) 46 | case ${GITHUB_EVENT_NAME} { 47 | pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;; 48 | push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;; 49 | *) ;; 50 | } 51 | 52 | if (( ${changes[(I)*.cmake|*CMakeLists.txt]} )) { 53 | echo ::group::Install cmakelang 54 | pip3 install cmakelang 55 | echo ::endgroup:: 56 | echo ::group::Run cmake-format 57 | ./build-aux/run-cmake-format --fail-${{ inputs.failCondition }} --check 58 | echo ::endgroup:: 59 | } 60 | -------------------------------------------------------------------------------- /cmake/macos/compilerconfig.cmake: -------------------------------------------------------------------------------- 1 | # CMake macOS compiler configuration module 2 | 3 | include_guard(GLOBAL) 4 | 5 | include(ccache) 6 | include(compiler_common) 7 | 8 | add_compile_options(-fopenmp-simd) 9 | 10 | if(XCODE) 11 | # Use Xcode's standard architecture selection 12 | set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)") 13 | # Enable dSYM generation for Release builds 14 | string(APPEND CMAKE_C_FLAGS_RELEASE " -g") 15 | string(APPEND CMAKE_CXX_FLAGS_RELEASE " -g") 16 | else() 17 | option(ENABLE_COMPILER_TRACE "Enable clang time-trace (requires Ninja)" OFF) 18 | mark_as_advanced(ENABLE_COMPILER_TRACE) 19 | 20 | # clang options for ObjC 21 | set(_obs_clang_objc_options 22 | # cmake-format: sortable 23 | -Werror=block-capture-autoreleasing -Wno-selector -Wno-strict-selector-match -Wnon-virtual-dtor -Wprotocol 24 | -Wundeclared-selector) 25 | 26 | # clang options for ObjC++ 27 | set(_obs_clang_objcxx_options 28 | # cmake-format: sortable 29 | ${_obs_clang_objc_options} -Warc-repeated-use-of-weak -Wno-arc-maybe-repeated-use-of-weak) 30 | 31 | add_compile_options( 32 | "$<$:${_obs_clang_c_options}>" "$<$:${_obs_clang_cxx_options}>" 33 | "$<$:${_obs_clang_objc_options}>" 34 | "$<$:${_obs_clang_objcxx_options}>") 35 | 36 | # Enable stripping of dead symbols when not building for Debug configuration 37 | set(_release_configs RelWithDebInfo Release MinSizeRel) 38 | if(CMAKE_BUILD_TYPE IN_LIST _release_configs) 39 | add_link_options(LINKER:-dead_strip) 40 | endif() 41 | 42 | # Enable color diagnostics for AppleClang 43 | set(CMAKE_COLOR_DIAGNOSTICS ON) 44 | # Set universal architectures via CMake flag for non-Xcode generators 45 | set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") 46 | 47 | # Enable compiler and build tracing (requires Ninja generator) 48 | if(ENABLE_COMPILER_TRACE AND CMAKE_GENERATOR STREQUAL "Ninja") 49 | add_compile_options($<$:-ftime-trace> $<$:-ftime-trace>) 50 | else() 51 | set(ENABLE_COMPILER_TRACE 52 | OFF 53 | CACHE STRING "Enable clang time-trace (requires Ninja)" FORCE) 54 | endif() 55 | endif() 56 | 57 | add_compile_definitions($<$:DEBUG> $<$:_DEBUG> SIMDE_ENABLE_OPENMP) 58 | -------------------------------------------------------------------------------- /.github/actions/run-clang-format/action.yaml: -------------------------------------------------------------------------------- 1 | name: Run clang-format 2 | description: Runs clang-format and checks for any changes introduced by it 3 | inputs: 4 | failCondition: 5 | description: Controls whether failed checks also fail the workflow run 6 | required: false 7 | default: 'never' 8 | workingDirectory: 9 | description: Working directory for checks 10 | required: false 11 | default: ${{ github.workspace }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Check Runner Operating System 🏃‍♂️ 16 | if: runner.os == 'Windows' 17 | shell: bash 18 | run: | 19 | : Check Runner Operating System 🏃‍♂️ 20 | echo "::notice::run-clang-format action requires a macOS-based or Linux-based runner." 21 | exit 2 22 | 23 | - name: Install Dependencies 🛍️ 24 | if: runner.os == 'Linux' 25 | shell: bash 26 | run: | 27 | : Install Dependencies 🛍️ 28 | echo ::group::Install Dependencies 29 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 30 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 31 | echo "/home/linuxbrew/.linuxbrew/opt/clang-format@13/bin" >> $GITHUB_PATH 32 | brew install --quiet zsh 33 | echo ::endgroup:: 34 | 35 | - name: Run clang-format 🐉 36 | id: result 37 | shell: zsh --no-rcs --errexit --pipefail {0} 38 | working-directory: ${{ inputs.workingDirectory }} 39 | env: 40 | GITHUB_EVENT_FORCED: ${{ github.event.forced }} 41 | GITHUB_REF_BEFORE: ${{ github.event.before }} 42 | run: | 43 | : Run clang-format 🐉 44 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 45 | 46 | local -a changes=($(git diff --name-only HEAD~1 HEAD)) 47 | case ${GITHUB_EVENT_NAME} { 48 | pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;; 49 | push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;; 50 | *) ;; 51 | } 52 | 53 | if (( ${changes[(I)(*.c|*.h|*.cpp|*.hpp|*.m|*.mm)]} )) { 54 | echo ::group::Install clang-format-13 55 | brew install --quiet obsproject/tools/clang-format@13 56 | echo ::endgroup:: 57 | 58 | echo ::group::Run clang-format-13 59 | ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check 60 | echo ::endgroup:: 61 | } 62 | -------------------------------------------------------------------------------- /cmake/common/compiler_common.cmake: -------------------------------------------------------------------------------- 1 | # CMake common compiler options module 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Set C and C++ language standards to C17 and C++17 6 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) 7 | set(CMAKE_C_STANDARD 17) 8 | else() 9 | set(CMAKE_C_STANDARD 11) 10 | endif() 11 | set(CMAKE_C_STANDARD_REQUIRED TRUE) 12 | set(CMAKE_CXX_STANDARD 17) 13 | set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 14 | 15 | # Set symbols to be hidden by default for C and C++ 16 | set(CMAKE_C_VISIBILITY_PRESET hidden) 17 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 18 | set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) 19 | 20 | # clang options for C 21 | set(_obs_clang_c_options 22 | # cmake-format: sortable 23 | -fno-strict-aliasing 24 | -Wbool-conversion 25 | -Wcomma 26 | -Wconstant-conversion 27 | -Wdeprecated-declarations 28 | -Wempty-body 29 | -Wenum-conversion 30 | -Werror=return-type 31 | -Wextra 32 | -Wformat 33 | -Wformat-security 34 | -Wfour-char-constants 35 | -Winfinite-recursion 36 | -Wint-conversion 37 | -Wno-conversion 38 | -Wno-error=newline-eof 39 | -Wno-float-conversion 40 | -Wno-implicit-fallthrough 41 | -Wno-missing-braces 42 | -Wno-missing-field-initializers 43 | -Wno-missing-prototypes 44 | -Wno-semicolon-before-method-body 45 | -Wno-shadow 46 | -Wno-sign-conversion 47 | -Wno-strict-prototypes 48 | -Wno-trigraphs 49 | -Wno-unknown-pragmas 50 | -Wno-unused-function 51 | -Wno-unused-label 52 | -Wnon-literal-null-conversion 53 | -Wobjc-literal-conversion 54 | -Wparentheses 55 | -Wpointer-sign 56 | -Wquoted-include-in-framework-header 57 | -Wshadow 58 | -Wshorten-64-to-32 59 | -Wuninitialized 60 | -Wunreachable-code 61 | -Wunused-parameter 62 | -Wunused-value 63 | -Wunused-variable 64 | -Wvla) 65 | 66 | # clang options for C++ 67 | set(_obs_clang_cxx_options 68 | # cmake-format: sortable 69 | ${_obs_clang_c_options} 70 | -Wconversion 71 | -Wdeprecated-implementations 72 | -Wduplicate-method-match 73 | -Wfloat-conversion 74 | -Wfour-char-constants 75 | -Wimplicit-retain-self 76 | -Winvalid-offsetof 77 | -Wmove 78 | -Wno-c++11-extensions 79 | -Wno-exit-time-destructors 80 | -Wno-implicit-atomic-properties 81 | -Wno-objc-interface-ivars 82 | -Wno-overloaded-virtual 83 | -Wrange-loop-analysis) 84 | 85 | if(NOT DEFINED CMAKE_COMPILE_WARNING_AS_ERROR) 86 | set(CMAKE_COMPILE_WARNING_AS_ERROR ON) 87 | endif() 88 | -------------------------------------------------------------------------------- /cmake/windows/resources/installer-Windows.iss.in: -------------------------------------------------------------------------------- 1 | #define MyAppName "@CMAKE_PROJECT_NAME@" 2 | #define MyAppVersion "@CMAKE_PROJECT_VERSION@" 3 | #define MyAppPublisher "@PLUGIN_AUTHOR@" 4 | #define MyAppURL "@PLUGIN_WEBSITE@" 5 | 6 | [Setup] 7 | ; NOTE: The value of AppId uniquely identifies this application. 8 | ; Do not use the same AppId value in installers for other applications. 9 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 10 | AppId={{@UUID_APP@} 11 | AppName={#MyAppName} 12 | AppVersion={#MyAppVersion} 13 | AppPublisher={#MyAppPublisher} 14 | AppPublisherURL={#MyAppURL} 15 | AppSupportURL={#MyAppURL} 16 | AppUpdatesURL={#MyAppURL} 17 | DefaultDirName={code:GetDirName} 18 | DefaultGroupName={#MyAppName} 19 | OutputBaseFilename={#MyAppName}-{#MyAppVersion}-Windows-Installer 20 | Compression=lzma 21 | SolidCompression=yes 22 | DirExistsWarning=no 23 | 24 | [Languages] 25 | Name: "english"; MessagesFile: "compiler:Default.isl" 26 | 27 | [Files] 28 | Source: "..\release\Package\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 29 | Source: "..\LICENSE"; Flags: dontcopy 30 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 31 | 32 | [Icons] 33 | Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}" 34 | Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" 35 | 36 | [Code] 37 | procedure InitializeWizard(); 38 | var 39 | GPLText: AnsiString; 40 | Page: TOutputMsgMemoWizardPage; 41 | begin 42 | ExtractTemporaryFile('LICENSE'); 43 | LoadStringFromFile(ExpandConstant('{tmp}\LICENSE'), GPLText); 44 | Page := CreateOutputMsgMemoPage(wpWelcome, 45 | 'License Information', 'Please review the license terms before installing {#MyAppName}', 46 | 'Press Page Down to see the rest of the agreement. Once you are aware of your rights, click Next to continue.', 47 | String(GPLText) 48 | ); 49 | end; 50 | 51 | // credit where it's due : 52 | // following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45 53 | function GetDirName(Value: string): string; 54 | var 55 | InstallPath: string; 56 | begin 57 | // initialize default path, which will be returned when the following registry 58 | // key queries fail due to missing keys or for some different reason 59 | Result := '{autopf}\obs-studio'; 60 | // query the first registry value; if this succeeds, return the obtained value 61 | if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then 62 | Result := InstallPath 63 | end; 64 | 65 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Install-BuildDependencies.ps1: -------------------------------------------------------------------------------- 1 | function Install-BuildDependencies { 2 | <# 3 | .SYNOPSIS 4 | Installs required build dependencies. 5 | .DESCRIPTION 6 | Additional packages might be needed for successful builds. This module contains additional 7 | dependencies available for installation via winget and, if possible, adds their locations 8 | to the environment path for future invocation. 9 | .EXAMPLE 10 | Install-BuildDependencies 11 | #> 12 | 13 | param( 14 | [string] $WingetFile = "$PSScriptRoot/.Wingetfile" 15 | ) 16 | 17 | if ( ! ( Test-Path function:Log-Warning ) ) { 18 | . $PSScriptRoot/Logger.ps1 19 | } 20 | 21 | $Prefixes = @{ 22 | 'x64' = ${Env:ProgramFiles} 23 | 'x86' = ${Env:ProgramFiles(x86)} 24 | 'arm64' = ${Env:ProgramFiles(arm)} 25 | } 26 | 27 | $Paths = $Env:Path -split [System.IO.Path]::PathSeparator 28 | 29 | $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements') 30 | 31 | if ( $script:Quiet ) { 32 | $WingetOptions += '--silent' 33 | } 34 | 35 | Log-Group 'Check Windows build requirements' 36 | Get-Content $WingetFile | ForEach-Object { 37 | $_, $Package, $_, $Path, $_, $Binary, $_, $Version = $_ -replace ',','' -split " +(?=(?:[^\']*\'[^\']*\')*[^\']*$)" -replace "'",'' 38 | 39 | $Prefixes.GetEnumerator() | ForEach-Object { 40 | $Prefix = $_.value 41 | $FullPath = "${Prefix}\${Path}" 42 | if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) { 43 | $Paths = @($FullPath) + $Paths 44 | $Env:Path = $Paths -join [System.IO.Path]::PathSeparator 45 | } 46 | } 47 | 48 | Log-Debug "Checking for command ${Binary}" 49 | $Found = Get-Command -ErrorAction SilentlyContinue $Binary 50 | 51 | if ( $Found ) { 52 | Log-Status "Found dependency ${Binary} as $($Found.Source)" 53 | } else { 54 | Log-Status "Installing package ${Package} $(if ( $Version -ne $null ) { "Version: ${Version}" } )" 55 | 56 | if ( $Version -ne $null ) { 57 | $WingetOptions += @('--version', ${Version}) 58 | } 59 | 60 | try { 61 | $Params = $WingetOptions + $Package 62 | 63 | winget @Params 64 | } catch { 65 | throw "Error while installing winget package ${Package}: $_" 66 | } 67 | } 68 | } 69 | Log-Group 70 | } 71 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Expand-ArchiveExt.ps1: -------------------------------------------------------------------------------- 1 | function Expand-ArchiveExt { 2 | <# 3 | .SYNOPSIS 4 | Expands archive files. 5 | .DESCRIPTION 6 | Allows extraction of zip, 7z, gz, and xz archives. 7 | Requires tar and 7-zip to be available on the system. 8 | Archives ending with .zip but created using LZMA compression are 9 | expanded using 7-zip as a fallback. 10 | .EXAMPLE 11 | Expand-ArchiveExt -Path 12 | Expand-ArchiveExt -Path -DestinationPath 13 | #> 14 | 15 | param( 16 | [Parameter(Mandatory)] 17 | [string] $Path, 18 | [string] $DestinationPath = [System.IO.Path]::GetFileNameWithoutExtension($Path), 19 | [switch] $Force 20 | ) 21 | 22 | switch ( [System.IO.Path]::GetExtension($Path) ) { 23 | .zip { 24 | try { 25 | Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force:$Force 26 | } catch { 27 | if ( Get-Command 7z ) { 28 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 29 | } else { 30 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 31 | } 32 | } 33 | break 34 | } 35 | { ( $_ -eq ".7z" ) -or ( $_ -eq ".exe" ) } { 36 | if ( Get-Command 7z ) { 37 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 38 | } else { 39 | throw "Extraction utility 7-zip not found. Please install 7-zip first." 40 | } 41 | break 42 | } 43 | .gz { 44 | try { 45 | Invoke-External tar -x -o $DestinationPath -f $Path 46 | } catch { 47 | if ( Get-Command 7z ) { 48 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 49 | } else { 50 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 51 | } 52 | } 53 | break 54 | } 55 | .xz { 56 | try { 57 | Invoke-External tar -x -o $DestinationPath -f $Path 58 | } catch { 59 | if ( Get-Command 7z ) { 60 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 61 | } else { 62 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 63 | } 64 | } 65 | } 66 | default { 67 | throw "Unsupported archive extension provided." 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cmake/linux/helpers.cmake: -------------------------------------------------------------------------------- 1 | # CMake Linux helper functions module 2 | 3 | include_guard(GLOBAL) 4 | 5 | include(helpers_common) 6 | 7 | # set_target_properties_plugin: Set target properties for use in obs-studio 8 | function(set_target_properties_plugin target) 9 | set(options "") 10 | set(oneValueArgs "") 11 | set(multiValueArgs PROPERTIES) 12 | cmake_parse_arguments(PARSE_ARGV 0 _STPO "${options}" "${oneValueArgs}" "${multiValueArgs}") 13 | 14 | message(DEBUG "Setting additional properties for target ${target}...") 15 | 16 | while(_STPO_PROPERTIES) 17 | list(POP_FRONT _STPO_PROPERTIES key value) 18 | set_property(TARGET ${target} PROPERTY ${key} "${value}") 19 | endwhile() 20 | 21 | set_target_properties( 22 | ${target} 23 | PROPERTIES VERSION 0 24 | SOVERSION ${PLUGIN_VERSION} 25 | PREFIX "") 26 | 27 | install( 28 | TARGETS ${target} 29 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 30 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/obs-plugins) 31 | 32 | if(TARGET plugin-support) 33 | target_link_libraries(${target} PRIVATE plugin-support) 34 | endif() 35 | 36 | target_install_resources(${target}) 37 | 38 | get_target_property(target_sources ${target} SOURCES) 39 | set(target_ui_files ${target_sources}) 40 | list(FILTER target_ui_files INCLUDE REGEX ".+\\.(ui|qrc)") 41 | source_group( 42 | TREE "${CMAKE_CURRENT_SOURCE_DIR}" 43 | PREFIX "UI Files" 44 | FILES ${target_ui_files}) 45 | endfunction() 46 | 47 | # Helper function to add resources into bundle 48 | function(target_install_resources target) 49 | message(DEBUG "Installing resources for target ${target}...") 50 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") 51 | file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") 52 | foreach(data_file IN LISTS data_files) 53 | cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE 54 | relative_path) 55 | cmake_path(GET relative_path PARENT_PATH relative_path) 56 | target_sources(${target} PRIVATE "${data_file}") 57 | source_group("Resources/${relative_path}" FILES "${data_file}") 58 | endforeach() 59 | 60 | install( 61 | DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" 62 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/obs/obs-plugins/${target} 63 | USE_SOURCE_PERMISSIONS) 64 | endif() 65 | endfunction() 66 | 67 | # Helper function to add a specific resource to a bundle 68 | function(target_add_resource target resource) 69 | message(DEBUG "Add resource '${resource}' to target ${target} at destination '${target_destination}'...") 70 | 71 | install(FILES "${resource}" DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/obs/obs-plugins/${target}) 72 | 73 | source_group("Resources" FILES "${resource}") 74 | endfunction() 75 | -------------------------------------------------------------------------------- /cmake/linux/defaults.cmake: -------------------------------------------------------------------------------- 1 | # CMake Linux defaults module 2 | 3 | # cmake-format: off 4 | # cmake-lint: disable=C0103 5 | # cmake-lint: disable=C0111 6 | # cmake-format: on 7 | 8 | include_guard(GLOBAL) 9 | 10 | include(GNUInstallDirs) 11 | 12 | # Enable find_package targets to become globally available targets 13 | set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE) 14 | 15 | set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}") 16 | set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}") 17 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_C_LIBRARY_ARCHITECTURE}") 18 | 19 | set(CPACK_GENERATOR "DEB") 20 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) 21 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PLUGIN_EMAIL}") 22 | set(CPACK_SET_DESTDIR ON) 23 | 24 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.25.0 OR NOT CMAKE_CROSSCOMPILING) 25 | set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON) 26 | endif() 27 | 28 | set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/release") 29 | 30 | set(CPACK_SOURCE_GENERATOR "TXZ") 31 | set(CPACK_SOURCE_IGNORE_FILES 32 | # cmake-format: sortable 33 | ".*~$" 34 | \\.git/ 35 | \\.github/ 36 | \\.gitignore 37 | build_.* 38 | cmake/\\.CMakeBuildNumber 39 | release/) 40 | 41 | set(CPACK_VERBATIM_VARIABLES YES) 42 | set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-source") 43 | set(CPACK_ARCHIVE_THREADS 0) 44 | 45 | include(CPack) 46 | 47 | find_package(libobs QUIET) 48 | 49 | if(NOT TARGET OBS::libobs) 50 | find_package(LibObs REQUIRED) 51 | add_library(OBS::libobs ALIAS libobs) 52 | 53 | if(ENABLE_FRONTEND_API) 54 | find_path( 55 | obs-frontend-api_INCLUDE_DIR 56 | NAMES obs-frontend-api.h 57 | PATHS /usr/include /usr/local/include 58 | PATH_SUFFIXES obs) 59 | 60 | find_library( 61 | obs-frontend-api_LIBRARY 62 | NAMES obs-frontend-api 63 | PATHS /usr/lib /usr/local/lib) 64 | 65 | if(obs-frontend-api_LIBRARY) 66 | if(NOT TARGET OBS::obs-frontend-api) 67 | if(IS_ABSOLUTE "${obs-frontend-api_LIBRARY}") 68 | add_library(OBS::obs-frontend-api UNKNOWN IMPORTED) 69 | set_property(TARGET OBS::obs-frontend-api PROPERTY IMPORTED_LOCATION "${obs-frontend-api_LIBRARY}") 70 | else() 71 | add_library(OBS::obs-frontend-api INTERFACE IMPORTED) 72 | set_property(TARGET OBS::obs-frontend-api PROPERTY IMPORTED_LIBNAME "${obs-frontend-api_LIBRARY}") 73 | endif() 74 | 75 | set_target_properties(OBS::obs-frontend-api PROPERTIES INTERFACE_INCLUDE_DIRECTORIES 76 | "${obs-frontend-api_INCLUDE_DIR}") 77 | endif() 78 | endif() 79 | endif() 80 | 81 | macro(find_package) 82 | if(NOT "${ARGV0}" STREQUAL libobs AND NOT "${ARGV0}" STREQUAL obs-frontend-api) 83 | _find_package(${ARGV}) 84 | endif() 85 | endmacro() 86 | endif() 87 | -------------------------------------------------------------------------------- /cmake/common/bootstrap.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16...3.26) 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Enable automatic PUSH and POP of policies to parent scope 6 | if(POLICY CMP0011) 7 | cmake_policy(SET CMP0011 NEW) 8 | endif() 9 | 10 | # Enable distinction between Clang and AppleClang 11 | if(POLICY CMP0025) 12 | cmake_policy(SET CMP0025 NEW) 13 | endif() 14 | 15 | # Enable strict checking of "break()" usage 16 | if(POLICY CMP0055) 17 | cmake_policy(SET CMP0055 NEW) 18 | endif() 19 | 20 | # Honor visibility presets for all target types (executable, shared, module, static) 21 | if(POLICY CMP0063) 22 | cmake_policy(SET CMP0063 NEW) 23 | endif() 24 | 25 | # Disable export function calls to populate package registry by default 26 | if(POLICY CMP0090) 27 | cmake_policy(SET CMP0090 NEW) 28 | endif() 29 | 30 | # Prohibit in-source builds 31 | if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") 32 | message(FATAL_ERROR "In-source builds are not supported. " 33 | "Specify a build directory via 'cmake -S -B ' instead.") 34 | file(REMOVE_RECURSE "${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt" "${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles") 35 | endif() 36 | 37 | # Use folders for source file organization with IDE generators (Visual Studio/Xcode) 38 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 39 | 40 | # Add common module directories to default search path 41 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/common") 42 | 43 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/buildspec.json" buildspec) 44 | 45 | # cmake-format: off 46 | string(JSON _name GET ${buildspec} name) 47 | string(JSON _website GET ${buildspec} website) 48 | string(JSON _author GET ${buildspec} author) 49 | string(JSON _email GET ${buildspec} email) 50 | string(JSON _version GET ${buildspec} version) 51 | string(JSON _bundleId GET ${buildspec} platformConfig macos bundleId) 52 | string(JSON _macosPackageUUID GET ${buildspec} uuids macosPackage) 53 | string(JSON _macosInstallerUUID GET ${buildspec} uuids macosInstaller) 54 | string(JSON _windowsAppUUID GET ${buildspec} uuids windowsApp) 55 | # cmake-format: on 56 | 57 | set(PLUGIN_AUTHOR ${_author}) 58 | set(PLUGIN_WEBSITE ${_website}) 59 | set(PLUGIN_EMAIL ${_email}) 60 | set(PLUGIN_VERSION ${_version}) 61 | set(MACOS_BUNDLEID ${_bundleId}) 62 | 63 | include(buildnumber) 64 | include(osconfig) 65 | 66 | # Allow selection of common build types via UI 67 | if(NOT CMAKE_BUILD_TYPE) 68 | set(CMAKE_BUILD_TYPE 69 | "RelWithDebInfo" 70 | CACHE STRING "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" FORCE) 71 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel) 72 | endif() 73 | 74 | # Disable exports automatically going into the CMake package registry 75 | set(CMAKE_EXPORT_PACKAGE_REGISTRY FALSE) 76 | # Enable default inclusion of targets' source and binary directory 77 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 78 | -------------------------------------------------------------------------------- /cmake/linux/compilerconfig.cmake: -------------------------------------------------------------------------------- 1 | # CMake Linux compiler configuration module 2 | 3 | include_guard(GLOBAL) 4 | 5 | include(ccache) 6 | include(compiler_common) 7 | 8 | option(ENABLE_COMPILER_TRACE "Enable Clang time-trace (required Clang and Ninja)" OFF) 9 | mark_as_advanced(ENABLE_COMPILER_TRACE) 10 | 11 | # gcc options for C 12 | set(_obs_gcc_c_options 13 | # cmake-format: sortable 14 | -fno-strict-aliasing 15 | -fopenmp-simd 16 | -Wempty-body 17 | -Wenum-conversion 18 | -Werror=return-type 19 | -Wextra 20 | -Wformat 21 | -Wformat-security 22 | -Wno-conversion 23 | -Wno-deprecated-declarations 24 | -Wno-error=deprecated-declarations 25 | -Wno-float-conversion 26 | -Wno-implicit-fallthrough 27 | -Wno-missing-braces 28 | -Wno-missing-field-initializers 29 | -Wno-shadow 30 | -Wno-sign-conversion 31 | -Wno-trigraphs 32 | -Wno-unknown-pragmas 33 | -Wno-unused-function 34 | -Wno-unused-label 35 | -Wparentheses 36 | -Wshadow 37 | -Wuninitialized 38 | -Wunreachable-code 39 | -Wunused-parameter 40 | -Wunused-value 41 | -Wunused-variable 42 | -Wvla) 43 | 44 | # gcc options for C++ 45 | set(_obs_gcc_cxx_options 46 | # cmake-format: sortable 47 | ${_obs_gcc_c_options} -Wconversion -Wfloat-conversion -Winvalid-offsetof -Wno-overloaded-virtual) 48 | 49 | add_compile_options( 50 | -fopenmp-simd 51 | "$<$:${_obs_gcc_c_options}>" 52 | "$<$:-Wint-conversion;-Wno-missing-prototypes;-Wno-strict-prototypes;-Wpointer-sign>" 53 | "$<$:${_obs_gcc_cxx_options}>" 54 | "$<$:${_obs_clang_c_options}>" 55 | "$<$:${_obs_clang_cxx_options}>") 56 | 57 | # Add support for color diagnostics and CMake switch for warnings as errors to CMake < 3.24 58 | if(CMAKE_VERSION VERSION_LESS 3.24.0) 59 | add_compile_options($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) 60 | if(CMAKE_COMPILE_WARNING_AS_ERROR) 61 | add_compile_options(-Werror) 62 | endif() 63 | else() 64 | set(CMAKE_COLOR_DIAGNOSTICS ON) 65 | endif() 66 | 67 | if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) 68 | # Disable false-positive warning in GCC 12.1.0 and later 69 | add_compile_options(-Wno-error=maybe-uninitialized) 70 | 71 | # Add warning for infinite recursion (added in GCC 12) 72 | if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0) 73 | add_compile_options(-Winfinite-recursion) 74 | endif() 75 | endif() 76 | 77 | # Enable compiler and build tracing (requires Ninja generator) 78 | if(ENABLE_COMPILER_TRACE AND CMAKE_GENERATOR STREQUAL "Ninja") 79 | add_compile_options($<$:-ftime-trace> $<$:-ftime-trace>) 80 | else() 81 | set(ENABLE_COMPILER_TRACE 82 | OFF 83 | CACHE STRING "Enable Clang time-trace (required Clang and Ninja)" FORCE) 84 | endif() 85 | 86 | add_compile_definitions($<$:DEBUG> $<$:_DEBUG> SIMDE_ENABLE_OPENMP) 87 | -------------------------------------------------------------------------------- /cmake/BuildSentencepiece.cmake: -------------------------------------------------------------------------------- 1 | # build sentencepiece from "https://github.com/google/sentencepiece.git" 2 | 3 | if(APPLE) 4 | 5 | include(FetchContent) 6 | 7 | FetchContent_Declare( 8 | sentencepiece_fetch 9 | URL https://github.com/occ-ai/obs-ai-ctranslate2-dep/releases/download/1.1.1/libsentencepiece-macos-Release-1.1.1.tar.gz 10 | URL_HASH SHA256=c911f1e84ea94925a8bc3fd3257185b2e18395075509c8659cc7003a979e0b32) 11 | FetchContent_MakeAvailable(sentencepiece_fetch) 12 | add_library(sentencepiece INTERFACE) 13 | target_link_libraries(sentencepiece INTERFACE ${sentencepiece_fetch_SOURCE_DIR}/lib/libsentencepiece.a) 14 | set_target_properties(sentencepiece PROPERTIES INTERFACE_INCLUDE_DIRECTORIES 15 | ${sentencepiece_fetch_SOURCE_DIR}/include) 16 | elseif(WIN32) 17 | 18 | FetchContent_Declare( 19 | sentencepiece_fetch 20 | URL https://github.com/occ-ai/obs-ai-ctranslate2-dep/releases/download/1.1.1/sentencepiece-windows-0.2.0-Release.zip 21 | URL_HASH SHA256=846699c7fa1e8918b71ed7f2bd5cd60e47e51105e1d84e3192919b4f0f10fdeb) 22 | FetchContent_MakeAvailable(sentencepiece_fetch) 23 | add_library(sentencepiece INTERFACE) 24 | target_link_libraries(sentencepiece INTERFACE ${sentencepiece_fetch_SOURCE_DIR}/lib/sentencepiece.lib) 25 | set_target_properties(sentencepiece PROPERTIES INTERFACE_INCLUDE_DIRECTORIES 26 | ${sentencepiece_fetch_SOURCE_DIR}/include) 27 | 28 | else() 29 | 30 | set(SP_URL 31 | "https://github.com/google/sentencepiece.git" 32 | CACHE STRING "URL of sentencepiece repository") 33 | 34 | set(SP_CMAKE_OPTIONS -DSPM_ENABLE_SHARED=OFF) 35 | set(SENTENCEPIECE_INSTALL_LIB_LOCATION lib/${CMAKE_STATIC_LIBRARY_PREFIX}sentencepiece${CMAKE_STATIC_LIBRARY_SUFFIX}) 36 | 37 | include(ExternalProject) 38 | 39 | ExternalProject_Add( 40 | sentencepiece_build 41 | GIT_REPOSITORY ${SP_URL} 42 | GIT_TAG v0.1.99 43 | BUILD_COMMAND ${CMAKE_COMMAND} --build --config ${CMAKE_BUILD_TYPE} 44 | CMAKE_GENERATOR ${CMAKE_GENERATOR} 45 | INSTALL_COMMAND ${CMAKE_COMMAND} --install --config ${CMAKE_BUILD_TYPE} 46 | BUILD_BYPRODUCTS /${SENTENCEPIECE_INSTALL_LIB_LOCATION} 47 | CMAKE_ARGS -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} -DCMAKE_INSTALL_PREFIX= 48 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} ${SP_CMAKE_OPTIONS}) 49 | ExternalProject_Get_Property(sentencepiece_build INSTALL_DIR) 50 | 51 | add_library(libsentencepiece STATIC IMPORTED GLOBAL) 52 | add_dependencies(libsentencepiece sentencepiece_build) 53 | set_target_properties(libsentencepiece PROPERTIES IMPORTED_LOCATION 54 | ${INSTALL_DIR}/${SENTENCEPIECE_INSTALL_LIB_LOCATION}) 55 | 56 | add_library(sentencepiece INTERFACE) 57 | add_dependencies(sentencepiece libsentencepiece) 58 | target_link_libraries(sentencepiece INTERFACE libsentencepiece) 59 | target_include_directories(sentencepiece INTERFACE ${INSTALL_DIR}/include) 60 | 61 | endif() 62 | -------------------------------------------------------------------------------- /cmake/BuildMyCurl.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | set(LibCurl_VERSION "8.4.0-3") 4 | set(LibCurl_BASEURL "https://github.com/occ-ai/obs-ai-libcurl-dep/releases/download/${LibCurl_VERSION}") 5 | 6 | if(${CMAKE_BUILD_TYPE} STREQUAL Release OR ${CMAKE_BUILD_TYPE} STREQUAL RelWithDebInfo) 7 | set(LibCurl_BUILD_TYPE Release) 8 | else() 9 | set(LibCurl_BUILD_TYPE Debug) 10 | endif() 11 | 12 | if(APPLE) 13 | if(LibCurl_BUILD_TYPE STREQUAL Release) 14 | set(LibCurl_URL "${LibCurl_BASEURL}/libcurl-macos-${LibCurl_VERSION}-Release.tar.gz") 15 | set(LibCurl_HASH SHA256=5ef7bfed2c2bca17ba562aede6a3c3eb465b8d7516cff86ca0f0d0337de951e1) 16 | else() 17 | set(LibCurl_URL "${LibCurl_BASEURL}/libcurl-macos-${LibCurl_VERSION}-Debug.tar.gz") 18 | set(LibCurl_HASH SHA256=da0801168eac5103e6b27bfd0f56f82e0617f85e4e6c69f476071dbba273403b) 19 | endif() 20 | elseif(MSVC) 21 | if(LibCurl_BUILD_TYPE STREQUAL Release) 22 | set(LibCurl_URL "${LibCurl_BASEURL}/libcurl-windows-${LibCurl_VERSION}-Release.zip") 23 | set(LibCurl_HASH SHA256=bf4d4cd7d741712a2913df0994258d11aabe22c9a305c9f336ed59e76f351adf) 24 | else() 25 | set(LibCurl_URL "${LibCurl_BASEURL}/libcurl-windows-${LibCurl_VERSION}-Debug.zip") 26 | set(LibCurl_HASH SHA256=9fe20e677ffb0d7dd927b978d532e23574cdb1923e2d2ca7c5e42f1fff2ec529) 27 | endif() 28 | else() 29 | if(LibCurl_BUILD_TYPE STREQUAL Release) 30 | set(LibCurl_URL "${LibCurl_BASEURL}/libcurl-linux-${LibCurl_VERSION}-Release.tar.gz") 31 | set(LibCurl_HASH SHA256=f2cd80b7d3288fe5b4c90833bcbf0bde7c9574bc60eddb13015df19c5a09f56b) 32 | else() 33 | set(LibCurl_URL "${LibCurl_BASEURL}/libcurl-linux-${LibCurl_VERSION}-Debug.tar.gz") 34 | set(LibCurl_HASH SHA256=6a41d3daef98acc3172b3702118dcf1cccbde923f3836ed2f4f3ed7301e47b8b) 35 | endif() 36 | endif() 37 | 38 | FetchContent_Declare( 39 | libcurl_fetch 40 | URL ${LibCurl_URL} 41 | URL_HASH ${LibCurl_HASH}) 42 | FetchContent_MakeAvailable(libcurl_fetch) 43 | 44 | if(MSVC) 45 | set(libcurl_fetch_lib_location "${libcurl_fetch_SOURCE_DIR}/lib/libcurl.lib") 46 | set(libcurl_fetch_link_libs "\$;\$;\$;\$") 47 | else() 48 | find_package(ZLIB REQUIRED) 49 | set(libcurl_fetch_lib_location "${libcurl_fetch_SOURCE_DIR}/lib/libcurl.a") 50 | if(UNIX AND NOT APPLE) 51 | find_package(OpenSSL REQUIRED) 52 | set(libcurl_fetch_link_libs "\$;\$;\$") 53 | else() 54 | set(libcurl_fetch_link_libs 55 | "-framework SystemConfiguration;-framework Security;-framework CoreFoundation;-framework CoreServices;ZLIB::ZLIB" 56 | ) 57 | endif() 58 | endif() 59 | 60 | # Create imported target 61 | add_library(libcurl STATIC IMPORTED) 62 | 63 | set_target_properties( 64 | libcurl 65 | PROPERTIES INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB" 66 | INTERFACE_INCLUDE_DIRECTORIES "${libcurl_fetch_SOURCE_DIR}/include" 67 | INTERFACE_LINK_LIBRARIES "${libcurl_fetch_link_libs}") 68 | set_property( 69 | TARGET libcurl 70 | APPEND 71 | PROPERTY IMPORTED_CONFIGURATIONS RELEASE) 72 | set_target_properties(libcurl PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" IMPORTED_LOCATION_RELEASE 73 | ${libcurl_fetch_lib_location}) 74 | -------------------------------------------------------------------------------- /src/translation-service/httpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "httpserver.h" 2 | #include "utils/config-data.h" 3 | #include "plugin-support.h" 4 | #include "translation.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // start the http server 12 | void start_http_server() 13 | { 14 | obs_log(LOG_INFO, "Starting Polyglot http server thread..."); 15 | 16 | std::thread([]() { 17 | // create the server 18 | if (global_context.svr != nullptr) { 19 | obs_log(LOG_INFO, "Polyglot Http server already running, stopping..."); 20 | stop_http_server(); 21 | } 22 | global_context.svr = new httplib::Server(); 23 | 24 | global_context.svr->set_pre_routing_handler([](const httplib::Request &, 25 | httplib::Response &res) { 26 | res.set_header("Access-Control-Allow-Origin", "*"); 27 | res.set_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); 28 | res.set_header("Access-Control-Allow-Headers", 29 | "Content-Type, Authorization"); 30 | return httplib::Server::HandlerResponse::Unhandled; 31 | }); 32 | 33 | // set an echo handler 34 | global_context.svr->Post("/echo", [](const httplib::Request &req, 35 | httplib::Response &res, 36 | const httplib::ContentReader &content_reader) { 37 | UNUSED_PARAMETER(req); 38 | obs_log(LOG_DEBUG, "Received request on /echo"); 39 | std::string body; 40 | content_reader([&](const char *data, size_t data_length) { 41 | body.append(data, data_length); 42 | return true; 43 | }); 44 | res.set_content(body, "text/plain"); 45 | }); 46 | // set a translation handler 47 | global_context.svr->Post( 48 | "/translate", [](const httplib::Request &req, httplib::Response &res, 49 | const httplib::ContentReader &content_reader) { 50 | UNUSED_PARAMETER(req); 51 | obs_log(LOG_DEBUG, "Received request on /translate"); 52 | std::string body; 53 | content_reader([&](const char *data, size_t data_length) { 54 | body.append(data, data_length); 55 | return true; 56 | }); 57 | 58 | std::string result; 59 | int ret = translate_from_json(body, result); 60 | if (ret == OBS_POLYGLOT_TRANSLATION_SUCCESS) { 61 | res.set_content(result, "text/plain"); 62 | } else { 63 | res.set_content("Translation failed", "text/plain"); 64 | res.status = 500; 65 | } 66 | }); 67 | 68 | // listen on the port 69 | obs_log(LOG_INFO, "Polyglot Http server starting on port %d", 70 | global_config.http_server_port); 71 | try { 72 | global_context.svr->listen("127.0.0.1", global_config.http_server_port); 73 | } catch (const std::exception &e) { 74 | obs_log(LOG_ERROR, "Polyglot Http server start error: %s", e.what()); 75 | } 76 | obs_log(LOG_INFO, "Polyglot Http server stopped."); 77 | }).detach(); 78 | 79 | global_context.status_callback("Ready for requests at http://localhost:" + 80 | std::to_string(global_config.http_server_port) + 81 | "/translate"); 82 | } 83 | 84 | // stop the http server 85 | void stop_http_server() 86 | { 87 | obs_log(LOG_INFO, "Stopping Polyglot http server..."); 88 | if (global_context.svr == nullptr) { 89 | obs_log(LOG_INFO, "Polyglot Http server not running."); 90 | } else { 91 | global_context.svr->stop(); 92 | delete global_context.svr; 93 | global_context.svr = nullptr; 94 | global_context.status_callback(""); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /.github/scripts/Build-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('x64')] 4 | [string] $Target = 'x64', 5 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 6 | [string] $Configuration = 'RelWithDebInfo', 7 | [switch] $SkipAll, 8 | [switch] $SkipBuild, 9 | [switch] $SkipDeps 10 | ) 11 | 12 | $ErrorActionPreference = 'Stop' 13 | 14 | if ( $DebugPreference -eq 'Continue' ) { 15 | $VerbosePreference = 'Continue' 16 | $InformationPreference = 'Continue' 17 | } 18 | 19 | if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) { 20 | throw "A 64-bit system is required to build the project." 21 | } 22 | 23 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 24 | Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 25 | exit 2 26 | } 27 | 28 | function Build { 29 | trap { 30 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 31 | Write-Error $_ 32 | Log-Group 33 | exit 2 34 | } 35 | 36 | $ScriptHome = $PSScriptRoot 37 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 38 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 39 | 40 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 41 | 42 | foreach($Utility in $UtilityFunctions) { 43 | Write-Debug "Loading $($Utility.FullName)" 44 | . $Utility.FullName 45 | } 46 | 47 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 48 | $ProductName = $BuildSpec.name 49 | $ProductVersion = $BuildSpec.version 50 | 51 | if ( ! $SkipDeps ) { 52 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 53 | } 54 | 55 | Push-Location -Stack BuildTemp 56 | if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) { 57 | Ensure-Location $ProjectRoot 58 | 59 | $CmakeArgs = @() 60 | $CmakeBuildArgs = @() 61 | $CmakeInstallArgs = @() 62 | 63 | if ( $VerbosePreference -eq 'Continue' ) { 64 | $CmakeBuildArgs += ('--verbose') 65 | $CmakeInstallArgs += ('--verbose') 66 | } 67 | 68 | if ( $DebugPreference -eq 'Continue' ) { 69 | $CmakeArgs += ('--debug-output') 70 | } 71 | 72 | $Preset = "windows-$(if ( $Env:CI -ne $null ) { 'ci-' })${Target}" 73 | 74 | $CmakeArgs += @( 75 | '--preset', $Preset 76 | ) 77 | 78 | $CmakeBuildArgs += @( 79 | '--build' 80 | '--preset', $Preset 81 | '--config', $Configuration 82 | '--parallel' 83 | '--', '/consoleLoggerParameters:Summary', '/noLogo' 84 | ) 85 | 86 | $CmakeInstallArgs += @( 87 | '--install', "build_${Target}" 88 | '--prefix', "${ProjectRoot}/release/${Configuration}" 89 | '--config', $Configuration 90 | ) 91 | 92 | Log-Group "Configuring ${ProductName}..." 93 | Invoke-External cmake @CmakeArgs 94 | 95 | Log-Group "Building ${ProductName}..." 96 | Invoke-External cmake @CmakeBuildArgs 97 | } 98 | Log-Group "Install ${ProductName}..." 99 | Invoke-External cmake @CmakeInstallArgs 100 | 101 | Pop-Location -Stack BuildTemp 102 | Log-Group 103 | } 104 | 105 | Build 106 | -------------------------------------------------------------------------------- /.github/scripts/Package-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('x64')] 4 | [string] $Target = 'x64', 5 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 6 | [string] $Configuration = 'RelWithDebInfo', 7 | [switch] $BuildInstaller, 8 | [switch] $SkipDeps 9 | ) 10 | 11 | $ErrorActionPreference = 'Stop' 12 | 13 | if ( $DebugPreference -eq 'Continue' ) { 14 | $VerbosePreference = 'Continue' 15 | $InformationPreference = 'Continue' 16 | } 17 | 18 | if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) { 19 | throw "Packaging script requires a 64-bit system to build and run." 20 | } 21 | 22 | 23 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 24 | Write-Warning 'The packaging script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 25 | exit 2 26 | } 27 | 28 | function Package { 29 | trap { 30 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 31 | Write-Error $_ 32 | Log-Group 33 | exit 2 34 | } 35 | 36 | $ScriptHome = $PSScriptRoot 37 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 38 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 39 | 40 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 41 | 42 | foreach( $Utility in $UtilityFunctions ) { 43 | Write-Debug "Loading $($Utility.FullName)" 44 | . $Utility.FullName 45 | } 46 | 47 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 48 | $ProductName = $BuildSpec.name 49 | $ProductVersion = $BuildSpec.version 50 | 51 | # check the CPU_OR_CUDA env variable to determine the target 52 | if ( $Env:CPU_OR_CUDA -eq 'cpu' ) { 53 | $cudaName = 'cpu' 54 | } else { 55 | $cudaName = "cuda${Env:CPU_OR_CUDA}" 56 | } 57 | 58 | $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}-${cudaName}" 59 | 60 | if ( ! $SkipDeps ) { 61 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 62 | } 63 | 64 | $RemoveArgs = @{ 65 | ErrorAction = 'SilentlyContinue' 66 | Path = @( 67 | "${ProjectRoot}/release/${ProductName}-*-windows-*.zip" 68 | "${ProjectRoot}/release/${ProductName}-*-windows-*.exe" 69 | ) 70 | } 71 | 72 | Remove-Item @RemoveArgs 73 | 74 | if ( ( $BuildInstaller ) ) { 75 | Log-Group "Packaging ${ProductName}..." 76 | $IsccFile = "${ProjectRoot}/build_${Target}/installer-Windows.generated.iss" 77 | 78 | if ( ! ( Test-Path -Path $IsccFile ) ) { 79 | throw 'InnoSetup install script not found. Run the build script or the CMake build and install procedures first.' 80 | } 81 | 82 | Log-Information 'Creating InnoSetup installer...' 83 | Push-Location -Stack BuildTemp 84 | Ensure-Location -Path "${ProjectRoot}/release" 85 | Copy-Item -Path ${Configuration} -Destination Package -Recurse 86 | Invoke-External iscc ${IsccFile} /O"${ProjectRoot}/release" /F"${OutputName}-Installer" 87 | Remove-Item -Path Package -Recurse 88 | Pop-Location -Stack BuildTemp 89 | } 90 | 91 | Log-Group "Archiving ${ProductName}..." 92 | $CompressArgs = @{ 93 | Path = (Get-ChildItem -Path "${ProjectRoot}/release/${Configuration}" -Exclude "${OutputName}*.*") 94 | CompressionLevel = 'Optimal' 95 | DestinationPath = "${ProjectRoot}/release/${OutputName}.zip" 96 | Verbose = ($Env:CI -ne $null) 97 | } 98 | 99 | Compress-Archive -Force @CompressArgs 100 | 101 | Log-Group 102 | } 103 | 104 | Package 105 | -------------------------------------------------------------------------------- /.github/actions/build-plugin/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Set up and build plugin' 2 | description: 'Builds the plugin for specified architecture and build config' 3 | inputs: 4 | target: 5 | description: 'Target architecture for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'RelWithDebInfo' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | codesignIdent: 16 | description: 'Developer ID for application codesigning (macOS only)' 17 | required: false 18 | default: '-' 19 | workingDirectory: 20 | description: 'Working directory for packaging' 21 | required: false 22 | default: ${{ github.workspace }} 23 | runs: 24 | using: composite 25 | steps: 26 | - name: Run macOS Build 27 | if: runner.os == 'macOS' 28 | shell: zsh --no-rcs --errexit --pipefail {0} 29 | working-directory: ${{ inputs.workingDirectory }} 30 | env: 31 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 32 | CODESIGN_TEAM: ${{ inputs.codesignTeam }} 33 | run: | 34 | : Run macOS Build 35 | 36 | local -a build_args=(--config ${{ inputs.config }}) 37 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 38 | 39 | if [[ '${{ inputs.codesign }}' == 'true' ]] build_args+=(--codesign) 40 | 41 | .github/scripts/build-macos ${build_args} 42 | 43 | - name: Install Dependencies 🛍️ 44 | if: runner.os == 'Linux' 45 | shell: bash 46 | run: | 47 | : Install Dependencies 🛍️ 48 | echo ::group::Install Dependencies 49 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 50 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 51 | brew install --quiet zsh 52 | echo ::endgroup:: 53 | 54 | - name: Run Ubuntu Build 55 | if: runner.os == 'Linux' 56 | shell: zsh --no-rcs --errexit --pipefail {0} 57 | working-directory: ${{ inputs.workingDirectory }} 58 | run: | 59 | : Run Ubuntu Build 60 | 61 | local -a build_args=( 62 | --target linux-${{ inputs.target }} 63 | --config ${{ inputs.config }} 64 | ) 65 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 66 | 67 | .github/scripts/build-linux ${build_args} 68 | 69 | - name: Run Windows Build 70 | if: runner.os == 'Windows' 71 | shell: pwsh 72 | run: | 73 | # Run Windows Build 74 | if ( $Env:RUNNER_DEBUG -ne $null ) { 75 | Set-PSDebug -Trace 1 76 | } 77 | 78 | $BuildArgs = @{ 79 | Target = '${{ inputs.target }}' 80 | Configuration = '${{ inputs.config }}' 81 | } 82 | 83 | .github/scripts/Build-Windows.ps1 @BuildArgs 84 | 85 | - name: Create Summary 📊 86 | if: contains(fromJSON('["Linux", "macOS"]'),runner.os) 87 | shell: zsh --no-rcs --errexit --pipefail {0} 88 | env: 89 | CCACHE_CONFIGPATH: ${{ inputs.workingDirectory }}/.ccache.conf 90 | run: | 91 | : Create Summary 📊 92 | 93 | local -a ccache_data 94 | if (( ${+RUNNER_DEBUG} )) { 95 | setopt XTRACE 96 | ccache_data=("${(fA)$(ccache -s -vv)}") 97 | } else { 98 | ccache_data=("${(fA)$(ccache -s)}") 99 | } 100 | 101 | print '### ${{ runner.os }} Ccache Stats (${{ inputs.target }})' >> $GITHUB_STEP_SUMMARY 102 | print '```' >> $GITHUB_STEP_SUMMARY 103 | for line (${ccache_data}) { 104 | print ${line} >> $GITHUB_STEP_SUMMARY 105 | } 106 | print '```' >> $GITHUB_STEP_SUMMARY 107 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # please use clang-format version 8 or later 2 | 3 | Standard: Cpp11 4 | AccessModifierOffset: -8 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | #AllowAllArgumentsOnNextLine: false # requires clang-format 9 12 | #AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: false 18 | #AllowShortLambdasOnASingleLine: Inline # requires clang-format 9 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: false 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BraceWrapping: 27 | AfterClass: false 28 | AfterControlStatement: false 29 | AfterEnum: false 30 | AfterFunction: true 31 | AfterNamespace: false 32 | AfterObjCDeclaration: false 33 | AfterStruct: false 34 | AfterUnion: false 35 | AfterExternBlock: false 36 | BeforeCatch: false 37 | BeforeElse: false 38 | IndentBraces: false 39 | SplitEmptyFunction: true 40 | SplitEmptyRecord: true 41 | SplitEmptyNamespace: true 42 | BreakBeforeBinaryOperators: None 43 | BreakBeforeBraces: Custom 44 | BreakBeforeTernaryOperators: true 45 | BreakConstructorInitializers: BeforeColon 46 | BreakStringLiterals: false # apparently unpredictable 47 | ColumnLimit: 100 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 50 | ConstructorInitializerIndentWidth: 8 51 | ContinuationIndentWidth: 8 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | FixNamespaceComments: false 56 | ForEachMacros: 57 | - 'json_object_foreach' 58 | - 'json_object_foreach_safe' 59 | - 'json_array_foreach' 60 | - 'HASH_ITER' 61 | IncludeBlocks: Preserve 62 | IndentCaseLabels: false 63 | IndentPPDirectives: None 64 | IndentWidth: 8 65 | IndentWrappedFunctionNames: false 66 | KeepEmptyLinesAtTheStartOfBlocks: true 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | #ObjCBinPackProtocolList: Auto # requires clang-format 7 70 | ObjCBlockIndentWidth: 8 71 | ObjCSpaceAfterProperty: true 72 | ObjCSpaceBeforeProtocolList: true 73 | 74 | PenaltyBreakAssignment: 10 75 | PenaltyBreakBeforeFirstCallParameter: 30 76 | PenaltyBreakComment: 10 77 | PenaltyBreakFirstLessLess: 0 78 | PenaltyBreakString: 10 79 | PenaltyExcessCharacter: 100 80 | PenaltyReturnTypeOnItsOwnLine: 60 81 | 82 | PointerAlignment: Right 83 | ReflowComments: false 84 | SortIncludes: false 85 | SortUsingDeclarations: false 86 | SpaceAfterCStyleCast: false 87 | #SpaceAfterLogicalNot: false # requires clang-format 9 88 | SpaceAfterTemplateKeyword: false 89 | SpaceBeforeAssignmentOperators: true 90 | #SpaceBeforeCtorInitializerColon: true # requires clang-format 7 91 | #SpaceBeforeInheritanceColon: true # requires clang-format 7 92 | SpaceBeforeParens: ControlStatements 93 | #SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7 94 | SpaceInEmptyParentheses: false 95 | SpacesBeforeTrailingComments: 1 96 | SpacesInAngles: false 97 | SpacesInCStyleCastParentheses: false 98 | SpacesInContainerLiterals: false 99 | SpacesInParentheses: false 100 | SpacesInSquareBrackets: false 101 | #StatementMacros: # requires clang-format 8 102 | # - 'Q_OBJECT' 103 | TabWidth: 8 104 | #TypenameMacros: # requires clang-format 9 105 | # - 'DARRAY' 106 | UseTab: ForContinuationAndIndentation 107 | --- 108 | Language: ObjC 109 | -------------------------------------------------------------------------------- /src/model-utils/model-downloader.cpp: -------------------------------------------------------------------------------- 1 | #include "model-downloader.h" 2 | #include "plugin-support.h" 3 | #include "model-downloader-ui.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | std::map models_info = { 16 | {"M2M100 418M (495Mb)", 17 | { 18 | "m2m100-418m", 19 | {"https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/model.bin?download=true", 20 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/config.json?download=true", 21 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/generation_config.json?download=true", 22 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/shared_vocabulary.json?download=true", 23 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/special_tokens_map.json?download=true", 24 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/tokenizer_config.json?download=true", 25 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/vocab.json?download=true"}, 26 | "", 27 | "https://huggingface.co/jncraton/m2m100_418M-ct2-int8/resolve/main/sentencepiece.bpe.model?download=true", 28 | "", 29 | }}, 30 | {"NLLB 200 Distilled 600M (625Mb)", 31 | { 32 | "nllb-200-distilled-600m", 33 | {"https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/model.bin?download=true", 34 | "https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/config.json?download=true", 35 | "https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/shared_vocabulary.txt?download=true", 36 | "https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/special_tokens_map.json?download=true", 37 | "https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/tokenizer.json?download=true", 38 | "https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/tokenizer_config.json?download=true"}, 39 | "", 40 | "https://huggingface.co/JustFrederik/nllb-200-distilled-600M-ct2-int8/resolve/main/sentencepiece.bpe.model?download=true", 41 | "", 42 | }}}; 43 | 44 | std::string find_model_file(const std::string &model_name) 45 | { 46 | const char *model_name_cstr = model_name.c_str(); 47 | obs_log(LOG_INFO, "Checking if model %s exists in data...", model_name_cstr); 48 | 49 | char *model_file_path = obs_module_file(model_name_cstr); 50 | if (model_file_path == nullptr) { 51 | obs_log(LOG_INFO, "Model %s not found in data.", model_name_cstr); 52 | } else { 53 | std::string model_file_path_str(model_file_path); 54 | bfree(model_file_path); 55 | if (!std::filesystem::exists(model_file_path_str)) { 56 | obs_log(LOG_INFO, "Model not found in data: %s", 57 | model_file_path_str.c_str()); 58 | } else { 59 | obs_log(LOG_INFO, "Model found in data: %s", model_file_path_str.c_str()); 60 | return model_file_path_str; 61 | } 62 | } 63 | 64 | // Check if model exists in the config folder 65 | char *model_config_path_str = 66 | obs_module_get_config_path(obs_current_module(), model_name_cstr); 67 | std::string model_config_path(model_config_path_str); 68 | bfree(model_config_path_str); 69 | obs_log(LOG_INFO, "Model path in config: %s", model_config_path.c_str()); 70 | if (std::filesystem::exists(model_config_path)) { 71 | obs_log(LOG_INFO, "Model exists in config folder: %s", model_config_path.c_str()); 72 | return model_config_path; 73 | } 74 | 75 | obs_log(LOG_INFO, "Model %s not found.", model_name_cstr); 76 | return ""; 77 | } 78 | 79 | void download_model_with_ui_dialog(const ModelInfo &model_info, 80 | download_finished_callback_t download_finished_callback) 81 | { 82 | // Start the model downloader UI 83 | ModelDownloader *model_downloader = new ModelDownloader( 84 | model_info, download_finished_callback, (QWidget *)obs_frontend_get_main_window()); 85 | model_downloader->show(); 86 | } 87 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Logger.ps1: -------------------------------------------------------------------------------- 1 | function Log-Debug { 2 | [CmdletBinding()] 3 | param( 4 | [Parameter(Mandatory,ValueFromPipeline)] 5 | [ValidateNotNullOrEmpty()] 6 | [string[]] $Message 7 | ) 8 | 9 | Process { 10 | foreach($m in $Message) { 11 | Write-Debug "$(if ( $env:CI -ne $null ) { '::debug::' })$m" 12 | } 13 | } 14 | } 15 | 16 | function Log-Verbose { 17 | [CmdletBinding()] 18 | param( 19 | [Parameter(Mandatory,ValueFromPipeline)] 20 | [ValidateNotNullOrEmpty()] 21 | [string[]] $Message 22 | ) 23 | 24 | Process { 25 | foreach($m in $Message) { 26 | Write-Verbose $m 27 | } 28 | } 29 | } 30 | 31 | function Log-Warning { 32 | [CmdletBinding()] 33 | param( 34 | [Parameter(Mandatory,ValueFromPipeline)] 35 | [ValidateNotNullOrEmpty()] 36 | [string[]] $Message 37 | ) 38 | 39 | Process { 40 | foreach($m in $Message) { 41 | Write-Warning "$(if ( $env:CI -ne $null ) { '::warning::' })$m" 42 | } 43 | } 44 | } 45 | 46 | function Log-Error { 47 | [CmdletBinding()] 48 | param( 49 | [Parameter(Mandatory,ValueFromPipeline)] 50 | [ValidateNotNullOrEmpty()] 51 | [string[]] $Message 52 | ) 53 | 54 | Process { 55 | foreach($m in $Message) { 56 | Write-Error "$(if ( $env:CI -ne $null ) { '::error::' })$m" 57 | } 58 | } 59 | } 60 | 61 | function Log-Information { 62 | [CmdletBinding()] 63 | param( 64 | [Parameter(Mandatory,ValueFromPipeline)] 65 | [ValidateNotNullOrEmpty()] 66 | [string[]] $Message 67 | ) 68 | 69 | Process { 70 | if ( ! ( $script:Quiet ) ) { 71 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 72 | $Icon = ' =>' 73 | 74 | foreach($m in $Message) { 75 | Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) " 76 | Write-Host "${m}" 77 | } 78 | } 79 | } 80 | } 81 | 82 | function Log-Group { 83 | [CmdletBinding()] 84 | param( 85 | [Parameter(ValueFromPipeline)] 86 | [string[]] $Message 87 | ) 88 | 89 | Process { 90 | if ( $Env:CI -ne $null ) { 91 | if ( $script:LogGroup ) { 92 | Write-Output '::endgroup::' 93 | $script:LogGroup = $false 94 | } 95 | 96 | if ( $Message.count -ge 1 ) { 97 | Write-Output "::group::$($Message -join ' ')" 98 | $script:LogGroup = $true 99 | } 100 | } else { 101 | if ( $Message.count -ge 1 ) { 102 | Log-Information $Message 103 | } 104 | } 105 | } 106 | } 107 | 108 | function Log-Status { 109 | [CmdletBinding()] 110 | param( 111 | [Parameter(Mandatory,ValueFromPipeline)] 112 | [ValidateNotNullOrEmpty()] 113 | [string[]] $Message 114 | ) 115 | 116 | Process { 117 | if ( ! ( $script:Quiet ) ) { 118 | $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' }) 119 | $Icon = ' >' 120 | 121 | foreach($m in $Message) { 122 | Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) " 123 | Write-Host "${m}" 124 | } 125 | } 126 | } 127 | } 128 | 129 | function Log-Output { 130 | [CmdletBinding()] 131 | param( 132 | [Parameter(Mandatory,ValueFromPipeline)] 133 | [ValidateNotNullOrEmpty()] 134 | [string[]] $Message 135 | ) 136 | 137 | Process { 138 | if ( ! ( $script:Quiet ) ) { 139 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 140 | $Icon = '' 141 | 142 | foreach($m in $Message) { 143 | Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}" 144 | } 145 | } 146 | } 147 | } 148 | 149 | $Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5 150 | -------------------------------------------------------------------------------- /.github/actions/package-plugin/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Package plugin' 2 | description: 'Packages the plugin for specified architecture and build config.' 3 | inputs: 4 | target: 5 | description: 'Build target for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'RelWithDebInfo' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | notarize: 16 | description: 'Enable notarization (macOS only)' 17 | required: false 18 | default: 'false' 19 | codesignIdent: 20 | description: 'Developer ID for application codesigning (macOS only)' 21 | required: false 22 | default: '-' 23 | installerIdent: 24 | description: 'Developer ID for installer package codesigning (macOS only)' 25 | required: false 26 | default: '' 27 | codesignTeam: 28 | description: 'Developer team for codesigning (macOS only)' 29 | required: false 30 | default: '' 31 | codesignUser: 32 | description: 'Apple ID username for notarization (macOS only)' 33 | required: false 34 | default: '' 35 | codesignPass: 36 | description: 'Apple ID password for notarization (macOS only)' 37 | required: false 38 | default: '' 39 | package: 40 | description: 'Create Windows or macOS installation package' 41 | required: false 42 | default: 'false' 43 | workingDirectory: 44 | description: 'Working directory for packaging' 45 | required: false 46 | default: ${{ github.workspace }} 47 | runs: 48 | using: composite 49 | steps: 50 | - name: Run macOS Packaging 51 | if: runner.os == 'macOS' 52 | shell: zsh --no-rcs --errexit --pipefail {0} 53 | working-directory: ${{ inputs.workingDirectory }} 54 | env: 55 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 56 | CODESIGN_IDENT_INSTALLER: ${{ inputs.installerIdent }} 57 | CODESIGN_TEAM: ${{ inputs.codesignTeam }} 58 | CODESIGN_IDENT_USER: ${{ inputs.codesignUser }} 59 | CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }} 60 | run: | 61 | : Run macOS Packaging 62 | 63 | local -a package_args=(--config ${{ inputs.config }}) 64 | if (( ${+RUNNER_DEBUG} )) package_args+=(--debug) 65 | 66 | if [[ '${{ inputs.codesign }}' == 'true' ]] package_args+=(--codesign) 67 | if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(--notarize) 68 | if [[ '${{ inputs.package }}' == 'true' ]] package_args+=(--package) 69 | 70 | .github/scripts/package-macos ${package_args} 71 | 72 | - name: Install Dependencies 🛍️ 73 | if: runner.os == 'Linux' 74 | shell: bash 75 | run: | 76 | : Install Dependencies 🛍️ 77 | echo ::group::Install Dependencies 78 | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" 79 | echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH 80 | brew install --quiet zsh 81 | echo ::endgroup:: 82 | 83 | - name: Run Ubuntu Packaging 84 | if: runner.os == 'Linux' 85 | shell: zsh --no-rcs --errexit --pipefail {0} 86 | working-directory: ${{ inputs.workingDirectory }} 87 | run: | 88 | : Run Ubuntu Packaging 89 | package_args=( 90 | --target linux-${{ inputs.target }} 91 | --config ${{ inputs.config }} 92 | ) 93 | if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) 94 | 95 | if [[ '${{ inputs.package }}' == 'true' ]] package_args+=(--package) 96 | 97 | .github/scripts/package-linux ${package_args} 98 | 99 | - name: Run Windows Packaging 100 | if: runner.os == 'Windows' 101 | shell: pwsh 102 | run: | 103 | # Run Windows Packaging 104 | if ( $Env:RUNNER_DEBUG -ne $null ) { 105 | Set-PSDebug -Trace 1 106 | } 107 | 108 | $PackageArgs = @{ 109 | Target = '${{ inputs.target }}' 110 | Configuration = '${{ inputs.config }}' 111 | } 112 | 113 | if ( '${{ inputs.package }}' -eq 'true' ) { 114 | $PackageArgs += @{BuildInstaller = $true} 115 | } 116 | 117 | .github/scripts/Package-Windows.ps1 @PackageArgs 118 | -------------------------------------------------------------------------------- /.github/workflows/push.yaml: -------------------------------------------------------------------------------- 1 | name: Push to master 2 | run-name: ${{ github.ref_name }} push run 🚀 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | - 'release/**' 9 | tags: 10 | - '*' 11 | permissions: 12 | contents: write 13 | concurrency: 14 | group: '${{ github.workflow }} @ ${{ github.ref }}' 15 | cancel-in-progress: ${{ github.ref_type == 'tag' }} 16 | jobs: 17 | check-format: 18 | name: Check Formatting 🔍 19 | if: github.ref_name == 'master' 20 | uses: ./.github/workflows/check-format.yaml 21 | permissions: 22 | contents: read 23 | 24 | build-project: 25 | name: Build Project 🧱 26 | uses: ./.github/workflows/build-project.yaml 27 | secrets: inherit 28 | permissions: 29 | contents: read 30 | 31 | create-release: 32 | name: Create Release 🛫 33 | if: github.ref_type == 'tag' 34 | runs-on: ubuntu-22.04 35 | needs: build-project 36 | defaults: 37 | run: 38 | shell: bash 39 | steps: 40 | - name: Check Release Tag ☑️ 41 | id: check 42 | run: | 43 | : Check Release Tag ☑️ 44 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 45 | shopt -s extglob 46 | 47 | case "${GITHUB_REF_NAME}" in 48 | +([0-9]).+([0-9]).+([0-9]) ) 49 | echo 'validTag=true' >> $GITHUB_OUTPUT 50 | echo 'prerelease=false' >> $GITHUB_OUTPUT 51 | echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT 52 | ;; 53 | +([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) ) 54 | echo 'validTag=true' >> $GITHUB_OUTPUT 55 | echo 'prerelease=true' >> $GITHUB_OUTPUT 56 | echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT 57 | ;; 58 | *) echo 'validTag=false' >> $GITHUB_OUTPUT ;; 59 | esac 60 | 61 | - name: Download Build Artifacts 📥 62 | uses: actions/download-artifact@v3 63 | if: fromJSON(steps.check.outputs.validTag) 64 | id: download 65 | 66 | - name: Rename Files 🏷️ 67 | if: fromJSON(steps.check.outputs.validTag) 68 | run: | 69 | : Rename Files 🏷️ 70 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 71 | shopt -s extglob 72 | shopt -s nullglob 73 | 74 | root_dir="$(pwd)" 75 | commit_hash="${GITHUB_SHA:0:9}" 76 | 77 | variants=( 78 | 'windows-x64-cpu;zip|exe' 79 | 'windows-x64-cuda12.2.0;zip|exe' 80 | 'windows-x64-cuda11.8.0;zip|exe' 81 | 'macos-universal;tar.xz|pkg' 82 | 'ubuntu-22.04-x86_64;tar.xz|deb|ddeb' 83 | 'sources;tar.xz' 84 | ) 85 | 86 | for variant_data in "${variants[@]}"; do 87 | IFS=';' read -r variant suffix <<< "${variant_data}" 88 | 89 | candidates=(*-${variant}-${commit_hash}/@(*|*-dbgsym).@(${suffix})) 90 | 91 | for candidate in "${candidates[@]}"; do 92 | mv "${candidate}" "${root_dir}" 93 | done 94 | done 95 | 96 | - name: Generate Checksums 🪪 97 | if: fromJSON(steps.check.outputs.validTag) 98 | run: | 99 | : Generate Checksums 🪪 100 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 101 | shopt -s extglob 102 | 103 | echo "### Checksums" > ${{ github.workspace }}/CHECKSUMS.txt 104 | for file in ${{ github.workspace }}/@(*.exe|*.deb|*.ddeb|*.pkg|*.tar.xz|*.zip); do 105 | echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt 106 | done 107 | 108 | - name: Create Release 🛫 109 | if: fromJSON(steps.check.outputs.validTag) 110 | id: create_release 111 | uses: softprops/action-gh-release@d4e8205d7e959a9107da6396278b2f1f07af0f9b 112 | with: 113 | draft: true 114 | prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }} 115 | tag_name: ${{ steps.check.outputs.version }} 116 | name: OBS Studio ${{ steps.check.outputs.version }} 117 | body_path: ${{ github.workspace }}/CHECKSUMS.txt 118 | files: | 119 | ${{ github.workspace }}/*.exe 120 | ${{ github.workspace }}/*.zip 121 | ${{ github.workspace }}/*.pkg 122 | ${{ github.workspace }}/*.deb 123 | ${{ github.workspace }}/*.ddeb 124 | ${{ github.workspace }}/*.tar.xz 125 | -------------------------------------------------------------------------------- /src/translation-service/translation.cpp: -------------------------------------------------------------------------------- 1 | #include "translation.h" 2 | #include "plugin-support.h" 3 | #include "utils/config-data.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int build_translation_context() 11 | { 12 | obs_log(LOG_INFO, "Building translation context..."); 13 | if (global_config.model_selection == 0) { 14 | obs_log(LOG_INFO, "No model selected"); 15 | global_context.error_callback("No model selected"); 16 | return OBS_POLYGLOT_TRANSLATION_INIT_FAIL; 17 | } 18 | try { 19 | obs_log(LOG_INFO, "Loading SPM from %s", global_config.local_spm_path.c_str()); 20 | global_context.processor = new sentencepiece::SentencePieceProcessor(); 21 | const auto status = global_context.processor->Load(global_config.local_spm_path); 22 | if (!status.ok()) { 23 | obs_log(LOG_ERROR, "Failed to load SPM: %s", status.ToString().c_str()); 24 | global_context.error_callback("Failed to load SPM. " + status.ToString()); 25 | return OBS_POLYGLOT_TRANSLATION_INIT_FAIL; 26 | } 27 | 28 | global_context.tokenizer = [](const std::string &text) { 29 | std::vector tokens; 30 | global_context.processor->Encode(text, &tokens); 31 | return tokens; 32 | }; 33 | global_context.detokenizer = [](const std::vector &tokens) { 34 | std::string text; 35 | global_context.processor->Decode(tokens, &text); 36 | return std::regex_replace(text, std::regex(""), "UNK"); 37 | }; 38 | 39 | obs_log(LOG_INFO, "Loading CT2 model from %s", 40 | global_config.local_model_path.c_str()); 41 | 42 | #ifdef POLYGLOT_WITH_CUDA 43 | ctranslate2::Device device = ctranslate2::Device::CUDA; 44 | obs_log(LOG_INFO, "Using CUDA"); 45 | #else 46 | ctranslate2::Device device = ctranslate2::Device::CPU; 47 | obs_log(LOG_INFO, "Using CPU"); 48 | #endif 49 | 50 | global_context.translator = new ctranslate2::Translator( 51 | global_config.local_model_path, device, ctranslate2::ComputeType::AUTO); 52 | obs_log(LOG_INFO, "CT2 Model loaded"); 53 | 54 | global_context.options = new ctranslate2::TranslationOptions; 55 | global_context.options->beam_size = 1; 56 | global_context.options->max_decoding_length = global_config.max_output_tokens; 57 | global_context.options->use_vmap = true; 58 | global_context.options->return_scores = false; 59 | } catch (std::exception &e) { 60 | obs_log(LOG_ERROR, "Failed to load CT2 model: %s", e.what()); 61 | global_context.error_callback("Failed to load CT2 model. " + std::string(e.what())); 62 | return OBS_POLYGLOT_TRANSLATION_INIT_FAIL; 63 | } 64 | global_context.error_callback(""); // Clear any errors 65 | global_context.status_callback("Translation engine ready"); 66 | return OBS_POLYGLOT_TRANSLATION_INIT_SUCCESS; 67 | } 68 | 69 | int translate_from_json(const std::string &body, std::string &result) 70 | { 71 | std::string input_text; 72 | std::string source_lang; 73 | std::string target_lang; 74 | // parse body json 75 | try { 76 | nlohmann::json j = nlohmann::json::parse(body); 77 | input_text = j["text"]; 78 | source_lang = j["source_lang"]; 79 | target_lang = j["target_lang"]; 80 | } catch (std::exception &e) { 81 | obs_log(LOG_ERROR, "Error: %s", e.what()); 82 | result = "Error parsing json"; 83 | return OBS_POLYGLOT_TRANSLATION_FAIL; 84 | } 85 | 86 | return translate(input_text, source_lang, target_lang, result); 87 | } 88 | 89 | int translate(const std::string &text, const std::string &source_lang, 90 | const std::string &target_lang, std::string &result) 91 | { 92 | try { 93 | // get tokens 94 | std::vector tokens = global_context.tokenizer(text); 95 | tokens.insert(tokens.begin(), ""); 96 | tokens.insert(tokens.begin(), source_lang); 97 | tokens.push_back(""); 98 | 99 | const std::vector> batch = {tokens}; 100 | 101 | obs_log(LOG_DEBUG, "Translating..."); 102 | 103 | const std::vector> target_prefix = {{target_lang}}; 104 | const std::vector results = 105 | global_context.translator->translate_batch(batch, target_prefix, 106 | *global_context.options); 107 | 108 | obs_log(LOG_DEBUG, "Translation done"); 109 | // detokenize starting with the 2nd token 110 | const auto &tokens_result = results[0].output(); 111 | result = global_context.detokenizer( 112 | std::vector(tokens_result.begin() + 1, tokens_result.end())); 113 | obs_log(LOG_DEBUG, "Result: %s", result.c_str()); 114 | } catch (std::exception &e) { 115 | obs_log(LOG_ERROR, "Error: %s", e.what()); 116 | return OBS_POLYGLOT_TRANSLATION_FAIL; 117 | } 118 | return OBS_POLYGLOT_TRANSLATION_SUCCESS; 119 | } 120 | -------------------------------------------------------------------------------- /cmake/macos/helpers.cmake: -------------------------------------------------------------------------------- 1 | # CMake macOS helper functions module 2 | 3 | # cmake-format: off 4 | # cmake-lint: disable=C0103 5 | # cmake-lint: disable=C0307 6 | # cmake-format: on 7 | 8 | include_guard(GLOBAL) 9 | 10 | include(helpers_common) 11 | 12 | # set_target_properties_obs: Set target properties for use in obs-studio 13 | function(set_target_properties_plugin target) 14 | set(options "") 15 | set(oneValueArgs "") 16 | set(multiValueArgs PROPERTIES) 17 | cmake_parse_arguments(PARSE_ARGV 0 _STPO "${options}" "${oneValueArgs}" "${multiValueArgs}") 18 | 19 | message(DEBUG "Setting additional properties for target ${target}...") 20 | 21 | while(_STPO_PROPERTIES) 22 | list(POP_FRONT _STPO_PROPERTIES key value) 23 | set_property(TARGET ${target} PROPERTY ${key} "${value}") 24 | endwhile() 25 | 26 | string(TIMESTAMP CURRENT_YEAR "%Y") 27 | set_target_properties( 28 | ${target} 29 | PROPERTIES BUNDLE TRUE 30 | BUNDLE_EXTENSION plugin 31 | XCODE_ATTRIBUTE_PRODUCT_NAME ${target} 32 | XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER ${MACOS_BUNDLEID} 33 | XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION ${PLUGIN_BUILD_NUMBER} 34 | XCODE_ATTRIBUTE_MARKETING_VERSION ${PLUGIN_VERSION} 35 | XCODE_ATTRIBUTE_GENERATE_INFOPLIST_FILE YES 36 | XCODE_ATTRIBUTE_INFOPLIST_FILE "" 37 | XCODE_ATTRIBUTE_INFOPLIST_KEY_CFBundleDisplayName ${target} 38 | XCODE_ATTRIBUTE_INFOPLIST_KEY_NSHumanReadableCopyright "(c) ${CURRENT_YEAR} ${PLUGIN_AUTHOR}" 39 | XCODE_ATTRIBUTE_INSTALL_PATH "$(USER_LIBRARY_DIR)/Application Support/obs-studio/plugins") 40 | 41 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist") 42 | set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS 43 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist") 44 | endif() 45 | 46 | if(TARGET plugin-support) 47 | target_link_libraries(${target} PRIVATE plugin-support) 48 | endif() 49 | 50 | target_install_resources(${target}) 51 | 52 | get_target_property(target_sources ${target} SOURCES) 53 | set(target_ui_files ${target_sources}) 54 | list(FILTER target_ui_files INCLUDE REGEX ".+\\.(ui|qrc)") 55 | source_group( 56 | TREE "${CMAKE_CURRENT_SOURCE_DIR}" 57 | PREFIX "UI Files" 58 | FILES ${target_ui_files}) 59 | 60 | set(valid_uuid FALSE) 61 | check_uuid(${_macosPackageUUID} valid_uuid) 62 | if(NOT valid_uuid) 63 | message(FATAL_ERROR "Specified macOS package UUID is not a valid UUID value: ${_macosPackageUUID}") 64 | else() 65 | set(UUID_PACKAGE ${_macosPackageUUID}) 66 | endif() 67 | 68 | set(valid_uuid FALSE) 69 | check_uuid(${_macosInstallerUUID} valid_uuid) 70 | if(NOT valid_uuid) 71 | message(FATAL_ERROR "Specified macOS package UUID is not a valid UUID value: ${_macosInstallerUUID}") 72 | else() 73 | set(UUID_INSTALLER ${_macosInstallerUUID}) 74 | endif() 75 | 76 | install(TARGETS ${target} LIBRARY DESTINATION .) 77 | install( 78 | FILES "$.dsym" 79 | CONFIGURATIONS Release 80 | DESTINATION . 81 | OPTIONAL) 82 | configure_file(cmake/macos/resources/distribution.in "${CMAKE_CURRENT_BINARY_DIR}/distribution" @ONLY) 83 | configure_file(cmake/macos/resources/create-package.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/create-package.cmake" @ONLY) 84 | install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/create-package.cmake") 85 | endfunction() 86 | 87 | # target_install_resources: Helper function to add resources into bundle 88 | function(target_install_resources target) 89 | message(DEBUG "Installing resources for target ${target}...") 90 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") 91 | file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") 92 | foreach(data_file IN LISTS data_files) 93 | cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE 94 | relative_path) 95 | cmake_path(GET relative_path PARENT_PATH relative_path) 96 | target_sources(${target} PRIVATE "${data_file}") 97 | set_property(SOURCE "${data_file}" PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${relative_path}") 98 | source_group("Resources/${relative_path}" FILES "${data_file}") 99 | endforeach() 100 | endif() 101 | endfunction() 102 | 103 | # target_add_resource: Helper function to add a specific resource to a bundle 104 | function(target_add_resource target resource) 105 | message(DEBUG "Add resource ${resource} to target ${target} at destination ${destination}...") 106 | target_sources(${target} PRIVATE "${resource}") 107 | set_property(SOURCE "${resource}" PROPERTY MACOSX_PACKAGE_LOCATION Resources) 108 | source_group("Resources" FILES "${resource}") 109 | endfunction() 110 | -------------------------------------------------------------------------------- /cmake/BuildCTranslate2.cmake: -------------------------------------------------------------------------------- 1 | # build the CTranslate2 library from source https://github.com/OpenNMT/CTranslate2.git 2 | 3 | include(ExternalProject) 4 | include(FetchContent) 5 | 6 | if(APPLE) 7 | 8 | FetchContent_Declare( 9 | ctranslate2_fetch 10 | URL https://github.com/occ-ai/obs-ai-ctranslate2-dep/releases/download/1.1.1/libctranslate2-macos-Release-1.1.1.tar.gz 11 | URL_HASH SHA256=da04d88ecc1ea105f8ee672e4eab33af96e50c999c5cc8170e105e110392182b) 12 | FetchContent_MakeAvailable(ctranslate2_fetch) 13 | 14 | add_library(ct2 INTERFACE) 15 | target_link_libraries(ct2 INTERFACE "-framework Accelerate" ${ctranslate2_fetch_SOURCE_DIR}/lib/libctranslate2.a 16 | ${ctranslate2_fetch_SOURCE_DIR}/lib/libcpu_features.a) 17 | set_target_properties(ct2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${ctranslate2_fetch_SOURCE_DIR}/include) 18 | target_compile_options(ct2 INTERFACE -Wno-shorten-64-to-32) 19 | 20 | elseif(WIN32) 21 | 22 | # check CPU_OR_CUDA environment variable 23 | if(NOT DEFINED ENV{CPU_OR_CUDA}) 24 | message(FATAL_ERROR "Please set the CPU_OR_CUDA environment variable to either CPU or CUDA") 25 | endif() 26 | 27 | if($ENV{CPU_OR_CUDA} STREQUAL "cpu") 28 | FetchContent_Declare( 29 | ctranslate2_fetch 30 | URL https://github.com/occ-ai/obs-ai-ctranslate2-dep/releases/download/1.2.0/libctranslate2-windows-4.1.1-Release-cpu.zip 31 | URL_HASH SHA256=30ff8b2499b8d3b5a6c4d6f7f8ddbc89e745ff06e0050b645e3b7c9b369451a3) 32 | else() 33 | # add compile definitions for CUDA 34 | add_compile_definitions(POLYGLOT_WITH_CUDA) 35 | add_compile_definitions(POLYGLOT_CUDA_VERSION=$ENV{CPU_OR_CUDA}) 36 | 37 | if($ENV{CPU_OR_CUDA} STREQUAL "12.2.0") 38 | FetchContent_Declare( 39 | ctranslate2_fetch 40 | URL https://github.com/occ-ai/obs-ai-ctranslate2-dep/releases/download/1.2.0/libctranslate2-windows-4.1.1-Release-cuda12.2.0.zip 41 | URL_HASH SHA256=131724d510f9f2829970953a1bc9e4e8fb7b4cbc8218e32270dcfe6172a51558) 42 | elseif($ENV{CPU_OR_CUDA} STREQUAL "11.8.0") 43 | FetchContent_Declare( 44 | ctranslate2_fetch 45 | URL https://github.com/occ-ai/obs-ai-ctranslate2-dep/releases/download/1.2.0/libctranslate2-windows-4.1.1-Release-cuda11.8.0.zip 46 | URL_HASH SHA256=a120bee82f821df35a4646add30ac18b5c23e4e16b56fa7ba338eeae336e0d81) 47 | else() 48 | message(FATAL_ERROR "Unsupported CUDA version: $ENV{CPU_OR_CUDA}") 49 | endif() 50 | endif() 51 | 52 | FetchContent_MakeAvailable(ctranslate2_fetch) 53 | 54 | add_library(ct2 INTERFACE) 55 | target_link_libraries(ct2 INTERFACE ${ctranslate2_fetch_SOURCE_DIR}/lib/ctranslate2.lib) 56 | set_target_properties(ct2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${ctranslate2_fetch_SOURCE_DIR}/include) 57 | target_compile_options(ct2 INTERFACE /wd4267 /wd4244 /wd4305 /wd4996 /wd4099) 58 | 59 | file(GLOB CT2_DLLS ${ctranslate2_fetch_SOURCE_DIR}/bin/*.dll) 60 | install(FILES ${CT2_DLLS} DESTINATION "obs-plugins/64bit") 61 | else() 62 | set(CT2_VERSION "4.1.1") 63 | set(CT2_URL "https://github.com/OpenNMT/CTranslate2.git") 64 | set(CT2_OPENBLAS_CMAKE_ARGS -DWITH_OPENBLAS=OFF) 65 | 66 | set(CT2_CMAKE_PLATFORM_OPTIONS -DBUILD_SHARED_LIBS=OFF -DOPENMP_RUNTIME=NONE -DCMAKE_POSITION_INDEPENDENT_CODE=ON) 67 | set(CT2_LIB_INSTALL_LOCATION lib/${CMAKE_SHARED_LIBRARY_PREFIX}ctranslate2${CMAKE_STATIC_LIBRARY_SUFFIX}) 68 | 69 | ExternalProject_Add( 70 | ct2_build 71 | GIT_REPOSITORY ${CT2_URL} 72 | GIT_TAG v${CT2_VERSION} 73 | GIT_PROGRESS 1 74 | BUILD_COMMAND ${CMAKE_COMMAND} --build --config ${CMAKE_BUILD_TYPE} 75 | CMAKE_GENERATOR ${CMAKE_GENERATOR} 76 | INSTALL_COMMAND ${CMAKE_COMMAND} --install --config ${CMAKE_BUILD_TYPE} 77 | BUILD_BYPRODUCTS /${CT2_LIB_INSTALL_LOCATION} 78 | CMAKE_ARGS -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} 79 | -DCMAKE_INSTALL_PREFIX= 80 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 81 | -DWITH_CUDA=OFF 82 | -DWITH_MKL=OFF 83 | -DWITH_TESTS=OFF 84 | -DWITH_EXAMPLES=OFF 85 | -DWITH_TFLITE=OFF 86 | -DWITH_TRT=OFF 87 | -DWITH_PYTHON=OFF 88 | -DWITH_SERVER=OFF 89 | -DWITH_COVERAGE=OFF 90 | -DWITH_PROFILING=OFF 91 | -DBUILD_CLI=OFF 92 | ${CT2_OPENBLAS_CMAKE_ARGS} 93 | ${CT2_CMAKE_PLATFORM_OPTIONS}) 94 | ExternalProject_Get_Property(ct2_build INSTALL_DIR) 95 | 96 | add_library(ct2::ct2 STATIC IMPORTED GLOBAL) 97 | add_dependencies(ct2::ct2 ct2_build) 98 | set_target_properties(ct2::ct2 PROPERTIES IMPORTED_LOCATION ${INSTALL_DIR}/${CT2_LIB_INSTALL_LOCATION}) 99 | 100 | add_library(ct2 INTERFACE) 101 | target_link_libraries(ct2 INTERFACE ct2::ct2) 102 | set_target_properties(ct2::ct2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) 103 | 104 | endif() 105 | -------------------------------------------------------------------------------- /cmake/windows/helpers.cmake: -------------------------------------------------------------------------------- 1 | # CMake Windows helper functions module 2 | 3 | # cmake-format: off 4 | # cmake-lint: disable=C0103 5 | # cmake-format: on 6 | 7 | include_guard(GLOBAL) 8 | 9 | include(helpers_common) 10 | 11 | # set_target_properties_plugin: Set target properties for use in obs-studio 12 | function(set_target_properties_plugin target) 13 | set(options "") 14 | set(oneValueArgs "") 15 | set(multiValueArgs PROPERTIES) 16 | cmake_parse_arguments(PARSE_ARGV 0 _STPO "${options}" "${oneValueArgs}" "${multiValueArgs}") 17 | 18 | message(DEBUG "Setting additional properties for target ${target}...") 19 | 20 | while(_STPO_PROPERTIES) 21 | list(POP_FRONT _STPO_PROPERTIES key value) 22 | set_property(TARGET ${target} PROPERTY ${key} "${value}") 23 | endwhile() 24 | 25 | string(TIMESTAMP CURRENT_YEAR "%Y") 26 | 27 | set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${PLUGIN_VERSION}) 28 | 29 | install( 30 | TARGETS ${target} 31 | RUNTIME DESTINATION bin/64bit 32 | LIBRARY DESTINATION obs-plugins/64bit) 33 | 34 | install( 35 | FILES "$" 36 | CONFIGURATIONS RelWithDebInfo Debug 37 | DESTINATION obs-plugins/64bit 38 | OPTIONAL) 39 | 40 | if(OBS_BUILD_DIR) 41 | add_custom_command( 42 | TARGET ${target} 43 | POST_BUILD 44 | COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_BUILD_DIR}/obs-plugins/64bit" 45 | COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" 46 | "$<$:$>" "${OBS_BUILD_DIR}/obs-plugin/64bit" 47 | COMMENT "Copy ${target} to obs-studio directory ${OBS_BUILD_DIR}" 48 | VERBATIM) 49 | endif() 50 | 51 | if(TARGET plugin-support) 52 | target_link_libraries(${target} PRIVATE plugin-support) 53 | endif() 54 | 55 | target_install_resources(${target}) 56 | 57 | get_target_property(target_sources ${target} SOURCES) 58 | set(target_ui_files ${target_sources}) 59 | list(FILTER target_ui_files INCLUDE REGEX ".+\\.(ui|qrc)") 60 | source_group( 61 | TREE "${CMAKE_CURRENT_SOURCE_DIR}" 62 | PREFIX "UI Files" 63 | FILES ${target_ui_files}) 64 | 65 | set(valid_uuid FALSE) 66 | check_uuid(${_windowsAppUUID} valid_uuid) 67 | if(NOT valid_uuid) 68 | message(FATAL_ERROR "Specified Windows package UUID is not a valid UUID value: ${_windowsAppUUID}") 69 | else() 70 | set(UUID_APP ${_windowsAppUUID}) 71 | endif() 72 | 73 | configure_file(cmake/windows/resources/installer-Windows.iss.in 74 | "${CMAKE_CURRENT_BINARY_DIR}/installer-Windows.generated.iss") 75 | 76 | configure_file(cmake/windows/resources/resource.rc.in "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc") 77 | target_sources(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc") 78 | endfunction() 79 | 80 | # Helper function to add resources into bundle 81 | function(target_install_resources target) 82 | message(DEBUG "Installing resources for target ${target}...") 83 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") 84 | file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") 85 | foreach(data_file IN LISTS data_files) 86 | cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE 87 | relative_path) 88 | cmake_path(GET relative_path PARENT_PATH relative_path) 89 | target_sources(${target} PRIVATE "${data_file}") 90 | source_group("Resources/${relative_path}" FILES "${data_file}") 91 | endforeach() 92 | 93 | install( 94 | DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" 95 | DESTINATION data/obs-plugins/${target} 96 | USE_SOURCE_PERMISSIONS) 97 | 98 | if(OBS_BUILD_DIR) 99 | add_custom_command( 100 | TARGET ${target} 101 | POST_BUILD 102 | COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_BUILD_DIR}/data/obs-plugins/${target}" 103 | COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/data" 104 | "${OBS_BUILD_DIR}/data/obs-plugins/${target}" 105 | COMMENT "Copy ${target} resources to data directory" 106 | VERBATIM) 107 | endif() 108 | endif() 109 | endfunction() 110 | 111 | # Helper function to add a specific resource to a bundle 112 | function(target_add_resource target resource) 113 | message(DEBUG "Add resource '${resource}' to target ${target} at destination '${target_destination}'...") 114 | 115 | install( 116 | FILES "${resource}" 117 | DESTINATION data/obs-plugins/${target} 118 | COMPONENT Runtime) 119 | 120 | if(OBS_BUILD_DIR) 121 | add_custom_command( 122 | TARGET ${target} 123 | POST_BUILD 124 | COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_BUILD_DIR}/data/obs-plugins/${target}" 125 | COMMAND "${CMAKE_COMMAND}" -E copy "${resource}" "${OBS_BUILD_DIR}/data/obs-plugins/${target}" 126 | COMMENT "Copy ${target} resource ${resource} to library directory" 127 | VERBATIM) 128 | endif() 129 | source_group("Resources" FILES "${resource}") 130 | endfunction() 131 | -------------------------------------------------------------------------------- /cmake/common/helpers_common.cmake: -------------------------------------------------------------------------------- 1 | # CMake common helper functions module 2 | 3 | # cmake-format: off 4 | # cmake-lint: disable=C0103 5 | # cmake-format: on 6 | 7 | include_guard(GLOBAL) 8 | 9 | # * Use QT_VERSION value as a hint for desired Qt version 10 | # * If "AUTO" was specified, prefer Qt6 over Qt5 11 | # * Creates versionless targets of desired component if none had been created by Qt itself (Qt versions < 5.15) 12 | if(NOT QT_VERSION) 13 | set(QT_VERSION 14 | AUTO 15 | CACHE STRING "OBS Qt version [AUTO, 5, 6]" FORCE) 16 | set_property(CACHE QT_VERSION PROPERTY STRINGS AUTO 5 6) 17 | endif() 18 | 19 | # find_qt: Macro to find best possible Qt version for use with the project: 20 | macro(find_qt) 21 | set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX) 22 | cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 23 | 24 | # Do not use versionless targets in the first step to avoid Qt::Core being clobbered by later opportunistic 25 | # find_package runs 26 | set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE) 27 | 28 | message(DEBUG "Start Qt version discovery...") 29 | # Loop until _QT_VERSION is set or FATAL_ERROR aborts script execution early 30 | while(NOT _QT_VERSION) 31 | message(DEBUG "QT_VERSION set to ${QT_VERSION}") 32 | if(QT_VERSION STREQUAL AUTO AND NOT qt_test_version) 33 | set(qt_test_version 6) 34 | elseif(NOT QT_VERSION STREQUAL AUTO) 35 | set(qt_test_version ${QT_VERSION}) 36 | endif() 37 | message(DEBUG "Attempting to find Qt${qt_test_version}") 38 | 39 | find_package( 40 | Qt${qt_test_version} 41 | COMPONENTS Core 42 | QUIET) 43 | 44 | if(TARGET Qt${qt_test_version}::Core) 45 | set(_QT_VERSION 46 | ${qt_test_version} 47 | CACHE INTERNAL "") 48 | message(STATUS "Qt version found: ${_QT_VERSION}") 49 | unset(qt_test_version) 50 | break() 51 | elseif(QT_VERSION STREQUAL AUTO) 52 | if(qt_test_version EQUAL 6) 53 | message(WARNING "Qt6 was not found, falling back to Qt5") 54 | set(qt_test_version 5) 55 | continue() 56 | endif() 57 | endif() 58 | message(FATAL_ERROR "Neither Qt6 nor Qt5 found.") 59 | endwhile() 60 | 61 | # Enable versionless targets for the remaining Qt components 62 | set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE) 63 | 64 | set(qt_components ${find_qt_COMPONENTS}) 65 | if(OS_WINDOWS) 66 | list(APPEND qt_components ${find_qt_COMPONENTS_WIN}) 67 | elseif(OS_MACOS) 68 | list(APPEND qt_components ${find_qt_COMPONENTS_MAC}) 69 | else() 70 | list(APPEND qt_components ${find_qt_COMPONENTS_LINUX}) 71 | endif() 72 | message(DEBUG "Trying to find Qt components ${qt_components}...") 73 | 74 | find_package(Qt${_QT_VERSION} REQUIRED ${qt_components}) 75 | 76 | list(APPEND qt_components Core) 77 | 78 | if("Gui" IN_LIST find_qt_COMPONENTS_LINUX) 79 | list(APPEND qt_components "GuiPrivate") 80 | endif() 81 | 82 | # Check for versionless targets of each requested component and create if necessary 83 | foreach(component IN LISTS qt_components) 84 | message(DEBUG "Checking for target Qt::${component}") 85 | if(NOT TARGET Qt::${component} AND TARGET Qt${_QT_VERSION}::${component}) 86 | add_library(Qt::${component} INTERFACE IMPORTED) 87 | set_target_properties(Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES Qt${_QT_VERSION}::${component}) 88 | endif() 89 | set_property(TARGET Qt::${component} PROPERTY INTERFACE_COMPILE_FEATURES "") 90 | endforeach() 91 | 92 | endmacro() 93 | 94 | # check_uuid: Helper function to check for valid UUID 95 | function(check_uuid uuid_string return_value) 96 | set(valid_uuid TRUE) 97 | set(uuid_token_lengths 8 4 4 4 12) 98 | set(token_num 0) 99 | 100 | string(REPLACE "-" ";" uuid_tokens ${uuid_string}) 101 | list(LENGTH uuid_tokens uuid_num_tokens) 102 | 103 | if(uuid_num_tokens EQUAL 5) 104 | message(DEBUG "UUID ${uuid_string} is valid with 5 tokens.") 105 | foreach(uuid_token IN LISTS uuid_tokens) 106 | list(GET uuid_token_lengths ${token_num} uuid_target_length) 107 | string(LENGTH "${uuid_token}" uuid_actual_length) 108 | if(uuid_actual_length EQUAL uuid_target_length) 109 | string(REGEX MATCH "[0-9a-fA-F]+" uuid_hex_match ${uuid_token}) 110 | if(NOT uuid_hex_match STREQUAL uuid_token) 111 | set(valid_uuid FALSE) 112 | break() 113 | endif() 114 | else() 115 | set(valid_uuid FALSE) 116 | break() 117 | endif() 118 | math(EXPR token_num "${token_num}+1") 119 | endforeach() 120 | else() 121 | set(valid_uuid FALSE) 122 | endif() 123 | message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") 124 | set(${return_value} 125 | ${valid_uuid} 126 | PARENT_SCOPE) 127 | endfunction() 128 | 129 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/plugin-support.c.in") 130 | configure_file(src/plugin-support.c.in plugin-support.c @ONLY) 131 | add_library(plugin-support STATIC) 132 | target_sources( 133 | plugin-support 134 | PRIVATE plugin-support.c 135 | PUBLIC src/plugin-support.h) 136 | target_include_directories(plugin-support PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") 137 | endif() 138 | -------------------------------------------------------------------------------- /src/ui/settingsdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "settingsdialog.h" 2 | #include "ui_settingsdialog.h" 3 | #include "utils/config-data.h" 4 | #include "plugin-support.h" 5 | #include "translation-service/translation.h" 6 | #include "model-utils/model-downloader.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace { 12 | // get home directory 13 | std::string getHomeDir() 14 | { 15 | #ifdef _WIN32 16 | return std::string(getenv("USERPROFILE")); 17 | #else 18 | return std::string(getenv("HOME")); 19 | #endif 20 | } 21 | } 22 | 23 | SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SettingsDialog) 24 | { 25 | ui->setupUi(this); 26 | 27 | // add model selection from model infos 28 | for (const auto &model : models_info) { 29 | this->ui->comboBox_modelSelection->addItem(QString::fromStdString(model.first)); 30 | } 31 | 32 | // populate the UI with the current settings 33 | this->ui->comboBox_modelSelection->setCurrentIndex(global_config.model_selection); 34 | this->ui->modelFile->setEnabled(false); 35 | this->ui->modelFileBtn->setEnabled(false); 36 | this->ui->spmFile->setEnabled(false); 37 | this->ui->spmFileBtn->setEnabled(false); 38 | 39 | if (global_config.model_selection == 1) { 40 | this->ui->modelFile->setEnabled(true); 41 | this->ui->modelFileBtn->setEnabled(true); 42 | this->ui->spmFile->setEnabled(true); 43 | this->ui->spmFileBtn->setEnabled(true); 44 | } 45 | if (global_config.model_selection >= 1) { 46 | this->ui->modelFile->setText( 47 | QString::fromStdString(global_config.local_model_path)); 48 | this->ui->spmFile->setText(QString::fromStdString(global_config.local_spm_path)); 49 | } 50 | 51 | this->ui->httpPort->setText(QString::number(global_config.http_server_port)); 52 | 53 | connect(this->ui->comboBox_modelSelection, 54 | QOverload::of(&QComboBox::currentIndexChanged), [=](int index) { 55 | if (index == 0) { 56 | // None 57 | this->ui->modelFile->setEnabled(false); 58 | this->ui->modelFileBtn->setEnabled(false); 59 | this->ui->spmFile->setEnabled(false); 60 | this->ui->spmFileBtn->setEnabled(false); 61 | this->ui->modelFile->setText(""); 62 | this->ui->spmFile->setText(""); 63 | } else if (index == 1) { 64 | // Custom 65 | this->ui->modelFile->setEnabled(true); 66 | this->ui->modelFileBtn->setEnabled(true); 67 | this->ui->spmFile->setEnabled(true); 68 | this->ui->spmFileBtn->setEnabled(true); 69 | } else { 70 | this->ui->modelFile->setEnabled(false); 71 | this->ui->modelFileBtn->setEnabled(false); 72 | this->ui->spmFile->setEnabled(false); 73 | this->ui->spmFileBtn->setEnabled(false); 74 | 75 | // launch the model downloader 76 | download_model_with_ui_dialog( 77 | models_info[this->ui->comboBox_modelSelection->currentText() 78 | .toStdString()], 79 | [=](int download_status, const ModelInfo &modelInfo) { 80 | if (download_status == 0) { 81 | this->ui->modelFile->setText( 82 | QString::fromStdString( 83 | modelInfo.localPath)); 84 | this->ui->spmFile->setText( 85 | QString::fromStdString( 86 | modelInfo.localSpmPath)); 87 | } else { 88 | obs_log(LOG_ERROR, 89 | "Failed to download model"); 90 | this->ui->comboBox_modelSelection 91 | ->setCurrentIndex(0); 92 | } 93 | }); 94 | } 95 | }); 96 | 97 | // Model folder selection dialog 98 | connect(this->ui->modelFileBtn, &QPushButton::clicked, this, [=]() { 99 | // Allow selection of a folder only 100 | QString dir = QFileDialog::getExistingDirectory( 101 | this, tr("Open Directory"), QString::fromStdString(getHomeDir()), 102 | QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); 103 | if (dir != "") { 104 | this->ui->modelFile->setText(dir); 105 | } 106 | }); 107 | 108 | // SPM folder selection dialog 109 | connect(this->ui->spmFileBtn, &QPushButton::clicked, this, [=]() { 110 | // Allow selection of a .model file only 111 | QString file = QFileDialog::getOpenFileName(this, tr("Open File"), 112 | QString::fromStdString(getHomeDir()), 113 | tr("Model Files (*.model)")); 114 | if (file != "") { 115 | this->ui->spmFile->setText(file); 116 | } 117 | }); 118 | 119 | // connect to the dialog Cancel action to close the dialog 120 | this->connect(this->ui->buttonBox, &QDialogButtonBox::rejected, this, [=]() { 121 | // close the dialog 122 | this->close(); 123 | }); 124 | // connect to the dialog Save action to save the settings 125 | this->connect(this->ui->buttonBox, &QDialogButtonBox::accepted, this, [=]() { 126 | // get settings from UI into config struct 127 | global_config.model_selection = this->ui->comboBox_modelSelection->currentIndex(); 128 | global_config.local_model_path = this->ui->modelFile->text().toStdString(); 129 | global_config.local_spm_path = this->ui->spmFile->text().toStdString(); 130 | global_config.http_server_port = this->ui->httpPort->text().toUShort(); 131 | 132 | // serialize to json and save to the OBS module settings 133 | if (saveConfig(false) == OBS_POLYGLOT_CONFIG_SUCCESS) { 134 | obs_log(LOG_INFO, "Saved settings"); 135 | 136 | // update the plugin 137 | freeContext(false); 138 | if (build_translation_context() == OBS_POLYGLOT_TRANSLATION_INIT_SUCCESS) { 139 | obs_log(LOG_INFO, "Translation context updated"); 140 | } else { 141 | obs_log(LOG_ERROR, "Failed to update translation context"); 142 | } 143 | } else { 144 | obs_log(LOG_ERROR, "Failed to save settings"); 145 | } 146 | 147 | // close the dialog 148 | this->close(); 149 | }); 150 | } 151 | 152 | SettingsDialog::~SettingsDialog() 153 | { 154 | delete ui; 155 | } 156 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 22, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "macos", 11 | "displayName": "macOS Universal", 12 | "description": "Build for macOS 11.0+ (Universal binary)", 13 | "binaryDir": "${sourceDir}/build_macos", 14 | "condition": { 15 | "type": "equals", 16 | "lhs": "${hostSystemName}", 17 | "rhs": "Darwin" 18 | }, 19 | "generator": "Xcode", 20 | "warnings": {"dev": true, "deprecated": true}, 21 | "cacheVariables": { 22 | "QT_VERSION": "6", 23 | "CMAKE_OSX_DEPLOYMENT_TARGET": "11.0", 24 | "CODESIGN_IDENTITY": "$penv{CODESIGN_IDENT}", 25 | "CODESIGN_TEAM": "$penv{CODESIGN_TEAM}", 26 | "ENABLE_FRONTEND_API": true, 27 | "ENABLE_QT": true 28 | } 29 | }, 30 | { 31 | "name": "macos-ci", 32 | "inherits": ["macos"], 33 | "displayName": "macOS Universal CI build", 34 | "description": "Build for macOS 11.0+ (Universal binary) for CI", 35 | "generator": "Xcode", 36 | "cacheVariables": { 37 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 38 | } 39 | }, 40 | { 41 | "name": "windows-x64", 42 | "displayName": "Windows x64", 43 | "description": "Build for Windows x64", 44 | "binaryDir": "${sourceDir}/build_x64", 45 | "condition": { 46 | "type": "equals", 47 | "lhs": "${hostSystemName}", 48 | "rhs": "Windows" 49 | }, 50 | "generator": "Visual Studio 17 2022", 51 | "architecture": "x64", 52 | "warnings": {"dev": true, "deprecated": true}, 53 | "cacheVariables": { 54 | "QT_VERSION": "6", 55 | "CMAKE_SYSTEM_VERSION": "10.0.18363.657", 56 | "ENABLE_FRONTEND_API": true, 57 | "ENABLE_QT": true 58 | } 59 | }, 60 | { 61 | "name": "windows-ci-x64", 62 | "inherits": ["windows-x64"], 63 | "displayName": "Windows x64 CI build", 64 | "description": "Build for Windows x64 on CI", 65 | "cacheVariables": { 66 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 67 | } 68 | }, 69 | { 70 | "name": "linux-x86_64", 71 | "displayName": "Linux x86_64", 72 | "description": "Build for Linux x86_64", 73 | "binaryDir": "${sourceDir}/build_x86_64", 74 | "condition": { 75 | "type": "equals", 76 | "lhs": "${hostSystemName}", 77 | "rhs": "Linux" 78 | }, 79 | "generator": "Ninja", 80 | "warnings": {"dev": true, "deprecated": true}, 81 | "cacheVariables": { 82 | "QT_VERSION": "6", 83 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 84 | "ENABLE_FRONTEND_API": true, 85 | "ENABLE_QT": true 86 | } 87 | }, 88 | { 89 | "name": "linux-ci-x86_64", 90 | "inherits": ["linux-x86_64"], 91 | "displayName": "Linux x86_64 CI build", 92 | "description": "Build for Linux x86_64 on CI", 93 | "cacheVariables": { 94 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 95 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 96 | } 97 | }, 98 | { 99 | "name": "linux-aarch64", 100 | "displayName": "Linux aarch64", 101 | "description": "Build for Linux aarch64", 102 | "binaryDir": "${sourceDir}/build_aarch64", 103 | "condition": { 104 | "type": "equals", 105 | "lhs": "${hostSystemName}", 106 | "rhs": "Linux" 107 | }, 108 | "generator": "Ninja", 109 | "warnings": {"dev": true, "deprecated": true}, 110 | "cacheVariables": { 111 | "QT_VERSION": "6", 112 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 113 | "ENABLE_FRONTEND_API": true, 114 | "ENABLE_QT": true 115 | } 116 | }, 117 | { 118 | "name": "linux-ci-aarch64", 119 | "inherits": ["linux-aarch64"], 120 | "displayName": "Linux aarch64 CI build", 121 | "description": "Build for Linux aarch64 on CI", 122 | "cacheVariables": { 123 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 124 | "CMAKE_COMPILE_WARNING_AS_ERROR": true 125 | } 126 | } 127 | ], 128 | "buildPresets": [ 129 | { 130 | "name": "macos", 131 | "configurePreset": "macos", 132 | "displayName": "macOS Universal", 133 | "description": "macOS build for Universal architectures", 134 | "configuration": "Release" 135 | }, 136 | { 137 | "name": "macos-ci", 138 | "configurePreset": "macos-ci", 139 | "displayName": "macOS Universal CI", 140 | "description": "macOS CI build for Universal architectures", 141 | "configuration": "RelWithDebInfo" 142 | }, 143 | { 144 | "name": "windows-x64", 145 | "configurePreset": "windows-x64", 146 | "displayName": "Windows x64", 147 | "description": "Windows build for x64", 148 | "configuration": "RelWithDebInfo" 149 | }, 150 | { 151 | "name": "windows-ci-x64", 152 | "configurePreset": "windows-ci-x64", 153 | "displayName": "Windows x64 CI", 154 | "description": "Windows CI build for x64 (RelWithDebInfo configuration)", 155 | "configuration": "RelWithDebInfo" 156 | }, 157 | { 158 | "name": "linux-x86_64", 159 | "configurePreset": "linux-x86_64", 160 | "displayName": "Linux x86_64", 161 | "description": "Linux build for x86_64", 162 | "configuration": "RelWithDebInfo" 163 | }, 164 | { 165 | "name": "linux-ci-x86_64", 166 | "configurePreset": "linux-ci-x86_64", 167 | "displayName": "Linux x86_64 CI", 168 | "description": "Linux CI build for x86_64", 169 | "configuration": "RelWithDebInfo" 170 | }, 171 | { 172 | "name": "linux-aarch64", 173 | "configurePreset": "linux-aarch64", 174 | "displayName": "Linux aarch64", 175 | "description": "Linux build for aarch64", 176 | "configuration": "RelWithDebInfo" 177 | }, 178 | { 179 | "name": "linux-ci-aarch64", 180 | "configurePreset": "linux-ci-aarch64", 181 | "displayName": "Linux aarch64 CI", 182 | "description": "Linux CI build for aarch64", 183 | "configuration": "RelWithDebInfo" 184 | } 185 | ] 186 | } 187 | -------------------------------------------------------------------------------- /src/utils/config-data.cpp: -------------------------------------------------------------------------------- 1 | #include "config-data.h" 2 | #include "plugin-support.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | polyglot_config_data global_config; 13 | polyglot_global_context global_context; 14 | 15 | void config_defaults() 16 | { 17 | global_config.model_selection = 0; 18 | global_config.local = true; 19 | global_config.local_model_path = ""; 20 | global_config.local_spm_path = ""; 21 | global_config.cloud_model_name = ""; 22 | global_config.cloud_api_key = ""; 23 | global_config.max_output_tokens = 100; 24 | global_config.http_server_port = 18080; 25 | } 26 | 27 | void create_config_folder() 28 | { 29 | char *config_folder_path = obs_module_config_path(""); 30 | if (config_folder_path == nullptr) { 31 | obs_log(LOG_ERROR, "Failed to get config folder path"); 32 | return; 33 | } 34 | std::filesystem::path config_folder_std_path(config_folder_path); 35 | bfree(config_folder_path); 36 | 37 | // create the folder if it doesn't exist 38 | if (!std::filesystem::exists(config_folder_std_path)) { 39 | #ifdef _WIN32 40 | obs_log(LOG_INFO, "Config folder does not exist, creating: %S", 41 | config_folder_std_path.c_str()); 42 | #else 43 | obs_log(LOG_INFO, "Config folder does not exist, creating: %s", 44 | config_folder_std_path.c_str()); 45 | #endif 46 | // Create the config folder 47 | std::filesystem::create_directories(config_folder_std_path); 48 | } 49 | } 50 | 51 | int getConfig(config_t **config, bool create_if_not_exist = false) 52 | { 53 | create_config_folder(); // ensure the config folder exists 54 | 55 | // Get the config file 56 | char *config_file_path = obs_module_config_path("config.ini"); 57 | 58 | int ret = config_open(config, config_file_path, 59 | create_if_not_exist ? CONFIG_OPEN_ALWAYS : CONFIG_OPEN_EXISTING); 60 | if (ret != CONFIG_SUCCESS) { 61 | obs_log(LOG_INFO, "Failed to open config file %s", config_file_path); 62 | return OBS_POLYGLOT_CONFIG_FAIL; 63 | } 64 | 65 | return OBS_POLYGLOT_CONFIG_SUCCESS; 66 | } 67 | 68 | std::string config_data_to_json(const polyglot_config_data &data); 69 | polyglot_config_data config_data_from_json(const std::string &json); 70 | 71 | int saveConfig(bool create_if_not_exist) 72 | { 73 | config_t *config_file; 74 | if (getConfig(&config_file, create_if_not_exist) == OBS_POLYGLOT_CONFIG_SUCCESS) { 75 | std::string json = config_data_to_json(global_config); 76 | config_set_string(config_file, "general", "polyglot_config", json.c_str()); 77 | config_save(config_file); 78 | config_close(config_file); 79 | return OBS_POLYGLOT_CONFIG_SUCCESS; 80 | } 81 | return OBS_POLYGLOT_CONFIG_FAIL; 82 | } 83 | 84 | int loadConfig() 85 | { 86 | config_t *config_file; 87 | if (getConfig(&config_file) == OBS_POLYGLOT_CONFIG_SUCCESS) { 88 | const char *json = config_get_string(config_file, "general", "polyglot_config"); 89 | if (json != nullptr) { 90 | global_config = config_data_from_json(json); 91 | config_close(config_file); 92 | return OBS_POLYGLOT_CONFIG_SUCCESS; 93 | } 94 | config_close(config_file); 95 | } else { 96 | obs_log(LOG_WARNING, "Failed to load config file. Creating a new one."); 97 | config_defaults(); 98 | if (saveConfig(true) == OBS_POLYGLOT_CONFIG_SUCCESS) { 99 | obs_log(LOG_INFO, "Saved default settings"); 100 | return OBS_POLYGLOT_CONFIG_SUCCESS; 101 | } else { 102 | obs_log(LOG_ERROR, "Failed to save settings"); 103 | } 104 | } 105 | return OBS_POLYGLOT_CONFIG_FAIL; 106 | } 107 | 108 | // serialize config_data to a json string 109 | std::string config_data_to_json(const polyglot_config_data &data) 110 | { 111 | nlohmann::json j; 112 | j["model_selection"] = data.model_selection; 113 | j["local"] = data.local; 114 | j["local_model_path"] = data.local_model_path; 115 | j["local_spm_path"] = data.local_spm_path; 116 | j["cloud_model_name"] = data.cloud_model_name; 117 | j["cloud_api_key"] = data.cloud_api_key; 118 | j["max_output_tokens"] = data.max_output_tokens; 119 | j["http_server_port"] = data.http_server_port; 120 | return j.dump(); 121 | } 122 | 123 | // deserialize config_data from a json string 124 | polyglot_config_data config_data_from_json(const std::string &json) 125 | { 126 | nlohmann::json j = nlohmann::json::parse(json); 127 | polyglot_config_data data; 128 | try { 129 | data.model_selection = j["model_selection"]; 130 | data.local = j["local"]; 131 | data.local_model_path = j["local_model_path"]; 132 | data.local_spm_path = j["local_spm_path"]; 133 | data.cloud_model_name = j["cloud_model_name"]; 134 | data.cloud_api_key = j["cloud_api_key"]; 135 | data.max_output_tokens = j["max_output_tokens"]; 136 | data.http_server_port = j["http_server_port"]; 137 | } catch (nlohmann::json::exception &e) { 138 | obs_log(LOG_ERROR, "Failed to parse config data: %s", e.what()); 139 | } 140 | return data; 141 | } 142 | 143 | void resetContext(bool resetCallbacks) 144 | { 145 | global_context.error_message = ""; 146 | global_context.options = nullptr; 147 | global_context.translator = nullptr; 148 | global_context.processor = nullptr; 149 | global_context.svr = nullptr; 150 | if (resetCallbacks) { 151 | global_context.error_callback = [](const std::string &error_message) { 152 | global_context.error_message = error_message; 153 | if (!error_message.empty()) { 154 | obs_log(LOG_ERROR, "Error (vanilla callback): %s", 155 | error_message.c_str()); 156 | } 157 | }; 158 | global_context.status_callback = [](const std::string &message) { 159 | global_context.status_message = message; 160 | if (!message.empty()) { 161 | obs_log(LOG_INFO, "Status (vanilla callback): %s", message.c_str()); 162 | } 163 | }; 164 | } 165 | global_context.tokenizer = [](const std::string &) { return std::vector(); }; 166 | global_context.detokenizer = [](const std::vector &) { return std::string(); }; 167 | } 168 | 169 | void freeContext(bool resetCallbacks) 170 | { 171 | if (global_context.options != nullptr) { 172 | delete global_context.options; 173 | global_context.options = nullptr; 174 | } 175 | if (global_context.translator != nullptr) { 176 | delete global_context.translator; 177 | global_context.translator = nullptr; 178 | } 179 | if (global_context.processor != nullptr) { 180 | delete global_context.processor; 181 | global_context.processor = nullptr; 182 | } 183 | if (global_context.svr != nullptr) { 184 | delete global_context.svr; 185 | global_context.svr = nullptr; 186 | } 187 | resetContext(resetCallbacks); 188 | } 189 | -------------------------------------------------------------------------------- /.github/actions/setup-macos-codesigning/action.yaml: -------------------------------------------------------------------------------- 1 | name: Set up macOS codesigning 2 | description: Sets up code signing certificates, provisioning profiles, and notarization information 3 | inputs: 4 | codesignIdentity: 5 | description: Codesigning identity 6 | required: true 7 | installerIdentity: 8 | description: Codesigning identity for package installer 9 | required: false 10 | codesignCertificate: 11 | description: PKCS12 certificate in base64 format 12 | required: true 13 | certificatePassword: 14 | description: Password required to install PKCS12 certificate 15 | required: true 16 | keychainPassword: 17 | description: Password to use for temporary keychain 18 | required: false 19 | notarizationUser: 20 | description: Apple ID to use for notarization 21 | required: false 22 | notarizationPassword: 23 | description: Application password for notarization 24 | provisioningProfile: 25 | description: Provisioning profile in base64 format 26 | required: false 27 | outputs: 28 | haveCodesignIdent: 29 | description: True if necessary codesigning credentials were found 30 | value: ${{ steps.codesign.outputs.haveCodesignIdent }} 31 | haveProvisioningProfile: 32 | description: True if necessary provisioning profile credentials were found 33 | value: ${{ steps.provisioning.outputs.haveProvisioningProfile }} 34 | haveNotarizationUser: 35 | description: True if necessary notarization credentials were found 36 | value: ${{ steps.notarization.outputs.haveNotarizationUser }} 37 | codesignIdent: 38 | description: Codesigning identity 39 | value: ${{ steps.codesign.outputs.codesignIdent }} 40 | installerIdent: 41 | description: Codesigning identity for package installer 42 | value: ${{ steps.codesign.outputs.installerIdent }} 43 | codesignTeam: 44 | description: Codesigning team 45 | value: ${{ steps.codesign.outputs.codesignTeam }} 46 | runs: 47 | using: composite 48 | steps: 49 | - name: Check Runner Operating System 🏃‍♂️ 50 | if: runner.os != 'macOS' 51 | shell: bash 52 | run: | 53 | : Check Runner Operating System 🏃‍♂️ 54 | echo "setup-macos-codesigning action requires a macOS-based runner." 55 | exit 2 56 | 57 | - name: macOS Codesigning ✍️ 58 | shell: zsh --no-rcs --errexit --pipefail {0} 59 | id: codesign 60 | env: 61 | MACOS_SIGNING_IDENTITY: ${{ inputs.codesignIdentity }} 62 | MACOS_SIGNING_IDENTITY_INSTALLER: ${{ inputs.installerIdentity}} 63 | MACOS_SIGNING_CERT: ${{ inputs.codesignCertificate }} 64 | MAOCS_SIGNING_CERT_PASSWORD: ${{ inputs.certificatePassword }} 65 | MACOS_KEYCHAIN_PASSWORD: ${{ inputs.keychainPassword }} 66 | run: | 67 | : macOS Codesigning ✍️ 68 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 69 | 70 | if [[ ${MACOS_SIGNING_IDENTITY} && ${MACOS_SIGNING_IDENTITY_INSTALLER} && ${MACOS_SIGNING_CERT} ]] { 71 | print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT 72 | 73 | local -r certificate_path="${RUNNER_TEMP}/build_certificate.p12" 74 | local -r keychain_path="${RUNNER_TEMP}/app-signing.keychain-db" 75 | 76 | print -n "${MACOS_SIGNING_CERT}" | base64 --decode --output="${certificate_path}" 77 | 78 | : "${MACOS_KEYCHAIN_PASSWORD:="$(print ${RANDOM} | sha1sum | head -c 32)"}" 79 | 80 | print '::group::Keychain setup' 81 | security create-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" ${keychain_path} 82 | security set-keychain-settings -lut 21600 ${keychain_path} 83 | security unlock-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" ${keychain_path} 84 | 85 | security import "${certificate_path}" -P "${MAOCS_SIGNING_CERT_PASSWORD}" -A \ 86 | -t cert -f pkcs12 -k ${keychain_path} \ 87 | -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/xcrun 88 | 89 | security set-key-partition-list -S 'apple-tool:,apple:' -k "${MACOS_KEYCHAIN_PASSWORD}" \ 90 | ${keychain_path} &> /dev/null 91 | 92 | security list-keychain -d user -s ${keychain_path} 'login-keychain' 93 | print '::endgroup::' 94 | 95 | local -r team_id="${${MACOS_SIGNING_IDENTITY##* }//(\(|\))/}" 96 | 97 | print "codesignIdent=${MACOS_SIGNING_IDENTITY}" >> $GITHUB_OUTPUT 98 | print "installerIdent=${MACOS_SIGNING_IDENTITY_INSTALLER}" >> $GITHUB_OUTPUT 99 | print "MACOS_KEYCHAIN_PASSWORD=${MACOS_KEYCHAIN_PASSWORD}" >> $GITHUB_ENV 100 | print "codesignTeam=${team_id}" >> $GITHUB_OUTPUT 101 | } else { 102 | print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT 103 | } 104 | 105 | - name: Provisioning Profile 👤 106 | shell: zsh --no-rcs --errexit --pipefail {0} 107 | id: provisioning 108 | if: ${{ fromJSON(steps.codesign.outputs.haveCodesignIdent) }} 109 | env: 110 | MACOS_SIGNING_PROVISIONING_PROFILE: ${{ inputs.provisioningProfile }} 111 | run: | 112 | : Provisioning Profile 👤 113 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 114 | 115 | if [[ ${MACOS_SIGNING_PROVISIONING_PROFILE} ]] { 116 | print 'haveProvisioningProfile=true' >> $GITHUB_OUTPUT 117 | 118 | local -r profile_path="${RUNNER_TEMP}/build_profile.provisionprofile" 119 | print -n "${MACOS_SIGNING_PROVISIONING_PROFILE}" \ 120 | | base64 --decode --output ${profile_path} 121 | 122 | print '::group::Provisioning Profile Setup' 123 | mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles 124 | security cms -D -i ${profile_path} -o ${RUNNER_TEMP}/build_profile.plist 125 | local -r uuid="$(plutil -extract UUID raw ${RUNNER_TEMP}/build_profile.plist)" 126 | local -r team_id="$(plutil -extract TeamIdentifier.0 raw -expect string ${RUNNER_TEMP}/build_profile.plist)" 127 | 128 | if [[ ${team_id} != '${{ steps.codesign.codesignTeam }}' ]] { 129 | print '::notice::Code Signing team in provisioning profile does not match certificate.' 130 | } 131 | 132 | cp ${profile_path} ~/Library/MobileDevice/Provisioning\ Profiles/${uuid}.provisionprofile 133 | print "provisioningProfileUUID=${uuid}" >> $GITHUB_OUTPUT 134 | print '::endgroup::' 135 | } else { 136 | print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT 137 | } 138 | 139 | - name: Notarization 🧑‍💼 140 | shell: zsh --no-rcs --errexit --pipefail {0} 141 | id: notarization 142 | if: ${{ fromJSON(steps.codesign.outputs.haveCodesignIdent) }} 143 | env: 144 | MACOS_NOTARIZATION_USERNAME: ${{ inputs.notarizationUser }} 145 | MACOS_NOTARIZATION_PASSWORD: ${{ inputs.notarizationPassword }} 146 | run: | 147 | : Notarization 🧑‍💼 148 | if (( ${+RUNNER_DEBUG} )) setopt XTRACE 149 | 150 | if [[ ${MACOS_NOTARIZATION_USERNAME} && ${MACOS_NOTARIZATION_PASSWORD} ]] { 151 | print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT 152 | } else { 153 | print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT 154 | } 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ Stalled ⚠️ This project is not under active development 2 | 3 | ## Ployglot - Translation AI assistant OBS Plugin 4 | 5 |
6 | 7 | [![GitHub](https://img.shields.io/github/license/obs-ai/obs-polyglot)](https://github.com/obs-ai/obs-polyglot/blob/main/LICENSE) 8 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/obs-ai/obs-polyglot/push.yaml)](https://github.com/obs-ai/obs-polyglot/actions/workflows/push.yaml) 9 | [![Total downloads](https://img.shields.io/github/downloads/obs-ai/obs-polyglot/total)](https://github.com/obs-ai/obs-polyglot/releases) 10 | [![GitHub release (latest by date)](https://img.shields.io/github/v/release/obs-ai/obs-polyglot)](https://github.com/obs-ai/obs-polyglot/releases) 11 | [![Discord](https://img.shields.io/discord/1200229425141252116)](https://discord.gg/KbjGU2vvUz) 12 | 13 |
14 | 15 | ## Introduction 16 | 17 | Polyglot translation AI plugin allows you to translate text in multiple languages in real-time and locally on your machine. ✅ No GPU required, ✅ no cloud costs, ✅ no network and ✅ no downtime! Privacy first - all data stays on your machine. 18 | 19 | "polyglot" is derived from the Greek words: "poly-" meaning "many"and "glōtta" (or "glōssa") meaning "tongue" or "language". 20 | 21 | It's using the excellent [CTranslate2](https://github.com/OpenNMT/CTranslate2) project from [OpenNMT](https://opennmt.net/). 22 | 23 | If this free plugin has been valuable to you consider adding a ⭐ to this GH repo, subscribing to [my YouTube channel](https://www.youtube.com/@royshilk) where I post updates, and supporting my work: https://github.com/sponsors/royshil 24 | 25 | ### Usage Tutorial 26 | Watch a short tutorial on how to use and setup Polyglot on your OBS scene. 27 |
28 | 29 | 30 | 31 |
32 | https://youtu.be/ZMNILPWDkDw 33 |
34 | 35 | Current Features: 36 | - Translate in real time using an internal HTTP server 37 | - Choice of CTranslate2 model file 38 | 39 | Roadmap: 40 | - Translation directly on OBS text sources, e.g. with a filter 41 | - Translation of live stream or recording captions 42 | 43 | Check out our other plugins: 44 | - [Background Removal](https://github.com/royshil/obs-backgroundremoval) removes background from webcam without a green screen. 45 | - 🚧 Experimental 🚧 [CleanStream](https://github.com/obs-ai/obs-cleanstream) for real-time filler word (uh,um) and profanity removal from live audio stream 46 | - [URL/API Source](https://github.com/obs-ai/obs-urlsource) that allows fetching live data from an API and displaying it in OBS. 47 | - [LocalVocal](https://github.com/royshil/obs-localvocal) speech AI assistant plugin for real-time transcription (captions), translation and more language functions 48 | 49 | 50 | ## Download 51 | Check out the [latest releases](https://github.com/obs-ai/obs-polyglot/releases) for downloads and install instructions. 52 | 53 | ### Models 54 | _You need to download a CT2 model for the translation service to work._ 55 | 56 | Here are download links for models that are compatible with the plugin: 57 | - https://forum.opennmt.net/t/nllb-200-with-ctranslate2/5090 NLLB-200 multilingual model (recommended) ([List of NLLB language codes](https://github.com/facebookresearch/LASER/blob/main/nllb/README.md)) 58 | - https://huggingface.co/models?sort=trending&search=ctranslate CTranslate2 models on HuggingFace 59 | 60 | Download models options: (get e.g. `model.bin` and SPM = SentencePiece Model `.model` file) 61 | - https://forum.opennmt.net/t/nllb-200-with-ctranslate2/5090 (NLLB) 62 | - https://huggingface.co/JustFrederik/m2m_100_418m_ct2_int8/tree/main (M2M100) 63 | - https://huggingface.co/Rohith04/ct2fast_m2m100_418M/tree/main (M2M100) 64 | - https://huggingface.co/jncraton/m2m100_418M-ct2-int8/tree/main (M2M100) 65 | 66 | ## Building 67 | 68 | The plugin was built and tested on Mac OSX (Intel & Apple silicon), Windows and Linux. 69 | 70 | Start by cloning this repo to a directory of your choice. 71 | 72 | Remember to sync and fetch the submodules before building, e.g. 73 | ```sh 74 | $ git submodule sync --recursive 75 | $ git update --init --recursive 76 | ``` 77 | 78 | ### Mac OSX 79 | 80 | Using the CI pipeline scripts, locally you would just call the zsh script. By default this builds a universal binary for both Intel and Apple Silicon. To build for a specific architecture please see `.github/scripts/.build.zsh` for the `-arch` options. 81 | 82 | ```sh 83 | $ ./.github/scripts/build-macos -c Release 84 | ``` 85 | 86 | #### Install 87 | The above script should succeed and the plugin files (e.g. `obs-urlsource.plugin`) will reside in the `./release/Release` folder off of the root. Copy the `.plugin` file to the OBS directory e.g. `~/Library/Application Support/obs-studio/plugins`. 88 | 89 | To get `.pkg` installer file, run for example 90 | ```sh 91 | $ ./.github/scripts/package-macos -c Release 92 | ``` 93 | (Note that maybe the outputs will be in the `Release` folder and not the `install` folder like `pakage-macos` expects, so you will need to rename the folder from `build_x86_64/Release` to `build_x86_64/install`) 94 | 95 | ### Linux (Ubuntu) 96 | 97 | Use the CI scripts again 98 | ```sh 99 | $ ./.github/scripts/build-linux.sh 100 | ``` 101 | 102 | Copy the results to the standard OBS folders on Ubuntu 103 | ```sh 104 | $ sudo cp -R release/RelWithDebInfo/lib/* /usr/lib/x86_64-linux-gnu/ 105 | $ sudo cp -R release/RelWithDebInfo/share/* /usr/share/ 106 | ``` 107 | Note: The official [OBS plugins guide](https://obsproject.com/kb/plugins-guide) recommends adding plugins to the `~/.config/obs-studio/plugins` folder. 108 | 109 | ### Windows 110 | 111 | Use the CI scripts again, for example: 112 | 113 | ```powershell 114 | > .github/scripts/Build-Windows.ps1 -Target x64 -CMakeGenerator "Visual Studio 17 2022" 115 | ``` 116 | 117 | The build should exist in the `./release` folder off the root. You can manually install the files in the OBS directory. 118 | 119 | 137 | -------------------------------------------------------------------------------- /src/ui/settingsdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SettingsDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 370 10 | 290 11 | 12 | 13 | 14 | PolyGlot Settings 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | Model Files 36 | 37 | 38 | 39 | QFormLayout::ExpandingFieldsGrow 40 | 41 | 42 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 43 | 44 | 45 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 46 | 47 | 48 | 3 49 | 50 | 51 | 52 | 53 | false 54 | 55 | 56 | Model File 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 6 65 | 66 | 67 | 0 68 | 69 | 70 | 0 71 | 72 | 73 | 0 74 | 75 | 76 | 0 77 | 78 | 79 | 80 | 81 | false 82 | 83 | 84 | 85 | 86 | 87 | 88 | false 89 | 90 | 91 | 92 | 0 93 | 0 94 | 95 | 96 | 97 | ... 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | false 108 | 109 | 110 | SPM File 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 6 119 | 120 | 121 | 0 122 | 123 | 124 | 0 125 | 126 | 127 | 0 128 | 129 | 130 | 0 131 | 132 | 133 | 134 | 135 | false 136 | 137 | 138 | 139 | 140 | 141 | 142 | false 143 | 144 | 145 | ... 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 0 157 | 0 158 | 159 | 160 | 161 | 162 | Select a model 163 | 164 | 165 | 166 | 167 | Custom (Files) 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | Model 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | HTTP Server 186 | 187 | 188 | 189 | QFormLayout::ExpandingFieldsGrow 190 | 191 | 192 | 193 | 194 | Port 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | By default the HTTP server listenes on 127.0.0.1:{port} 205 | 206 | 207 | true 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | Qt::Vertical 218 | 219 | 220 | 221 | 20 222 | 40 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | Qt::Horizontal 231 | 232 | 233 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /cmake/common/buildspec_common.cmake: -------------------------------------------------------------------------------- 1 | # Common build dependencies module 2 | 3 | # cmake-format: off 4 | # cmake-lint: disable=C0103 5 | # cmake-lint: disable=E1126 6 | # cmake-lint: disable=R0912 7 | # cmake-lint: disable=R0915 8 | # cmake-format: on 9 | 10 | include_guard(GLOBAL) 11 | 12 | # _check_deps_version: Checks for obs-deps VERSION file in prefix paths 13 | function(_check_deps_version version) 14 | # cmake-format: off 15 | set(found FALSE PARENT_SCOPE) 16 | # cmake-format: on 17 | 18 | foreach(path IN LISTS CMAKE_PREFIX_PATH) 19 | if(EXISTS "${path}/share/obs-deps/VERSION") 20 | if(dependency STREQUAL qt6 AND NOT EXISTS "${path}/lib/cmake/Qt6/Qt6Config.cmake") 21 | # cmake-format: off 22 | set(found FALSE PARENT_SCOPE) 23 | # cmake-format: on 24 | continue() 25 | endif() 26 | 27 | file(READ "${path}/share/obs-deps/VERSION" _check_version) 28 | string(REPLACE "\n" "" _check_version "${_check_version}") 29 | string(REPLACE "-" "." _check_version "${_check_version}") 30 | string(REPLACE "-" "." version "${version}") 31 | 32 | if(_check_version VERSION_EQUAL version) 33 | # cmake-format: off 34 | set(found TRUE PARENT_SCOPE) 35 | # cmake-format: on 36 | break() 37 | elseif(_check_version VERSION_LESS version) 38 | message(AUTHOR_WARNING "Older ${label} version detected in ${path}: \n" 39 | "Found ${_check_version}, require ${version}") 40 | list(REMOVE_ITEM CMAKE_PREFIX_PATH "${path}") 41 | list(APPEND CMAKE_PREFIX_PATH "${path}") 42 | # cmake-format: off 43 | set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) 44 | # cmake-format: on 45 | continue() 46 | else() 47 | message(AUTHOR_WARNING "Newer ${label} version detected in ${path}: \n" 48 | "Found ${_check_version}, require ${version}") 49 | # cmake-format: off 50 | set(found TRUE PARENT_SCOPE) 51 | # cmake-format: on 52 | break() 53 | endif() 54 | endif() 55 | endforeach() 56 | endfunction() 57 | 58 | # _setup_obs_studio: Create obs-studio build project, then build libobs and obs-frontend-api 59 | function(_setup_obs_studio) 60 | if(NOT libobs_DIR) 61 | set(_is_fresh --fresh) 62 | endif() 63 | 64 | if(OS_WINDOWS) 65 | set(_cmake_generator "${CMAKE_GENERATOR}") 66 | set(_cmake_arch "-A ${arch}") 67 | set(_cmake_extra "-DCMAKE_SYSTEM_VERSION=${CMAKE_SYSTEM_VERSION} -DCMAKE_ENABLE_SCRIPTING=OFF") 68 | set(_cmake_version "2.0.0") 69 | elseif(OS_MACOS) 70 | set(_cmake_generator "Xcode") 71 | # TODO: enable arm64; currently fails 72 | set(_cmake_arch "-DCMAKE_OSX_ARCHITECTURES:STRING='x86_64;arm64'") 73 | set(_cmake_extra "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") 74 | set(_cmake_version "3.0.0") 75 | endif() 76 | 77 | message(STATUS "Patch libobs") 78 | execute_process( 79 | COMMAND patch --forward "libobs/CMakeLists.txt" "${CMAKE_CURRENT_SOURCE_DIR}/patch_libobs.diff" 80 | RESULT_VARIABLE _process_result 81 | WORKING_DIRECTORY "${dependencies_dir}/${_obs_destination}") 82 | message(STATUS "Patch - done") 83 | 84 | message(STATUS "Configure ${label} (${arch})") 85 | execute_process( 86 | COMMAND 87 | "${CMAKE_COMMAND}" -S "${dependencies_dir}/${_obs_destination}" -B 88 | "${dependencies_dir}/${_obs_destination}/build_${arch}" -G ${_cmake_generator} "${_cmake_arch}" 89 | -DOBS_CMAKE_VERSION:STRING=${_cmake_version} -DENABLE_PLUGINS:BOOL=OFF -DENABLE_UI:BOOL=OFF 90 | -DOBS_VERSION_OVERRIDE:STRING=${_obs_version} "-DCMAKE_PREFIX_PATH='${CMAKE_PREFIX_PATH}'" ${_is_fresh} 91 | ${_cmake_extra} 92 | RESULT_VARIABLE _process_result COMMAND_ERROR_IS_FATAL ANY 93 | OUTPUT_QUIET) 94 | message(STATUS "Configure ${label} (${arch}) - done") 95 | 96 | message(STATUS "Build ${label} (${arch})") 97 | execute_process( 98 | COMMAND "${CMAKE_COMMAND}" --build build_${arch} --target obs-frontend-api --config Debug --parallel 99 | WORKING_DIRECTORY "${dependencies_dir}/${_obs_destination}" 100 | RESULT_VARIABLE _process_result COMMAND_ERROR_IS_FATAL ANY 101 | OUTPUT_QUIET) 102 | message(STATUS "Build ${label} (${arch}) - done") 103 | 104 | message(STATUS "Install ${label} (${arch})") 105 | if(OS_WINDOWS) 106 | set(_cmake_extra "--component obs_libraries") 107 | else() 108 | set(_cmake_extra "") 109 | endif() 110 | execute_process( 111 | COMMAND "${CMAKE_COMMAND}" --install build_${arch} --component Development --config Debug --prefix 112 | "${dependencies_dir}" ${_cmake_extra} 113 | WORKING_DIRECTORY "${dependencies_dir}/${_obs_destination}" 114 | RESULT_VARIABLE _process_result COMMAND_ERROR_IS_FATAL ANY 115 | OUTPUT_QUIET) 116 | message(STATUS "Install ${label} (${arch}) - done") 117 | endfunction() 118 | 119 | # _check_dependencies: Fetch and extract pre-built OBS build dependencies 120 | function(_check_dependencies) 121 | if(NOT buildspec) 122 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/buildspec.json" buildspec) 123 | endif() 124 | 125 | # cmake-format: off 126 | string(JSON dependency_data GET ${buildspec} dependencies) 127 | # cmake-format: on 128 | 129 | foreach(dependency IN LISTS dependencies_list) 130 | # cmake-format: off 131 | string(JSON data GET ${dependency_data} ${dependency}) 132 | string(JSON version GET ${data} version) 133 | string(JSON hash GET ${data} hashes ${platform}) 134 | string(JSON url GET ${data} baseUrl) 135 | string(JSON label GET ${data} label) 136 | string(JSON revision ERROR_VARIABLE error GET ${data} revision ${platform}) 137 | # cmake-format: on 138 | 139 | message(STATUS "Setting up ${label} (${arch})") 140 | 141 | set(file "${${dependency}_filename}") 142 | set(destination "${${dependency}_destination}") 143 | string(REPLACE "VERSION" "${version}" file "${file}") 144 | string(REPLACE "VERSION" "${version}" destination "${destination}") 145 | string(REPLACE "ARCH" "${arch}" file "${file}") 146 | string(REPLACE "ARCH" "${arch}" destination "${destination}") 147 | if(revision) 148 | string(REPLACE "_REVISION" "_v${revision}" file "${file}") 149 | string(REPLACE "-REVISION" "-v${revision}" file "${file}") 150 | else() 151 | string(REPLACE "_REVISION" "" file "${file}") 152 | string(REPLACE "-REVISION" "" file "${file}") 153 | endif() 154 | 155 | set(skip FALSE) 156 | if(dependency STREQUAL prebuilt OR dependency STREQUAL qt6) 157 | _check_deps_version(${version}) 158 | 159 | if(found) 160 | set(skip TRUE) 161 | endif() 162 | endif() 163 | 164 | if(skip) 165 | message(STATUS "Setting up ${label} (${arch}) - skipped") 166 | continue() 167 | endif() 168 | 169 | if(dependency STREQUAL obs-studio) 170 | set(url ${url}/${file}) 171 | else() 172 | set(url ${url}/${version}/${file}) 173 | endif() 174 | 175 | if(NOT EXISTS "${dependencies_dir}/${file}") 176 | message(STATUS "Downloading ${url}") 177 | file( 178 | DOWNLOAD "${url}" "${dependencies_dir}/${file}" 179 | STATUS download_status 180 | EXPECTED_HASH SHA256=${hash}) 181 | 182 | list(GET download_status 0 error_code) 183 | list(GET download_status 1 error_message) 184 | if(error_code GREATER 0) 185 | message(STATUS "Downloading ${url} - Failure") 186 | message(FATAL_ERROR "Unable to download ${url}, failed with error: ${error_message}") 187 | file(REMOVE "${dependencies_dir}/${file}") 188 | else() 189 | message(STATUS "Downloading ${url} - done") 190 | endif() 191 | endif() 192 | 193 | if(NOT EXISTS "${dependencies_dir}/${destination}") 194 | file(MAKE_DIRECTORY "${dependencies_dir}/${destination}") 195 | if(dependency STREQUAL obs-studio) 196 | file(ARCHIVE_EXTRACT INPUT "${dependencies_dir}/${file}" DESTINATION "${dependencies_dir}") 197 | else() 198 | file(ARCHIVE_EXTRACT INPUT "${dependencies_dir}/${file}" DESTINATION "${dependencies_dir}/${destination}") 199 | endif() 200 | endif() 201 | 202 | if(dependency STREQUAL prebuilt) 203 | list(APPEND CMAKE_PREFIX_PATH "${dependencies_dir}/${destination}") 204 | elseif(dependency STREQUAL qt6) 205 | list(APPEND CMAKE_PREFIX_PATH "${dependencies_dir}/${destination}") 206 | elseif(dependency STREQUAL obs-studio) 207 | set(_obs_version ${version}) 208 | set(_obs_destination "${destination}") 209 | list(APPEND CMAKE_PREFIX_PATH "${dependencies_dir}") 210 | 211 | endif() 212 | 213 | message(STATUS "Setting up ${label} (${arch}) - done") 214 | endforeach() 215 | 216 | list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH) 217 | 218 | # cmake-format: off 219 | set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} CACHE PATH "CMake prefix search path" FORCE) 220 | # cmake-format: on 221 | 222 | _setup_obs_studio() 223 | endfunction() 224 | -------------------------------------------------------------------------------- /cmake/macos/xcode.cmake: -------------------------------------------------------------------------------- 1 | # CMake macOS Xcode module 2 | 3 | include_guard(GLOBAL) 4 | 5 | # Use a compiler wrapper to enable ccache in Xcode projects 6 | if(ENABLE_CCACHE AND CCACHE_PROGRAM) 7 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/ccache-launcher-c.in" ccache-launcher-c) 8 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/ccache-launcher-cxx.in" ccache-launcher-cxx) 9 | 10 | execute_process(COMMAND chmod a+rx "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-c" 11 | "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-cxx") 12 | set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-c") 13 | set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-cxx") 14 | set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_C_COMPILER}") 15 | set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CXX_COMPILER}") 16 | endif() 17 | 18 | # Set project variables 19 | set(CMAKE_XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION ${PLUGIN_BUILD_NUMBER}) 20 | set(CMAKE_XCODE_ATTRIBUTE_DYLIB_COMPATIBILITY_VERSION 1.0.0) 21 | set(CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION ${PLUGIN_VERSION}) 22 | 23 | # Set deployment target 24 | set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) 25 | 26 | if(NOT CODESIGN_TEAM) 27 | # Switch to manual codesigning if no codesigning team is provided 28 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual) 29 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${CODESIGN_IDENTITY}") 30 | else() 31 | if(CODESIGN_IDENTITY AND NOT CODESIGN_IDENTITY STREQUAL "-") 32 | # Switch to manual codesigning if a non-adhoc codesigning identity is provided 33 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual) 34 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${CODESIGN_IDENTITY}") 35 | else() 36 | # Switch to automatic codesigning via valid team ID 37 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic) 38 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Development") 39 | endif() 40 | set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${CODESIGN_TEAM}") 41 | endif() 42 | 43 | # Only create a single Xcode project file 44 | set(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY TRUE) 45 | # Add all libraries to project link phase (lets Xcode handle linking) 46 | set(CMAKE_XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION) 47 | 48 | # Enable codesigning with secure timestamp when not in Debug configuration (required for Notarization) 49 | set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=Release] "--timestamp") 50 | set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=RelWithDebInfo] "--timestamp") 51 | set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=MinSizeRel] "--timestamp") 52 | 53 | # Enable codesigning with hardened runtime option when not in Debug configuration (required for Notarization) 54 | set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=Release] YES) 55 | set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=RelWithDebInfo] YES) 56 | set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=MinSizeRel] YES) 57 | 58 | # Disable injection of Xcode's base entitlements used for debugging when not in Debug configuration (required for 59 | # Notarization) 60 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=Release] NO) 61 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=RelWithDebInfo] NO) 62 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=MinSizeRel] NO) 63 | 64 | # Use Swift version 5.0 by default 65 | set(CMAKE_XCODE_ATTRIBUTE_SWIFT_VERSION 5.0) 66 | 67 | # Use DWARF with separate dSYM files when in Release or MinSizeRel configuration. 68 | # 69 | # * Currently overruled by CMake's Xcode generator, requires adding '-g' flag to raw compiler command line for desired 70 | # output configuration. Report to KitWare. 71 | # 72 | set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Debug] dwarf) 73 | set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=RelWithDebInfo] dwarf) 74 | set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] dwarf-with-dsym) 75 | set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=MinSizeRel] dwarf-with-dsym) 76 | 77 | # Make all symbols hidden by default (currently overriden by CMake's compiler flags) 78 | set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN YES) 79 | set(CMAKE_XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN YES) 80 | 81 | # Strip unused code 82 | set(CMAKE_XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING YES) 83 | 84 | # Display mangled names in Debug configuration 85 | set(CMAKE_XCODE_ATTRIBUTE_LINKER_DISPLAYS_MANGLED_NAMES[variant=Debug] YES) 86 | 87 | # Build active architecture only in Debug configuration 88 | set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES) 89 | 90 | # Enable testability in Debug configuration 91 | set(CMAKE_XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES) 92 | 93 | # Enable using ARC in ObjC by default 94 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES) 95 | # Enable weak references in manual retain release 96 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_WEAK YES) 97 | # Disable strict aliasing 98 | set(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO) 99 | 100 | # Set C++ language default to c17 101 | # 102 | # * CMake explicitly sets the version via compiler flag when transitive dependencies require specific compiler feature 103 | # set, resulting in the flag being added twice. Report to KitWare as a feature request for Xcode generator 104 | # * See also: https://gitlab.kitware.com/cmake/cmake/-/issues/17183 105 | # 106 | # set(CMAKE_XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD c17) 107 | # 108 | # Set C++ language default to c++17 109 | # 110 | # * See above. Report to KitWare as a feature request for Xcode generator 111 | # 112 | # set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD c++17) 113 | 114 | # Enable support for module imports in ObjC 115 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES YES) 116 | # Enable automatic linking of imported modules in ObjC 117 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_MODULES_AUTOLINK YES) 118 | # Enable strict msg_send rules for ObjC 119 | set(CMAKE_XCODE_ATTRIBUTE_ENABLE_STRICT_OBJC_MSGSEND YES) 120 | 121 | # Set default warnings for ObjC and C++ 122 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING YES_ERROR) 123 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_BOOL_CONVERSION YES) 124 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS YES) 125 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_COMMA YES) 126 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_CONSTANT_CONVERSION YES) 127 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_EMPTY_BODY YES) 128 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_ENUM_CONVERSION YES) 129 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_INFINITE_RECURSION YES) 130 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_INT_CONVERSION YES) 131 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_NON_LITERAL_NULL_CONVERSION YES) 132 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF YES) 133 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_OBJC_LITERAL_CONVERSION YES) 134 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK YES) 135 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER YES) 136 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_RANGE_LOOP_ANALYSIS YES) 137 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_STRICT_PROTOTYPES NO) 138 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION NO) 139 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_MOVE YES) 140 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_UNREACHABLE_CODE YES) 141 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN__DUPLICATE_METHOD_MATCH YES) 142 | 143 | # Set default warnings for C and C++ 144 | set(CMAKE_XCODE_ATTRIBUTE_GCC_NO_COMMON_BLOCKS YES) 145 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_64_TO_32_BIT_CONVERSION YES) 146 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS NO) 147 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_NEWLINE NO) 148 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_ABOUT_RETURN_TYPE YES_ERROR) 149 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_CHECK_SWITCH_STATEMENTS YES) 150 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_FOUR_CHARACTER_CONSTANTS YES) 151 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_SHADOW NO) 152 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_SIGN_COMPARE YES) 153 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_TYPECHECK_CALLS_TO_PRINTF YES) 154 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNDECLARED_SELECTOR YES) 155 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNINITIALIZED_AUTOS YES) 156 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_FUNCTION NO) 157 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_PARAMETER YES) 158 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_VALUE YES) 159 | set(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_VARIABLE YES) 160 | 161 | # Add additional warning compiler flags 162 | set(CMAKE_XCODE_ATTRIBUTE_WARNING_CFLAGS "-Wvla -Wformat-security") 163 | 164 | if(CMAKE_COMPILE_WARNING_AS_ERROR) 165 | set(CMAKE_XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS YES) 166 | endif() 167 | 168 | # Enable color diagnostics 169 | set(CMAKE_COLOR_DIAGNOSTICS TRUE) 170 | 171 | # Disable usage of RPATH in build or install configurations 172 | set(CMAKE_SKIP_RPATH TRUE) 173 | # Have Xcode set default RPATH entries 174 | set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/../Frameworks") 175 | --------------------------------------------------------------------------------