├── .clang-format ├── .cmake-format.json ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── config.yml ├── actions │ ├── build-plugin │ │ └── action.yaml │ ├── package-plugin │ │ └── action.yaml │ ├── run-clang-format │ │ └── action.yaml │ ├── run-cmake-format │ │ └── action.yaml │ └── setup-macos-codesigning │ │ └── action.yaml ├── scripts │ ├── .Aptfile │ ├── .Brewfile │ ├── .Wingetfile │ ├── .build.zsh │ ├── .package.zsh │ ├── Build-Windows.ps1 │ ├── Package-Windows.ps1 │ ├── build-linux │ ├── build-macos │ ├── package-linux │ ├── package-macos │ ├── utils.pwsh │ │ ├── Ensure-Location.ps1 │ │ ├── Expand-ArchiveExt.ps1 │ │ ├── Install-BuildDependencies.ps1 │ │ ├── Invoke-External.ps1 │ │ └── Logger.ps1 │ └── utils.zsh │ │ ├── check_linux │ │ ├── check_macos │ │ ├── log_debug │ │ ├── log_error │ │ ├── log_group │ │ ├── log_info │ │ ├── log_output │ │ ├── log_status │ │ ├── log_warning │ │ ├── mkcd │ │ ├── read_codesign │ │ ├── read_codesign_installer │ │ ├── read_codesign_pass │ │ ├── read_codesign_team │ │ ├── read_codesign_user │ │ ├── set_loglevel │ │ ├── setup_ccache │ │ └── setup_linux └── workflows │ ├── build-project.yaml │ ├── check-format.yaml │ ├── dispatch.yaml │ ├── pr-pull.yaml │ └── push.yaml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── build-aux ├── .functions │ ├── log_debug │ ├── log_error │ ├── log_group │ ├── log_info │ ├── log_output │ ├── log_status │ ├── log_warning │ └── set_loglevel ├── .run-format.zsh ├── run-clang-format ├── run-cmake-format └── run-swift-format ├── buildspec.json ├── cmake ├── .CMakeBuildNumber ├── FindDBus.cmake ├── common │ ├── bootstrap.cmake │ ├── buildnumber.cmake │ ├── buildspec_common.cmake │ ├── ccache.cmake │ ├── compiler_common.cmake │ ├── helpers_common.cmake │ └── osconfig.cmake ├── external │ └── FindLibcurl.cmake ├── linux │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ └── toolchains │ │ ├── aarch64-linux-clang.cmake │ │ ├── aarch64-linux-gcc.cmake │ │ ├── x86_64-linux-clang.cmake │ │ └── x86_64-linux-gcc.cmake ├── macos │ ├── buildspec.cmake │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ ├── resources │ │ ├── ccache-launcher-c.in │ │ ├── ccache-launcher-cxx.in │ │ ├── create-package.cmake.in │ │ ├── distribution.in │ │ └── installer-macos.pkgproj.in │ └── xcode.cmake └── windows │ ├── buildspec.cmake │ ├── compilerconfig.cmake │ ├── defaults.cmake │ ├── helpers.cmake │ └── resources │ ├── installer-Windows.iss.in │ └── resource.rc.in ├── data ├── locale │ ├── da-DK.ini │ ├── de-DE.ini │ ├── en-US.ini │ ├── es-ES.ini │ ├── fr-FR.ini │ ├── pt-BR.ini │ ├── ru-RU.ini │ ├── zh-CN.ini │ └── zh-TW.ini ├── lyrics.html ├── placeholder.png └── widget.html ├── deps ├── CMakeLists.txt ├── ConfigureChecks.cmake ├── cpp-httplib │ └── httplib.h ├── libmpd.config.h.in └── tuna_browser.user.js ├── format.sh ├── preview.png └── src ├── CMakeLists.txt ├── gui ├── images │ ├── icons.svg │ ├── icons │ │ ├── help_light.svg │ │ ├── next.svg │ │ ├── obs.png │ │ ├── pause.svg │ │ ├── play.svg │ │ ├── prev.svg │ │ ├── stop.svg │ │ ├── visible.svg │ │ ├── voldown.svg │ │ └── volup.svg │ └── tuna.png ├── music_control.cpp ├── music_control.hpp ├── music_control.ui ├── output_edit_dialog.cpp ├── output_edit_dialog.hpp ├── output_edit_dialog.ui ├── scrolltext.cpp ├── scrolltext.hpp ├── tuna.qrc ├── tuna_gui.cpp ├── tuna_gui.hpp ├── tuna_gui.ui └── widgets │ ├── icecast.cpp │ ├── icecast.hpp │ ├── icecast.ui │ ├── lastfm.cpp │ ├── lastfm.hpp │ ├── lastfm.ui │ ├── mpd.cpp │ ├── mpd.hpp │ ├── mpd.ui │ ├── mpris.cpp │ ├── mpris.hpp │ ├── mpris.ui │ ├── spotify.cpp │ ├── spotify.hpp │ ├── spotify.ui │ ├── vlc.cpp │ ├── vlc.hpp │ ├── vlc.ui │ ├── window_title.cpp │ ├── window_title.hpp │ ├── window_title.ui │ ├── wmc.cpp │ ├── wmc.hpp │ └── wmc.ui ├── plugin-macros.h.in ├── query ├── gpmdp_source.cpp ├── gpmdp_source.hpp ├── icecast_source.cpp ├── icecast_source.hpp ├── lastfm_source.cpp ├── lastfm_source.hpp ├── mpd_source.cpp ├── mpd_source.hpp ├── mpris_source.cpp ├── mpris_source.hpp ├── music_source.cpp ├── music_source.hpp ├── song.cpp ├── song.hpp ├── spotify_source.cpp ├── spotify_source.hpp ├── vlc_obs_source.cpp ├── vlc_obs_source.hpp ├── web_source.cpp ├── web_source.hpp ├── window_source.cpp ├── window_source.hpp ├── wmc_source.cpp └── wmc_source.hpp ├── source ├── progress.cpp └── progress.hpp ├── tuna_plugin.cpp └── util ├── config.cpp ├── config.hpp ├── constants.hpp ├── cover_tag_handler.cpp ├── cover_tag_handler.hpp ├── creds.hpp ├── format.cpp ├── format.hpp ├── lyrics_handler.cpp ├── lyrics_handler.hpp ├── tuna_thread.cpp ├── tuna_thread.hpp ├── utility.cpp ├── utility.hpp ├── web_server.cpp ├── web_server.hpp └── window ├── window_helper.hpp ├── window_helper_mac.mm ├── window_helper_nix.cpp └── window_helper_win.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | Standard: Cpp11 2 | Language: Cpp 3 | BasedOnStyle: WebKit 4 | SpaceAfterTemplateKeyword: false 5 | AlignEscapedNewlines: true 6 | AlignTrailingComments: true 7 | BreakBeforeInheritanceComma: true 8 | BreakConstructorInitializers: BeforeComma 9 | IndentPPDirectives: AfterHash 10 | BreakBeforeBraces: Custom 11 | BraceWrapping: 12 | AfterFunction: true 13 | AfterObjCDeclaration: false 14 | NamespaceIndentation: None 15 | ObjCBlockIndentWidth: 4 16 | ObjCSpaceAfterProperty: true 17 | ObjCSpaceBeforeProtocolList: true 18 | --- 19 | Language: ObjC 20 | -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "additional_commands": { 3 | "find_qt": { 4 | "flags": [], 5 | "kwargs": { 6 | "COMPONENTS": "+", 7 | "COMPONENTS_WIN": "+", 8 | "COMPONENTS_MACOS": "+", 9 | "COMPONENTS_LINUX": "+" 10 | } 11 | } 12 | }, 13 | "format": { 14 | "line_width": 100 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: vrsal.xyz/$ 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: "Bug report" 4 | about: "For reporting bugs or crashes" 5 | title: "issue title" 6 | ref: "main" 7 | labels: 8 | 9 | - bug 10 | - crash 11 | 12 | --- 13 | 14 | If you're unsure how to properly file a bug report read [this](https://vrsal.xyz/issues). 15 | I need as much information as possible otherwise I can't help you and I'm more inclined to 16 | just close your issue. 17 | 18 | **Describe the bug** 19 | A clear and concise description of what the bug is. 20 | 21 | **To Reproduce** 22 | Steps to reproduce the behavior: 23 | 1. Go to '...' 24 | 2. Click on '....' 25 | 3. Scroll down to '....' 26 | 4. See error 27 | 28 | **Expected behavior** 29 | A clear and concise description of what you expected to happen. 30 | 31 | **Screenshots** 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | **Log** 35 | [Add the link to your **entire** obs studio log](https://obsproject.com/forum/threads/please-post-a-log-with-your-issue-heres-how.23074/) 36 | 37 | Also add the **entire** crash report, if this issues is about a crash. 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.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@17/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-17 55 | brew install --quiet obsproject/tools/clang-format@17 56 | echo ::endgroup:: 57 | 58 | echo ::group::Run clang-format-17 59 | ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check 60 | echo ::endgroup:: 61 | } 62 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/scripts/.Aptfile: -------------------------------------------------------------------------------- 1 | package 'cmake' 2 | package 'ccache' 3 | package 'git' 4 | package 'jq' 5 | package 'curl' 6 | package 'ninja-build', bin: 'ninja' 7 | package 'pkg-config' 8 | package 'libcurl4-openssl-dev' 9 | package 'dbus' 10 | package 'libdbus-1-dev' 11 | -------------------------------------------------------------------------------- /.github/scripts/.Brewfile: -------------------------------------------------------------------------------- 1 | brew "ccache" 2 | brew "coreutils" 3 | brew "cmake" 4 | brew "git" 5 | brew "jq" 6 | brew "xcbeautify" 7 | brew "curl" 8 | -------------------------------------------------------------------------------- /.github/scripts/.Wingetfile: -------------------------------------------------------------------------------- 1 | package 'cmake', path: 'Cmake\bin', bin: 'cmake' 2 | package 'innosetup', path: 'Inno Setup 6', bin: 'iscc' 3 | -------------------------------------------------------------------------------- /.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 | $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}" 52 | 53 | if ( ! $SkipDeps ) { 54 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 55 | } 56 | 57 | $RemoveArgs = @{ 58 | ErrorAction = 'SilentlyContinue' 59 | Path = @( 60 | "${ProjectRoot}/release/${ProductName}-*-windows-*.zip" 61 | "${ProjectRoot}/release/${ProductName}-*-windows-*.exe" 62 | ) 63 | } 64 | 65 | Remove-Item @RemoveArgs 66 | 67 | Log-Group "Archiving ${ProductName}..." 68 | $CompressArgs = @{ 69 | Path = (Get-ChildItem -Path "${ProjectRoot}/release/${Configuration}" -Exclude "${OutputName}*.*") 70 | CompressionLevel = 'Optimal' 71 | DestinationPath = "${ProjectRoot}/release/${OutputName}.zip" 72 | Verbose = ($Env:CI -ne $null) 73 | } 74 | Compress-Archive -Force @CompressArgs 75 | Log-Group 76 | 77 | if ( ( $BuildInstaller ) ) { 78 | Log-Group "Packaging ${ProductName}..." 79 | 80 | $IsccFile = "${ProjectRoot}/build_${Target}/installer-Windows.generated.iss" 81 | if ( ! ( Test-Path -Path $IsccFile ) ) { 82 | throw 'InnoSetup install script not found. Run the build script or the CMake build and install procedures first.' 83 | } 84 | 85 | Log-Information 'Creating InnoSetup installer...' 86 | Push-Location -Stack BuildTemp 87 | Ensure-Location -Path "${ProjectRoot}/release" 88 | Copy-Item -Path ${Configuration} -Destination Package -Recurse 89 | Invoke-External iscc ${IsccFile} /O"${ProjectRoot}/release" /F"${OutputName}-Installer" 90 | Remove-Item -Path Package -Recurse 91 | Pop-Location -Stack BuildTemp 92 | 93 | Log-Group 94 | } 95 | } 96 | 97 | Package 98 | -------------------------------------------------------------------------------- /.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.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/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/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 | -------------------------------------------------------------------------------- /.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 --file ${SCRIPT_HOME}/.Brewfile 21 | rehash 22 | log_group 23 | -------------------------------------------------------------------------------- /.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_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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_warning: -------------------------------------------------------------------------------- 1 | if (( _loglevel > 0 )) { 2 | local icon=' =>' 3 | 4 | print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" 5 | } 6 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/mkcd: -------------------------------------------------------------------------------- 1 | [[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1} 2 | -------------------------------------------------------------------------------- /.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/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_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/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_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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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@v4 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@v4 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/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 | -------------------------------------------------------------------------------- /.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/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 | build-project: 18 | name: Build Project 🧱 19 | uses: ./.github/workflows/build-project.yaml 20 | secrets: inherit 21 | permissions: 22 | contents: read 23 | 24 | create-release: 25 | name: Create Release 🛫 26 | if: github.ref_type == 'tag' 27 | runs-on: ubuntu-22.04 28 | needs: build-project 29 | defaults: 30 | run: 31 | shell: bash 32 | steps: 33 | - name: Check Release Tag ☑️ 34 | id: check 35 | run: | 36 | : Check Release Tag ☑️ 37 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 38 | shopt -s extglob 39 | 40 | echo 'validTag=true' >> $GITHUB_OUTPUT 41 | echo 'prerelease=false' >> $GITHUB_OUTPUT 42 | echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT 43 | 44 | - name: Download Build Artifacts 📥 45 | uses: actions/download-artifact@v4 46 | if: fromJSON(steps.check.outputs.validTag) 47 | id: download 48 | 49 | - name: Rename Files 🏷️ 50 | if: fromJSON(steps.check.outputs.validTag) 51 | run: | 52 | : Rename Files 🏷️ 53 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 54 | shopt -s extglob 55 | shopt -s nullglob 56 | 57 | root_dir="$(pwd)" 58 | commit_hash="${GITHUB_SHA:0:9}" 59 | 60 | variants=( 61 | 'windows-x64;zip|exe' 62 | 'ubuntu-22.04-x86_64;tar.xz|deb|ddeb' 63 | 'sources;tar.xz' 64 | ) 65 | 66 | for variant_data in "${variants[@]}"; do 67 | IFS=';' read -r variant suffix <<< "${variant_data}" 68 | 69 | candidates=(*-${variant}-${commit_hash}/@(*|*-dbgsym).@(${suffix})) 70 | 71 | for candidate in "${candidates[@]}"; do 72 | mv "${candidate}" "${root_dir}" 73 | done 74 | done 75 | 76 | - name: Generate Checksums 🪪 77 | if: fromJSON(steps.check.outputs.validTag) 78 | run: | 79 | : Generate Checksums 🪪 80 | if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi 81 | shopt -s extglob 82 | 83 | echo "### Checksums" > ${{ github.workspace }}/CHECKSUMS.txt 84 | for file in ${{ github.workspace }}/@(*.exe|*.deb|*.ddeb|*.pkg|*.tar.xz|*.zip); do 85 | echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt 86 | done 87 | 88 | - name: Create Release 🛫 89 | if: fromJSON(steps.check.outputs.validTag) 90 | id: create_release 91 | uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 92 | with: 93 | draft: true 94 | prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }} 95 | tag_name: ${{ steps.check.outputs.version }} 96 | name: ${{ needs.build-project.outputs.pluginName }} ${{ steps.check.outputs.version }} 97 | body_path: ${{ github.workspace }}/CHECKSUMS.txt 98 | files: | 99 | ${{ github.workspace }}/*.exe 100 | ${{ github.workspace }}/*.zip 101 | ${{ github.workspace }}/*.pkg 102 | ${{ github.workspace }}/*.deb 103 | ${{ github.workspace }}/*.ddeb 104 | ${{ github.workspace }}/*.tar.xz 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | creds.txt 2 | *.autosave 3 | .DS_Store 4 | *.user 5 | build-tuna*/ 6 | .idea 7 | /cmake-build-debug/ 8 | *.generated.h 9 | build_*/ 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/taglib"] 2 | path = deps/taglib 3 | url = https://github.com/taglib/taglib 4 | [submodule "deps/libmpdclient"] 5 | path = deps/libmpdclient 6 | url = https://github.com/MusicPlayerDaemon/libmpdclient 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![header](./preview.png "tuna running on obs linux") 2 | 3 | [![Push to master](https://github.com/univrsal/tuna/actions/workflows/push.yaml/badge.svg)](https://github.com/univrsal/tuna/actions/workflows/push.yaml) 4 | # tuna 5 | Get song info from right within obs. 6 | 7 | Currently supports 8 | - Spotify 9 | - MPD 10 | - Any Window title 11 | - [last.fm](https://last.fm) scrobbling 12 | - OBS VLC source 13 | - [YouTube Music](https://github.com/th-ch/youtube-music) 14 | - Most music players through [MPRIS](https://specifications.freedesktop.org/mpris-spec/latest/) and [Windows Media Control](https://learn.microsoft.com/en-us/uwp/api/windows.media.control?view=winrt-19041) 15 | - Through [tampermonkey script](https://github.com/univrsal/tuna/raw/master/deps/tuna_browser.user.js): 16 | - Soundcloud 17 | - Spotify Web Player 18 | - Deezer 19 | - Yandex Music 20 | - Pretzel.rocks 21 | 22 | Lyrics for the lyrics html overlay are served via [lrclib](https://github.com/tranxuanthang/lrclib) hosted at lyrics.vrsal.cc. 23 | 24 | hey tuna 25 | 26 | ### Translators 27 | - [COOLIGUAY](https://github.com/COOLIGUAY) (Spanish) 28 | - [dEN5-tech](https://github.com/dEN5-tech) (Russian) 29 | - [libraV3](https://github.com/libraV3) (Russian) 30 | - [Cyame](https://github.com/Cyame) (Simplified Chinese) 31 | - [KaiLee2588](https://github.com/KaiLee2588) (Simplified and Traditional Chinese) 32 | - [gabrielpastori1](https://github.com/gabrielpastori1) (Brazilian Portuguese) 33 | - [orion78fr](https://github.com/orion78fr) (French) 34 | - [jegadk](https://obsproject.com/forum/members/jegadk.185246/) (Danish) 35 | 36 | ### Additional credits 37 | 38 | - MPRIS and Windows Media Control support taken from [obs_media_info_plugin](https://github.com/rmoalic/obs_media_info_plugin). 39 | - Metadata extraction via [Taglib](https://taglib.org/) 40 | - MPD connection via [libmpdclient](https://musicpd.org/libs/libmpdclient/) 41 | - Webserver implemented through [cpp-httplib](https://github.com/yhirose/cpp-httplib) 42 | - [cURL](https://curl.se) for fetching remote content and API interactions 43 | -------------------------------------------------------------------------------- /build-aux/.functions/log_debug: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f" 4 | -------------------------------------------------------------------------------- /build-aux/.functions/log_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /build-aux/.functions/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 | -------------------------------------------------------------------------------- /build-aux/.functions/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 | -------------------------------------------------------------------------------- /build-aux/.functions/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 | -------------------------------------------------------------------------------- /build-aux/.functions/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 | -------------------------------------------------------------------------------- /build-aux/.functions/log_warning: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f" 7 | } 8 | -------------------------------------------------------------------------------- /build-aux/.functions/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 | -------------------------------------------------------------------------------- /build-aux/run-clang-format: -------------------------------------------------------------------------------- 1 | .run-format.zsh -------------------------------------------------------------------------------- /build-aux/run-cmake-format: -------------------------------------------------------------------------------- 1 | .run-format.zsh -------------------------------------------------------------------------------- /build-aux/run-swift-format: -------------------------------------------------------------------------------- 1 | .run-format.zsh -------------------------------------------------------------------------------- /buildspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "obs-studio": { 4 | "version": "30.1.2", 5 | "baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags", 6 | "label": "OBS sources", 7 | "hashes": { 8 | "macos": "490bae1c392b3b344b0270afd8cb887da4bc50bd92c0c426e96713c1ccb9701a", 9 | "windows-x64": "c2dd03fa7fd01fad5beafce8f7156da11f9ed9a588373fd40b44a06f4c03b867" 10 | } 11 | }, 12 | "prebuilt": { 13 | "version": "2024-03-19", 14 | "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", 15 | "label": "Pre-Built obs-deps", 16 | "hashes": { 17 | "macos": "2e9bfb55a5e0e4c1086fa1fda4cf268debfead473089df2aaea80e1c7a3ca7ff", 18 | "windows-x64": "6e86068371526a967e805f6f9903f9407adb683c21820db5f07da8f30d11e998" 19 | } 20 | }, 21 | "qt6": { 22 | "version": "2024-03-19", 23 | "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", 24 | "label": "Pre-Built Qt6", 25 | "hashes": { 26 | "macos": "694f1e639c017e3b1f456f735330dc5afae287cbea85757101af1368de3142c8", 27 | "windows-x64": "72d1df34a0ef7413a681d5fcc88cae81da60adc03dcd23ef17862ab170bcc0dd" 28 | }, 29 | "debugSymbols": { 30 | "windows-x64": "fbddd1f659c360f2291911ac5709b67b6f8182e6bca519d24712e4f6fd3cc865" 31 | } 32 | } 33 | }, 34 | "platformConfig": { 35 | "macos": { 36 | "bundleId": "cc.vrsal.tuna" 37 | } 38 | }, 39 | "name": "tuna", 40 | "displayName": "Tuna", 41 | "version": "1.9.10", 42 | "author": "univrsal", 43 | "website": "https://vrsal.cc", 44 | "email": "contact@vrsal.xyz", 45 | "uuids": { 46 | "windowsApp": "44b785ed-eef5-4b73-bef0-42ee3493c021" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cmake/.CMakeBuildNumber: -------------------------------------------------------------------------------- 1 | 73 -------------------------------------------------------------------------------- /cmake/FindDBus.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the low-level D-Bus library 2 | # Once done this will define 3 | # 4 | # DBUS_FOUND - system has D-Bus 5 | # DBUS_INCLUDE_DIR - the D-Bus include directory 6 | # DBUS_ARCH_INCLUDE_DIR - the D-Bus architecture-specific include directory 7 | # DBUS_LIBRARIES - the libraries needed to use D-Bus 8 | 9 | # Copyright (c) 2008, Kevin Kofler, 10 | # modeled after FindLibArt.cmake: 11 | # Copyright (c) 2006, Alexander Neundorf, 12 | # 13 | # Redistribution and use is allowed according to the terms of the BSD license. 14 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 15 | 16 | if (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) 17 | 18 | # in cache already 19 | SET(DBUS_FOUND TRUE) 20 | 21 | else (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) 22 | 23 | IF (NOT WIN32) 24 | FIND_PACKAGE(PkgConfig) 25 | IF (PKG_CONFIG_FOUND) 26 | # use pkg-config to get the directories and then use these values 27 | # in the FIND_PATH() and FIND_LIBRARY() calls 28 | pkg_check_modules(_DBUS_PC QUIET dbus-1) 29 | ENDIF (PKG_CONFIG_FOUND) 30 | ENDIF (NOT WIN32) 31 | 32 | FIND_PATH(DBUS_INCLUDE_DIR dbus/dbus.h 33 | ${_DBUS_PC_INCLUDE_DIRS} 34 | /usr/include 35 | /usr/include/dbus-1.0 36 | /usr/local/include 37 | ) 38 | 39 | FIND_PATH(DBUS_ARCH_INCLUDE_DIR dbus/dbus-arch-deps.h 40 | ${_DBUS_PC_INCLUDE_DIRS} 41 | /usr/lib${LIB_SUFFIX}/include 42 | /usr/lib${LIB_SUFFIX}/dbus-1.0/include 43 | /usr/lib64/include 44 | /usr/lib64/dbus-1.0/include 45 | /usr/lib/include 46 | /usr/lib/dbus-1.0/include 47 | ) 48 | 49 | FIND_LIBRARY(DBUS_LIBRARIES NAMES dbus-1 dbus 50 | PATHS 51 | ${_DBUS_PC_LIBDIR} 52 | ) 53 | 54 | 55 | if (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) 56 | set(DBUS_FOUND TRUE) 57 | endif (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) 58 | 59 | 60 | if (DBUS_FOUND) 61 | if (NOT DBus_FIND_QUIETLY) 62 | message(STATUS "Found D-Bus: ${DBUS_LIBRARIES}") 63 | endif (NOT DBus_FIND_QUIETLY) 64 | else (DBUS_FOUND) 65 | if (DBus_FIND_REQUIRED) 66 | message(FATAL_ERROR "Could NOT find D-Bus") 67 | endif (DBus_FIND_REQUIRED) 68 | endif (DBUS_FOUND) 69 | 70 | MARK_AS_ADVANCED(DBUS_INCLUDE_DIR DBUS_ARCH_INCLUDE_DIR DBUS_LIBRARIES) 71 | 72 | endif (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) 73 | -------------------------------------------------------------------------------- /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 _windowsAppUUID GET ${buildspec} uuids windowsApp) 53 | # cmake-format: on 54 | 55 | set(PLUGIN_AUTHOR ${_author}) 56 | set(PLUGIN_WEBSITE ${_website}) 57 | set(PLUGIN_EMAIL ${_email}) 58 | set(PLUGIN_VERSION ${_version}) 59 | set(MACOS_BUNDLEID ${_bundleId}) 60 | 61 | include(buildnumber) 62 | include(osconfig) 63 | 64 | # Allow selection of common build types via UI 65 | if(NOT CMAKE_BUILD_TYPE) 66 | set(CMAKE_BUILD_TYPE 67 | "RelWithDebInfo" 68 | CACHE STRING "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" FORCE) 69 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel) 70 | endif() 71 | 72 | # Disable exports automatically going into the CMake package registry 73 | set(CMAKE_EXPORT_PACKAGE_REGISTRY FALSE) 74 | # Enable default inclusion of targets' source and binary directory 75 | set(CMAKE_INCLUDE_CURRENT_DIR TRUE) 76 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -Wnewline-eof 38 | -Wno-conversion 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/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 | # check_uuid: Helper function to check for valid UUID 10 | function(check_uuid uuid_string return_value) 11 | set(valid_uuid TRUE) 12 | set(uuid_token_lengths 8 4 4 4 12) 13 | set(token_num 0) 14 | 15 | string(REPLACE "-" ";" uuid_tokens ${uuid_string}) 16 | list(LENGTH uuid_tokens uuid_num_tokens) 17 | 18 | if(uuid_num_tokens EQUAL 5) 19 | message(DEBUG "UUID ${uuid_string} is valid with 5 tokens.") 20 | foreach(uuid_token IN LISTS uuid_tokens) 21 | list(GET uuid_token_lengths ${token_num} uuid_target_length) 22 | string(LENGTH "${uuid_token}" uuid_actual_length) 23 | if(uuid_actual_length EQUAL uuid_target_length) 24 | string(REGEX MATCH "[0-9a-fA-F]+" uuid_hex_match ${uuid_token}) 25 | if(NOT uuid_hex_match STREQUAL uuid_token) 26 | set(valid_uuid FALSE) 27 | break() 28 | endif() 29 | else() 30 | set(valid_uuid FALSE) 31 | break() 32 | endif() 33 | math(EXPR token_num "${token_num}+1") 34 | endforeach() 35 | else() 36 | set(valid_uuid FALSE) 37 | endif() 38 | message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") 39 | set(${return_value} 40 | ${valid_uuid} 41 | PARENT_SCOPE) 42 | endfunction() 43 | 44 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/plugin-support.c.in") 45 | configure_file(src/plugin-support.c.in plugin-support.c @ONLY) 46 | add_library(plugin-support STATIC) 47 | target_sources( 48 | plugin-support 49 | PRIVATE plugin-support.c 50 | PUBLIC src/plugin-support.h) 51 | target_include_directories(plugin-support PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") 52 | if(OS_LINUX 53 | OR OS_FREEBSD 54 | OR OS_OPENBSD) 55 | # add fPIC on Linux to prevent shared object errors 56 | set_property(TARGET plugin-support PROPERTY POSITION_INDEPENDENT_CODE ON) 57 | endif() 58 | endif() 59 | -------------------------------------------------------------------------------- /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/external/FindLibcurl.cmake: -------------------------------------------------------------------------------- 1 | 2 | # Once done these will be defined: 3 | # 4 | # LIBCURL_FOUND 5 | # LIBCURL_INCLUDE_DIRS 6 | # LIBCURL_LIBRARIES 7 | # 8 | # For use in OBS: 9 | # 10 | # CURL_INCLUDE_DIR 11 | 12 | find_package(PkgConfig QUIET) 13 | if (PKG_CONFIG_FOUND) 14 | pkg_check_modules(_CURL QUIET curl libcurl) 15 | endif() 16 | 17 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 18 | set(_lib_suffix 64) 19 | else() 20 | set(_lib_suffix 32) 21 | endif() 22 | find_path(CURL_INCLUDE_DIR 23 | NAMES curl/curl.h 24 | HINTS 25 | ENV curlPath${_lib_suffix} 26 | ENV curlPath 27 | ENV DepsPath${_lib_suffix} 28 | ENV DepsPath 29 | ${curlPath${_lib_suffix}} 30 | ${curlPath} 31 | ${DepsPath${_lib_suffix}} 32 | ${DepsPath} 33 | ${_CURL_INCLUDE_DIRS} 34 | PATHS 35 | /usr/include /usr/local/include /opt/local/include /sw/include 36 | PATH_SUFFIXES 37 | include) 38 | 39 | find_library(CURL_LIB 40 | NAMES ${_CURL_LIBRARIES} curl libcurl 41 | HINTS 42 | ENV curlPath${_lib_suffix} 43 | ENV curlPath 44 | ENV DepsPath${_lib_suffix} 45 | ENV DepsPath 46 | ${curlPath${_lib_suffix}} 47 | ${curlPath} 48 | ${DepsPath${_lib_suffix}} 49 | ${DepsPath} 50 | ${_CURL_LIBRARY_DIRS} 51 | PATHS 52 | /usr/lib /usr/local/lib /opt/local/lib /sw/lib 53 | PATH_SUFFIXES 54 | lib${_lib_suffix} lib 55 | libs${_lib_suffix} libs 56 | bin${_lib_suffix} bin 57 | ../lib${_lib_suffix} ../lib 58 | ../libs${_lib_suffix} ../libs 59 | ../bin${_lib_suffix} ../bin 60 | "build/Win${_lib_suffix}/VC12/DLL Release - DLL Windows SSPI" 61 | "../build/Win${_lib_suffix}/VC12/DLL Release - DLL Windows SSPI") 62 | 63 | include(FindPackageHandleStandardArgs) 64 | find_package_handle_standard_args(Libcurl DEFAULT_MSG CURL_LIB CURL_INCLUDE_DIR) 65 | mark_as_advanced(CURL_INCLUDE_DIR CURL_LIB) 66 | 67 | if(LIBCURL_FOUND) 68 | set(LIBCURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) 69 | set(LIBCURL_LIBRARIES ${CURL_LIB}) 70 | endif() 71 | -------------------------------------------------------------------------------- /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 | -Wdeprecated-declarations 17 | -Wempty-body 18 | -Wenum-conversion 19 | -Werror=return-type 20 | -Wextra 21 | -Wformat 22 | -Wformat-security 23 | -Wno-conversion 24 | -Wno-float-conversion 25 | -Wno-implicit-fallthrough 26 | -Wno-missing-braces 27 | -Wno-missing-field-initializers 28 | -Wno-shadow 29 | -Wno-sign-conversion 30 | -Wno-trigraphs 31 | -Wno-unknown-pragmas 32 | -Wno-unused-function 33 | -Wno-unused-label 34 | -Wparentheses 35 | -Wshadow 36 | -Wuninitialized 37 | -Wunreachable-code 38 | -Wunused-parameter 39 | -Wunused-value 40 | -Wunused-variable 41 | -Wvla) 42 | 43 | # gcc options for C++ 44 | set(_obs_gcc_cxx_options 45 | # cmake-format: sortable 46 | ${_obs_gcc_c_options} -Wconversion -Wfloat-conversion -Winvalid-offsetof -Wno-overloaded-virtual) 47 | 48 | add_compile_options( 49 | -fopenmp-simd 50 | "$<$:${_obs_gcc_c_options}>" 51 | "$<$:-Wint-conversion;-Wno-missing-prototypes;-Wno-strict-prototypes;-Wpointer-sign>" 52 | "$<$:${_obs_gcc_cxx_options}>" 53 | "$<$:${_obs_clang_c_options}>" 54 | "$<$:${_obs_clang_cxx_options}>") 55 | 56 | # Add support for color diagnostics and CMake switch for warnings as errors to CMake < 3.24 57 | if(CMAKE_VERSION VERSION_LESS 3.24.0) 58 | add_compile_options($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) 59 | if(CMAKE_COMPILE_WARNING_AS_ERROR) 60 | add_compile_options(-Werror) 61 | endif() 62 | else() 63 | set(CMAKE_COLOR_DIAGNOSTICS ON) 64 | endif() 65 | 66 | if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) 67 | # Disable false-positive warning in GCC 12.1.0 and later 68 | add_compile_options(-Wno-error=maybe-uninitialized) 69 | 70 | # Add warning for infinite recursion (added in GCC 12) 71 | if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0) 72 | add_compile_options(-Winfinite-recursion) 73 | endif() 74 | endif() 75 | 76 | # Enable compiler and build tracing (requires Ninja generator) 77 | if(ENABLE_COMPILER_TRACE AND CMAKE_GENERATOR STREQUAL "Ninja") 78 | add_compile_options($<$:-ftime-trace> $<$:-ftime-trace>) 79 | else() 80 | set(ENABLE_COMPILER_TRACE 81 | OFF 82 | CACHE STRING "Enable Clang time-trace (required Clang and Ninja)" FORCE) 83 | endif() 84 | 85 | add_compile_definitions($<$:DEBUG> $<$:_DEBUG> SIMDE_ENABLE_OPENMP) 86 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /cmake/macos/compilerconfig.cmake: -------------------------------------------------------------------------------- 1 | # OBS CMake macOS compiler configuration module 2 | 3 | include_guard(GLOBAL) 4 | 5 | option(ENABLE_COMPILER_TRACE "Enable clang time-trace" OFF) 6 | mark_as_advanced(ENABLE_COMPILER_TRACE) 7 | 8 | if(NOT XCODE) 9 | message(FATAL_ERROR "Building OBS Studio on macOS requires Xcode generator.") 10 | endif() 11 | 12 | include(ccache) 13 | include(compiler_common) 14 | 15 | add_compile_options("$<$>:-fopenmp-simd>") 16 | 17 | # Enable selection between arm64 and x86_64 targets 18 | if(NOT CMAKE_OSX_ARCHITECTURES) 19 | set(CMAKE_OSX_ARCHITECTURES arm64 CACHE STRING "Build architectures for macOS" FORCE) 20 | endif() 21 | set_property(CACHE CMAKE_OSX_ARCHITECTURES PROPERTY STRINGS arm64 x86_64) 22 | 23 | # Ensure recent enough Xcode and platform SDK 24 | function(check_sdk_requirements) 25 | set(_obs_macos_minimum_sdk 14.2) # Keep in sync with Xcode 26 | set(_obs_macos_minimum_xcode 15.1) # Keep in sync with SDK 27 | execute_process( 28 | COMMAND xcrun --sdk macosx --show-sdk-platform-version 29 | OUTPUT_VARIABLE obs_macos_current_sdk 30 | RESULT_VARIABLE result 31 | OUTPUT_STRIP_TRAILING_WHITESPACE 32 | ) 33 | if(NOT result EQUAL 0) 34 | message( 35 | FATAL_ERROR 36 | "Failed to fetch macOS SDK version. " 37 | "Ensure that the macOS SDK is installed and that xcode-select points at the Xcode developer directory." 38 | ) 39 | endif() 40 | message(DEBUG "macOS SDK version: ${obs_macos_current_sdk}") 41 | if(obs_macos_current_sdk VERSION_LESS obs_macos_minimum_sdk) 42 | message( 43 | FATAL_ERROR 44 | "Your macOS SDK version (${obs_macos_current_sdk}) is too low. " 45 | "The macOS ${obs_macos_minimum_sdk} SDK (Xcode ${obs_macos_minimum_xcode}) is required to build OBS." 46 | ) 47 | endif() 48 | execute_process(COMMAND xcrun --find xcodebuild OUTPUT_VARIABLE obs_macos_xcodebuild RESULT_VARIABLE result) 49 | if(NOT result EQUAL 0) 50 | message( 51 | FATAL_ERROR 52 | "Xcode was not found. " 53 | "Ensure you have installed Xcode and that xcode-select points at the Xcode developer directory." 54 | ) 55 | endif() 56 | message(DEBUG "Path to xcodebuild binary: ${obs_macos_xcodebuild}") 57 | if(XCODE_VERSION VERSION_LESS obs_macos_minimum_xcode) 58 | message( 59 | FATAL_ERROR 60 | "Your Xcode version (${XCODE_VERSION}) is too low. Xcode ${obs_macos_minimum_xcode} is required to build OBS." 61 | ) 62 | endif() 63 | endfunction() 64 | 65 | check_sdk_requirements() 66 | 67 | # Enable dSYM generator for release builds 68 | string(APPEND CMAKE_C_FLAGS_RELEASE " -g") 69 | string(APPEND CMAKE_CXX_FLAGS_RELEASE " -g") 70 | string(APPEND CMAKE_OBJC_FLAGS_RELEASE " -g") 71 | string(APPEND CMAKE_OBJCXX_FLAGS_RELEASE " -g") 72 | 73 | # Default ObjC compiler options used by Xcode: 74 | # 75 | # * -Wno-implicit-atomic-properties 76 | # * -Wno-objc-interface-ivars 77 | # * -Warc-repeated-use-of-weak 78 | # * -Wno-arc-maybe-repeated-use-of-weak 79 | # * -Wimplicit-retain-self 80 | # * -Wduplicate-method-match 81 | # * -Wshadow 82 | # * -Wfloat-conversion 83 | # * -Wobjc-literal-conversion 84 | # * -Wno-selector 85 | # * -Wno-strict-selector-match 86 | # * -Wundeclared-selector 87 | # * -Wdeprecated-implementations 88 | # * -Wprotocol 89 | # * -Werror=block-capture-autoreleasing 90 | # * -Wrange-loop-analysis 91 | 92 | # Default ObjC++ compiler options used by Xcode: 93 | # 94 | # * -Wno-non-virtual-dtor 95 | 96 | add_compile_definitions( 97 | $<$>:$<$:DEBUG>> 98 | $<$>:$<$:_DEBUG>> 99 | $<$>:SIMDE_ENABLE_OPENMP> 100 | ) 101 | 102 | if(ENABLE_COMPILER_TRACE) 103 | add_compile_options( 104 | $<$>:-ftime-trace> 105 | "$<$:SHELL:-Xfrontend -debug-time-expression-type-checking>" 106 | "$<$:SHELL:-Xfrontend -debug-time-function-bodies>" 107 | ) 108 | add_link_options(LINKER:-print_statistics) 109 | endif() 110 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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 | # CMake 3.25 changed the way symbol generation is handled on Windows 13 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.25.0) 14 | if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") 15 | set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT ProgramDatabase) 16 | else() 17 | set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded) 18 | endif() 19 | endif() 20 | 21 | message(DEBUG "Current Windows API version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") 22 | if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) 23 | message(DEBUG "Maximum Windows API version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM}") 24 | endif() 25 | 26 | if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS 10.0.20348) 27 | message(FATAL_ERROR "OBS requires Windows 10 SDK version 10.0.20348.0 or more recent.\n" 28 | "Please download and install the most recent Windows platform SDK.") 29 | endif() 30 | 31 | add_compile_options( 32 | /W3 33 | /utf-8 34 | "$<$:/MP>" 35 | "$<$:/MP>" 36 | "$<$:${_obs_clang_c_options}>" 37 | "$<$:${_obs_clang_cxx_options}>" 38 | $<$>:/Gy>) 39 | 40 | add_compile_definitions(UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS $<$:DEBUG> 41 | $<$:_DEBUG>) 42 | 43 | # cmake-format: off 44 | add_link_options($<$>:/OPT:REF> 45 | $<$>:/OPT:ICF> 46 | $<$>:/INCREMENTAL:NO> 47 | /DEBUG 48 | /Brepro) 49 | # cmake-format: on 50 | 51 | if(CMAKE_COMPILE_WARNING_AS_ERROR) 52 | add_link_options(/WX) 53 | endif() 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /data/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/univrsal/tuna/15d6488e275e0e512bf87db793f09f2cb886b17e/data/placeholder.png -------------------------------------------------------------------------------- /deps/libmpd.config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define DEFAULT_HOST "@MPD_DEFAULT_HOST@" 3 | #define DEFAULT_PORT @MPD_DEFAULT_PORT@ 4 | #define ENABLE_TCP 5 | #define PACKAGE "libmpdclient" 6 | #define VERSION "@MPD_VERSION@" 7 | #define DEFAULT_SOCKET "@MPD_DEFAULT_SOCKET@" 8 | 9 | #cmakedefine HAVE_GETADDRINFO 10 | #cmakedefine HAVE_STRNDUP 11 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | find ./src -iname *.mm -o -iname *.h* -o -iname *.c* | xargs clang-format -style=File -i -verbose 3 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/univrsal/tuna/15d6488e275e0e512bf87db793f09f2cb886b17e/preview.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(${CMAKE_PROJECT_NAME} PRIVATE 2 | ./gui/tuna.qrc 3 | ./gui/tuna_gui.ui 4 | ./gui/output_edit_dialog.ui 5 | ./gui/music_control.ui 6 | ./gui/widgets/lastfm.ui 7 | ./gui/widgets/mpd.ui 8 | ./gui/widgets/window_title.ui 9 | ./gui/widgets/spotify.ui 10 | ./gui/widgets/icecast.ui 11 | ./gui/widgets/vlc.ui 12 | ./tuna_plugin.cpp 13 | ./util/constants.hpp 14 | ./util/config.cpp 15 | ./util/config.hpp 16 | ./util/creds.hpp 17 | ./gui/tuna_gui.cpp 18 | ./gui/tuna_gui.hpp 19 | ./gui/output_edit_dialog.cpp 20 | ./gui/output_edit_dialog.hpp 21 | ./gui/music_control.cpp 22 | ./gui/music_control.hpp 23 | ./gui/scrolltext.cpp 24 | ./gui/scrolltext.hpp 25 | ./query/music_source.hpp 26 | ./query/music_source.cpp 27 | ./query/spotify_source.cpp 28 | ./query/spotify_source.hpp 29 | ./query/mpd_source.cpp 30 | ./query/mpd_source.hpp 31 | ./query/window_source.cpp 32 | ./query/window_source.hpp 33 | ./query/lastfm_source.cpp 34 | ./query/lastfm_source.hpp 35 | ./query/gpmdp_source.cpp 36 | ./query/gpmdp_source.hpp 37 | ./query/web_source.cpp 38 | ./query/web_source.hpp 39 | ./query/icecast_source.cpp 40 | ./query/icecast_source.hpp 41 | ./query/song.cpp 42 | ./query/song.hpp 43 | ./util/format.cpp 44 | ./util/format.hpp 45 | ./source/progress.cpp 46 | ./source/progress.hpp 47 | ./util/lyrics_handler.cpp 48 | ./util/lyrics_handler.hpp 49 | ./util/cover_tag_handler.cpp 50 | ./util/cover_tag_handler.hpp 51 | ./query/vlc_obs_source.cpp 52 | ./query/vlc_obs_source.hpp 53 | ./util/tuna_thread.cpp 54 | ./util/tuna_thread.hpp 55 | ./util/utility.cpp 56 | ./util/utility.hpp 57 | ./util/web_server.cpp 58 | ./util/web_server.hpp 59 | ./util/window/window_helper.hpp 60 | ./gui/widgets/lastfm.cpp 61 | ./gui/widgets/lastfm.hpp 62 | ./gui/widgets/mpd.cpp 63 | ./gui/widgets/mpd.hpp 64 | ./gui/widgets/window_title.cpp 65 | ./gui/widgets/window_title.hpp 66 | ./gui/widgets/icecast.cpp 67 | ./gui/widgets/icecast.hpp 68 | ./gui/widgets/spotify.cpp 69 | ./gui/widgets/spotify.hpp 70 | ./gui/widgets/vlc.cpp 71 | ./gui/widgets/vlc.hpp 72 | ) 73 | 74 | if (WIN32) 75 | target_sources(${CMAKE_PROJECT_NAME} PUBLIC "./util/window/window_helper_win.cpp") 76 | elseif(UNIX AND NOT APPLE) 77 | target_sources(${CMAKE_PROJECT_NAME} PUBLIC "./util/window/window_helper_nix.cpp") 78 | elseif(APPLE) 79 | target_sources(${CMAKE_PROJECT_NAME} PUBLIC "./util/window/window_helper_mac.mm") 80 | endif() 81 | -------------------------------------------------------------------------------- /src/gui/images/icons/help_light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/gui/images/icons/obs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/univrsal/tuna/15d6488e275e0e512bf87db793f09f2cb886b17e/src/gui/images/icons/obs.png -------------------------------------------------------------------------------- /src/gui/images/icons/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 60 | 68 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/gui/images/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/gui/images/icons/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/gui/images/icons/visible.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 28 | 48 | 54 | 55 | -------------------------------------------------------------------------------- /src/gui/images/icons/voldown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 60 | 65 | 69 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/gui/images/icons/volup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 56 | 60 | 65 | 69 | 78 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/gui/images/tuna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/univrsal/tuna/15d6488e275e0e512bf87db793f09f2cb886b17e/src/gui/images/tuna.png -------------------------------------------------------------------------------- /src/gui/music_control.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "scrolltext.hpp" 22 | #include 23 | #include 24 | #include 25 | 26 | class music_source; 27 | 28 | namespace Ui { 29 | class music_control; 30 | } 31 | 32 | class music_control : public QDockWidget { 33 | Q_OBJECT 34 | 35 | public: 36 | explicit music_control(QWidget* parent = nullptr); 37 | ~music_control(); 38 | 39 | void add_source(const QString& display, const QString& id); 40 | 41 | void select_source(int index); 42 | 43 | private slots: 44 | void refresh_play_state(); 45 | void showcontextmenu(const QPoint& pos); 46 | void toggle_title(); 47 | void toggle_volume(); 48 | void toggle_source(); 49 | void source_changed(int index); 50 | 51 | void on_btn_prev_clicked(); 52 | void on_btn_play_pause_clicked(); 53 | void on_btn_next_clicked(); 54 | void on_btn_stop_clicked(); 55 | void on_btn_voldown_clicked(); 56 | void on_btn_volup_clicked(); 57 | 58 | private: 59 | void save_settings(); 60 | void refresh_source(); 61 | bool last_thread_state = false; 62 | Ui::music_control* ui; 63 | QTimer* m_timer = nullptr; 64 | scroll_text* m_song_text = nullptr; 65 | }; 66 | 67 | extern music_control* music_dock; 68 | -------------------------------------------------------------------------------- /src/gui/output_edit_dialog.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | namespace Ui { 24 | class output_edit_dialog; 25 | } 26 | 27 | class tuna_gui; 28 | enum class edit_mode { create, 29 | modify }; 30 | 31 | class output_edit_dialog : public QDialog { 32 | Q_OBJECT 33 | public: 34 | output_edit_dialog(edit_mode m, QWidget* parent = nullptr); 35 | ~output_edit_dialog(); 36 | 37 | private slots: 38 | void browse_clicked(); 39 | void accept_clicked(); 40 | void format_changed(const QString& text); 41 | 42 | private: 43 | Ui::output_edit_dialog* ui; 44 | edit_mode m_mode; 45 | tuna_gui* m_tuna; 46 | }; 47 | -------------------------------------------------------------------------------- /src/gui/scrolltext.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | * 18 | * 19 | * Thanks StackOverflow, very cool 20 | * https://stackoverflow.com/a/10655396 21 | *************************************************************************/ 22 | 23 | #pragma once 24 | #include 25 | #include 26 | #include 27 | 28 | class scroll_text : public QWidget { 29 | Q_OBJECT 30 | Q_PROPERTY(QString text READ text WRITE set_text) 31 | Q_PROPERTY(QString separator READ separator WRITE set_separator) 32 | 33 | public: 34 | explicit scroll_text(QWidget* parent = nullptr); 35 | 36 | QString text() const; 37 | void set_text(QString text); 38 | 39 | QString separator() const; 40 | void set_separator(QString separator); 41 | 42 | protected: 43 | virtual void paintEvent(QPaintEvent*); 44 | virtual void resizeEvent(QResizeEvent*); 45 | 46 | private: 47 | void update_text(); 48 | QString m_text; 49 | QString m_separator; 50 | QStaticText m_static_text; 51 | int m_single_text_width; 52 | QSize m_whole_text_size; 53 | int m_left_margin; 54 | bool m_scroll_enabled; 55 | int m_scroll_pos; 56 | QImage m_alpha_channel; 57 | QImage m_buffer; 58 | QTimer m_timer; 59 | 60 | private slots: 61 | virtual void timer_timeout(); 62 | }; 63 | -------------------------------------------------------------------------------- /src/gui/tuna.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/tuna.png 4 | images/icons/pause.svg 5 | images/icons/prev.svg 6 | images/icons/stop.svg 7 | images/icons/next.svg 8 | images/icons/play.svg 9 | images/icons/volup.svg 10 | images/icons/voldown.svg 11 | images/icons/visible.svg 12 | images/icons/obs.png 13 | images/icons/help_light.svg 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/gui/tuna_gui.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace Ui { 26 | class tuna_gui; 27 | } 28 | 29 | class source_widget : public QWidget { 30 | Q_OBJECT 31 | public: 32 | explicit source_widget(QWidget* parent = nullptr) 33 | : QWidget(parent) 34 | { 35 | } 36 | virtual void save_settings() = 0; 37 | virtual void load_settings() = 0; 38 | virtual void tick() { } 39 | }; 40 | 41 | class tuna_gui : public QDialog { 42 | Q_OBJECT 43 | 44 | QList m_source_widgets; 45 | QTimer* m_refresh = nullptr; 46 | 47 | public: 48 | explicit tuna_gui(QWidget* parent = nullptr); 49 | 50 | ~tuna_gui(); 51 | 52 | void toggleShowHide(); 53 | void add_output(const QString& format, const QString& path, bool log_mode); 54 | void edit_output(const QString& format, const QString& path, bool log_mode); 55 | void get_selected_output(QString& format, QString& path, bool& log_mode); 56 | 57 | void add_source(const QString& display, const QString& id, source_widget* w); 58 | void refresh(); 59 | 60 | void select_source(int index); 61 | private slots: 62 | /* Element interactions */ 63 | void apply_pressed(); 64 | void tuna_gui_accepted(); 65 | void btn_start_clicked(); 66 | void set_state(); 67 | void btn_stop_clicked(); 68 | void btn_browse_song_cover_clicked(); 69 | void btn_browse_song_lyrics_clicked(); 70 | void btn_add_output_clicked(); 71 | void btn_remove_output_clicked(); 72 | void btn_edit_output_clicked(); 73 | void cb_download_missing_covers_clicked(int); 74 | 75 | private: 76 | void choose_file(QString& path, const char* title, const char* file_types); 77 | Ui::tuna_gui* ui; 78 | }; 79 | 80 | extern tuna_gui* tuna_dialog; 81 | -------------------------------------------------------------------------------- /src/gui/widgets/icecast.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "icecast.hpp" 20 | #include "../../util/config.hpp" 21 | #include "../../util/constants.hpp" 22 | #include "../../util/utility.hpp" 23 | #include "ui_icecast.h" 24 | 25 | icecast::icecast(QWidget* parent) 26 | : source_widget(parent) 27 | , ui(new Ui::icecast) 28 | { 29 | ui->setupUi(this); 30 | } 31 | 32 | icecast::~icecast() 33 | { 34 | delete ui; 35 | } 36 | 37 | void icecast::load_settings() 38 | { 39 | ui->txt_icecast_url->setText(utf8_to_qt(CGET_STR(CFG_ICECAST_URL))); 40 | } 41 | 42 | void icecast::save_settings() 43 | { 44 | CSET_STR(CFG_ICECAST_URL, qt_to_utf8(ui->txt_icecast_url->text())); 45 | } 46 | -------------------------------------------------------------------------------- /src/gui/widgets/icecast.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | 24 | namespace Ui { 25 | class icecast; 26 | } 27 | 28 | class icecast : public source_widget { 29 | Q_OBJECT 30 | 31 | public: 32 | explicit icecast(QWidget* parent = nullptr); 33 | ~icecast(); 34 | 35 | void load_settings() override; 36 | void save_settings() override; 37 | 38 | private: 39 | Ui::icecast* ui; 40 | }; 41 | -------------------------------------------------------------------------------- /src/gui/widgets/icecast.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | icecast 4 | 5 | 6 | 7 | 0 8 | 0 9 | 445 10 | 394 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | tuna.gui.tab.icecast.url 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | tuna.gui.tab.icecast.info 31 | 32 | 33 | true 34 | 35 | 36 | 37 | 38 | 39 | 40 | Qt::Vertical 41 | 42 | 43 | 44 | 20 45 | 40 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/gui/widgets/lastfm.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "lastfm.hpp" 20 | #include "../../util/config.hpp" 21 | #include "../../util/constants.hpp" 22 | #include "../../util/utility.hpp" 23 | #include "ui_lastfm.h" 24 | 25 | lastfm::lastfm(QWidget* parent) 26 | : source_widget(parent) 27 | , ui(new Ui::lastfm) 28 | { 29 | ui->setupUi(this); 30 | } 31 | 32 | lastfm::~lastfm() 33 | { 34 | delete ui; 35 | } 36 | 37 | void lastfm::load_settings() 38 | { 39 | ui->txt_username->setText(utf8_to_qt(CGET_STR(CFG_LASTFM_USERNAME))); 40 | ui->txt_apikey->setText(utf8_to_qt(CGET_STR(CFG_LASTFM_API_KEY))); 41 | } 42 | 43 | void lastfm::save_settings() 44 | { 45 | CSET_STR(CFG_LASTFM_USERNAME, qt_to_utf8(ui->txt_username->text())); 46 | CSET_STR(CFG_LASTFM_API_KEY, qt_to_utf8(ui->txt_apikey->text())); 47 | } 48 | -------------------------------------------------------------------------------- /src/gui/widgets/lastfm.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | 24 | namespace Ui { 25 | class lastfm; 26 | } 27 | 28 | class lastfm : public source_widget { 29 | Q_OBJECT 30 | 31 | public: 32 | explicit lastfm(QWidget* parent = nullptr); 33 | ~lastfm(); 34 | 35 | void load_settings() override; 36 | void save_settings() override; 37 | 38 | private: 39 | Ui::lastfm* ui; 40 | }; 41 | -------------------------------------------------------------------------------- /src/gui/widgets/lastfm.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | lastfm 4 | 5 | 6 | 7 | 0 8 | 0 9 | 445 10 | 394 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | tuna.gui.tab.lastfm.username 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | tuna.gui.tab.lastfm.apikey 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | <html><head/><body><p>Powered by <a href="https://www.last.fm/"><span style=" text-decoration: underline; color:#007af4;">last.fm</span></a></p></body></html> 41 | 42 | 43 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 44 | 45 | 46 | 47 | 48 | 49 | 50 | Qt::Vertical 51 | 52 | 53 | 54 | 20 55 | 40 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/gui/widgets/mpd.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "mpd.hpp" 20 | #include "../../util/config.hpp" 21 | #include "../../util/constants.hpp" 22 | #include "../../util/utility.hpp" 23 | #include "ui_mpd.h" 24 | #include 25 | 26 | mpd::mpd(QWidget* parent) 27 | : source_widget(parent) 28 | , ui(new Ui::mpd) 29 | { 30 | ui->setupUi(this); 31 | } 32 | 33 | mpd::~mpd() 34 | { 35 | delete ui; 36 | } 37 | 38 | void mpd::load_settings() 39 | { 40 | on_rb_remote_toggled(!CGET_BOOL(CFG_MPD_LOCAL)); 41 | ui->txt_ip->setText(utf8_to_qt(CGET_STR(CFG_MPD_IP))); 42 | ui->sb_port->setValue(CGET_INT(CFG_MPD_PORT)); 43 | ui->txt_base_folder->setText(utf8_to_qt(CGET_STR(CFG_MPD_BASE_FOLDER))); 44 | } 45 | 46 | void mpd::save_settings() 47 | { 48 | CSET_STR(CFG_MPD_IP, qt_to_utf8(ui->txt_ip->text())); 49 | CSET_UINT(CFG_MPD_PORT, ui->sb_port->value()); 50 | CSET_BOOL(CFG_MPD_LOCAL, ui->rb_local->isChecked()); 51 | auto path = ui->txt_base_folder->text(); 52 | if (!path.endsWith("/")) 53 | path.append("/"); 54 | CSET_STR(CFG_MPD_BASE_FOLDER, qt_to_utf8(path)); 55 | } 56 | 57 | void mpd::on_rb_remote_toggled(bool remote) 58 | { 59 | ui->rb_local->setChecked(!remote); 60 | ui->txt_base_folder->setEnabled(!remote); 61 | ui->rb_local->setChecked(!remote); 62 | ui->txt_ip->setEnabled(remote); 63 | ui->sb_port->setEnabled(remote); 64 | } 65 | 66 | void mpd::on_btn_browse_base_folder_clicked() 67 | { 68 | auto result = QFileDialog::getExistingDirectory(this, T_SELECT_MPD_FOLDER); 69 | if (!result.isEmpty()) 70 | ui->txt_base_folder->setText(result); 71 | } 72 | -------------------------------------------------------------------------------- /src/gui/widgets/mpd.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | 24 | namespace Ui { 25 | class mpd; 26 | } 27 | 28 | class mpd : public source_widget { 29 | Q_OBJECT 30 | 31 | public: 32 | explicit mpd(QWidget* parent = nullptr); 33 | ~mpd(); 34 | 35 | void load_settings() override; 36 | void save_settings() override; 37 | private slots: 38 | void on_rb_remote_toggled(bool checked); 39 | 40 | void on_btn_browse_base_folder_clicked(); 41 | 42 | private: 43 | Ui::mpd* ui; 44 | }; 45 | -------------------------------------------------------------------------------- /src/gui/widgets/mpris.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "mpris.hpp" 20 | #include "../../query/mpris_source.hpp" 21 | #include "../../util/config.hpp" 22 | #include "../../util/constants.hpp" 23 | #include "../../util/utility.hpp" 24 | #include "ui_mpris.h" 25 | 26 | mpris::mpris(QWidget* parent) 27 | : source_widget(parent) 28 | , ui(new Ui::mpris) 29 | { 30 | ui->setupUi(this); 31 | } 32 | 33 | mpris::~mpris() 34 | { 35 | delete ui; 36 | } 37 | 38 | void mpris::load_settings() 39 | { 40 | auto idx = ui->cb_player->findData(utf8_to_qt(CGET_STR(CFG_MPRIS_PLAYER))); 41 | if (idx >= 0) 42 | ui->cb_player->setCurrentIndex(idx); 43 | } 44 | 45 | void mpris::save_settings() 46 | { 47 | CSET_STR(CFG_MPRIS_PLAYER, qt_to_utf8(ui->cb_player->currentData().toString())); 48 | } 49 | 50 | void mpris::tick() 51 | { 52 | auto mpris_src = music_sources::get(S_SOURCE_MPRIS); 53 | if (mpris_src) { 54 | std::lock_guard lock(mpris_src->m_internal_mutex); 55 | 56 | if (m_current_players != mpris_src->m_players) { 57 | m_current_players = mpris_src->m_players; 58 | auto current_player = ui->cb_player->currentData().toString(); 59 | ui->cb_player->clear(); 60 | 61 | for (auto const& player : mpris_src->m_players.keys()) 62 | ui->cb_player->addItem(mpris_src->m_players[player], player); 63 | 64 | auto idx = ui->cb_player->findData(current_player); 65 | if (idx >= 0) 66 | ui->cb_player->setCurrentIndex(idx); 67 | } 68 | } 69 | } 70 | 71 | QComboBox* mpris::get_players() 72 | { 73 | return ui->cb_player; 74 | } 75 | -------------------------------------------------------------------------------- /src/gui/widgets/mpris.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | #include 24 | 25 | namespace Ui { 26 | class mpris; 27 | } 28 | 29 | class mpris : public source_widget { 30 | Q_OBJECT 31 | QMap m_current_players; 32 | 33 | public: 34 | explicit mpris(QWidget* parent = nullptr); 35 | ~mpris(); 36 | 37 | void load_settings() override; 38 | void save_settings() override; 39 | void tick() override; 40 | 41 | QComboBox* get_players(); 42 | 43 | private: 44 | Ui::mpris* ui; 45 | }; 46 | -------------------------------------------------------------------------------- /src/gui/widgets/mpris.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | mpris 4 | 5 | 6 | 7 | 0 8 | 0 9 | 365 10 | 290 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | tuna.gui.tab.mpris.playerlist 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Qt::Vertical 31 | 32 | 33 | 34 | 20 35 | 40 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/gui/widgets/spotify.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | 24 | namespace Ui { 25 | class spotify; 26 | } 27 | 28 | class spotify : public source_widget { 29 | struct result { 30 | bool success {}; 31 | QString value; 32 | }; 33 | 34 | Q_OBJECT 35 | void apply_login_state(bool state, const QString& log); 36 | 37 | std::promise*m_token_request_promise {}, *m_token_refresh_promise {}; 38 | std::future m_token_request_future {}, m_token_refresh_future {}; 39 | 40 | public: 41 | explicit spotify(QWidget* parent = nullptr); 42 | ~spotify(); 43 | 44 | void save_settings() override; 45 | void load_settings() override; 46 | void tick() override; 47 | signals: 48 | void login_state_changed(bool sate, QString& log); 49 | private slots: 50 | void on_btn_id_show_pressed(); 51 | void on_btn_id_show_released(); 52 | void on_btn_show_secret_pressed(); 53 | void on_btn_show_secret_released(); 54 | void on_btn_sp_show_auth_pressed(); 55 | void on_btn_sp_show_auth_released(); 56 | void on_btn_sp_show_token_pressed(); 57 | void on_btn_sp_show_token_released(); 58 | void on_btn_sp_show_refresh_token_pressed(); 59 | void on_btn_sp_show_refresh_token_released(); 60 | void on_btn_open_login_clicked(); 61 | void on_txt_auth_code_textChanged(const QString& arg1); 62 | void on_btn_request_token_clicked(); 63 | void on_btn_performrefresh_clicked(); 64 | 65 | private: 66 | Ui::spotify* ui; 67 | }; 68 | -------------------------------------------------------------------------------- /src/gui/widgets/vlc.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace Ui { 28 | class vlc; 29 | } 30 | 31 | class drag_list; 32 | 33 | class vlc : public source_widget { 34 | Q_OBJECT 35 | void load_vlc_sources(); 36 | QJsonObject m_source_map; 37 | drag_list* m_list { nullptr }; 38 | 39 | public: 40 | explicit vlc(QWidget* parent = nullptr); 41 | ~vlc(); 42 | 43 | void save_settings() override; 44 | void load_settings() override; 45 | 46 | void build_list(); 47 | bool has_mapping(const char* scene, const char* source); 48 | void rebuild_mapping(); 49 | QJsonArray get_mappings_for_scene(const char* scene); 50 | void rebuild_from_list(); 51 | 52 | QString get_scene_collection(); 53 | private slots: 54 | void on_btn_refresh_vlc_clicked(); 55 | void on_scene_changed(int index); 56 | void on_add_source(); 57 | void on_remove_source(); 58 | 59 | private: 60 | void set_map(const QString& scene, const QJsonArray&); 61 | bool valid_source_name(const QString& str); 62 | 63 | obs_scene_t* get_selected_scene(); 64 | void refresh_sources(); 65 | Ui::vlc* ui; 66 | }; 67 | 68 | class drag_list : public QListWidget { 69 | Q_OBJECT 70 | 71 | vlc* m_vlc; 72 | 73 | public: 74 | drag_list(vlc* parent = nullptr) 75 | : QListWidget(parent) 76 | , m_vlc(parent) 77 | { 78 | } 79 | 80 | protected: 81 | void dropEvent(QDropEvent* event) override 82 | { 83 | QListWidget::dropEvent(event); 84 | m_vlc->rebuild_from_list(); 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /src/gui/widgets/vlc.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | vlc 4 | 5 | 6 | 7 | 0 8 | 0 9 | 428 10 | 465 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | tuna.gui.vlc.mapping.text 21 | 22 | 23 | true 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | tuna.gui.vlc.scene 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | tuna.gui.vlc.source 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | tuna.gui.vlc.add 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 0 67 | 0 68 | 69 | 70 | 71 | tuna.gui.vlc.refresh 72 | 73 | 74 | 75 | 76 | 77 | 78 | tuna.gui.vlc.remove.source 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/gui/widgets/window_title.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "window_title.hpp" 20 | #include "../../util/config.hpp" 21 | #include "../../util/constants.hpp" 22 | #include "../../util/utility.hpp" 23 | #include "../../util/window/window_helper.hpp" 24 | #include "ui_window_title.h" 25 | 26 | window_title::window_title(QWidget* parent) 27 | : source_widget(parent) 28 | , ui(new Ui::window_title) 29 | { 30 | ui->setupUi(this); 31 | refresh_process_list(); 32 | } 33 | 34 | window_title::~window_title() 35 | { 36 | delete ui; 37 | } 38 | 39 | void window_title::refresh_process_list() 40 | { 41 | ui->cb_procress_list->clear(); 42 | ui->cb_procress_list->addItem(T_VLC_NONE); 43 | m_items.clear(); 44 | GetWindowAndExeList(m_items); 45 | int count = 0; 46 | for (const auto& p : m_items) { 47 | QString t = utf8_to_qt(("[" + p.first + "] " + p.second).c_str()); 48 | ui->cb_procress_list->addItem(t, count++); 49 | } 50 | } 51 | 52 | void window_title::load_settings() 53 | { 54 | ui->cb_regex->setChecked(CGET_BOOL(CFG_WINDOW_REGEX)); 55 | ui->txt_title->setText(utf8_to_qt(CGET_STR(CFG_WINDOW_TITLE))); 56 | ui->txt_search->setText(utf8_to_qt(CGET_STR(CFG_WINDOW_SEARCH))); 57 | ui->txt_replace->setText(utf8_to_qt(CGET_STR(CFG_WINDOW_REPLACE))); 58 | ui->txt_paused->setText(utf8_to_qt(CGET_STR(CFG_WINDOW_PAUSE))); 59 | ui->sb_begin->setValue(CGET_INT(CFG_WINDOW_CUT_BEGIN)); 60 | ui->sb_end->setValue(CGET_INT(CFG_WINDOW_CUT_END)); 61 | 62 | if (CGET_BOOL(CFG_WINDOW_USE_PROCRESS)) { 63 | ui->rb_process_name->setChecked(true); 64 | on_rb_process_name_clicked(true); 65 | } else { 66 | ui->rb_window_title->setChecked(true); 67 | on_rb_window_title_clicked(true); 68 | } 69 | } 70 | 71 | void window_title::save_settings() 72 | { 73 | CSET_STR(CFG_WINDOW_TITLE, qt_to_utf8(ui->txt_title->text())); 74 | CSET_STR(CFG_WINDOW_PAUSE, qt_to_utf8(ui->txt_paused->text())); 75 | CSET_STR(CFG_WINDOW_SEARCH, qt_to_utf8(ui->txt_search->text())); 76 | CSET_STR(CFG_WINDOW_REPLACE, qt_to_utf8(ui->txt_replace->text())); 77 | CSET_BOOL(CFG_WINDOW_REGEX, ui->cb_regex->isChecked()); 78 | CSET_UINT(CFG_WINDOW_CUT_BEGIN, ui->sb_begin->value()); 79 | CSET_UINT(CFG_WINDOW_CUT_END, ui->sb_end->value()); 80 | CSET_BOOL(CFG_WINDOW_USE_PROCRESS, ui->rb_process_name->isChecked()); 81 | auto p = ui->cb_procress_list->currentData().toUInt(); 82 | if (p < m_items.size()) 83 | CSET_STR(CFG_WINDOW_PROCESS_NAME, m_items[p].first.c_str()); 84 | } 85 | 86 | void window_title::on_rb_process_name_clicked(bool checked) 87 | { 88 | ui->cb_procress_list->setEnabled(checked); 89 | ui->btn_refresh->setEnabled(checked); 90 | ui->txt_title->setEnabled(!checked); 91 | ui->txt_paused->setEnabled(!checked); 92 | } 93 | 94 | void window_title::on_rb_window_title_clicked(bool checked) 95 | { 96 | ui->cb_procress_list->setEnabled(!checked); 97 | ui->btn_refresh->setEnabled(!checked); 98 | ui->txt_title->setEnabled(checked); 99 | ui->txt_paused->setEnabled(checked); 100 | } 101 | 102 | void window_title::on_btn_refresh_clicked() 103 | { 104 | refresh_process_list(); 105 | } 106 | -------------------------------------------------------------------------------- /src/gui/widgets/window_title.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace Ui { 28 | class window_title; 29 | } 30 | 31 | class window_title : public source_widget { 32 | Q_OBJECT 33 | std::vector> m_items; 34 | 35 | public: 36 | explicit window_title(QWidget* parent = nullptr); 37 | ~window_title(); 38 | 39 | void save_settings() override; 40 | void load_settings() override; 41 | 42 | void refresh_process_list(); 43 | private slots: 44 | 45 | void on_rb_process_name_clicked(bool checked); 46 | void on_rb_window_title_clicked(bool checked); 47 | void on_btn_refresh_clicked(); 48 | 49 | private: 50 | Ui::window_title* ui; 51 | QTimer* m_timer = nullptr; 52 | }; 53 | -------------------------------------------------------------------------------- /src/gui/widgets/wmc.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "wmc.hpp" 20 | #include "../../query/wmc_source.hpp" 21 | #include "../../util/config.hpp" 22 | #include "../../util/constants.hpp" 23 | #include "../../util/utility.hpp" 24 | #include "ui_wmc.h" 25 | 26 | wmc::wmc(QWidget* parent) 27 | : source_widget(parent) 28 | , ui(new Ui::wmc) 29 | { 30 | ui->setupUi(this); 31 | } 32 | 33 | wmc::~wmc() 34 | { 35 | delete ui; 36 | } 37 | 38 | void wmc::load_settings() 39 | { 40 | auto idx = ui->cb_player->findText(utf8_to_qt(CGET_STR(CFG_WMC_PLAYER))); 41 | if (idx >= 0) 42 | ui->cb_player->setCurrentIndex(idx); 43 | } 44 | 45 | void wmc::save_settings() 46 | { 47 | CSET_STR(CFG_WMC_PLAYER, qt_to_utf8(ui->cb_player->currentText())); 48 | } 49 | 50 | void wmc::tick() 51 | { 52 | auto wmc_src = music_sources::get(S_SOURCE_WMC); 53 | if (wmc_src) { 54 | std::lock_guard lock(wmc_src->m_internal_mutex); 55 | 56 | QStringList new_players; 57 | for (auto const& player : wmc_src->m_registered_players) 58 | new_players.append(utf8_to_qt(player.c_str())); 59 | 60 | if (m_current_players != new_players) { 61 | m_current_players = new_players; 62 | auto current_player = ui->cb_player->currentText(); 63 | ui->cb_player->clear(); 64 | 65 | for (auto const& player : new_players) 66 | ui->cb_player->addItem(player); 67 | 68 | auto idx = ui->cb_player->findText(current_player); 69 | if (idx >= 0) 70 | ui->cb_player->setCurrentIndex(idx); 71 | } 72 | } 73 | } 74 | 75 | QComboBox* wmc::get_players() 76 | { 77 | return ui->cb_player; 78 | } 79 | -------------------------------------------------------------------------------- /src/gui/widgets/wmc.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../tuna_gui.hpp" 22 | #include 23 | #include 24 | 25 | namespace Ui { 26 | class wmc; 27 | } 28 | 29 | class wmc : public source_widget { 30 | 31 | Q_OBJECT 32 | QStringList m_current_players; 33 | 34 | public: 35 | explicit wmc(QWidget* parent = nullptr); 36 | ~wmc(); 37 | 38 | void load_settings() override; 39 | void save_settings() override; 40 | void tick() override; 41 | 42 | QComboBox* get_players(); 43 | 44 | private: 45 | Ui::wmc* ui; 46 | }; 47 | -------------------------------------------------------------------------------- /src/gui/widgets/wmc.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | wmc 4 | 5 | 6 | 7 | 0 8 | 0 9 | 365 10 | 290 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | tuna.gui.tab.wmc.playerlist 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Qt::Vertical 31 | 32 | 33 | 34 | 20 35 | 40 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/plugin-macros.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | Plugin Name 3 | Copyright (C) 2022 univrsal 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 | #ifndef PLUGINNAME_H 20 | #define PLUGINNAME_H 21 | 22 | #define PLUGIN_NAME "@CMAKE_PROJECT_NAME@" 23 | #define PLUGIN_VERSION "@CMAKE_PROJECT_VERSION@" 24 | 25 | #endif // PLUGINNAME_H 26 | -------------------------------------------------------------------------------- /src/query/gpmdp_source.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "gpmdp_source.hpp" 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | gpmdp_source::gpmdp_source() 26 | : music_source(S_SOURCE_GPMDP, T_SOURCE_GPMDP) 27 | { 28 | supported_metadata({ meta::ALBUM, meta::TITLE, meta::ARTIST, meta::STATUS, meta::DURATION, meta::PROGRESS, meta::COVER }); 29 | #if _WIN32 30 | m_path = qgetenv("APPDATA") + "/Google Play Music Desktop Player/json_store/playback.json"; 31 | #elif __unix__ 32 | QDir home = QDir::homePath(); 33 | m_path = home.absolutePath() + "/.config/Google Play Music Desktop Player/json_store/playback.json"; 34 | #elif __APPLE__ 35 | QDir home = QDir::homePath(); 36 | m_path = home.absolutePath() + "/Library/Application Support/Google Play Music Desktop Player/json_store/playback.json"; 37 | #endif 38 | } 39 | 40 | void gpmdp_source::refresh() 41 | { 42 | QFile file(m_path); 43 | begin_refresh(); 44 | if (file.open(QIODevice::ReadOnly)) { 45 | m_current.clear(); 46 | auto doc = QJsonDocument::fromJson(file.readAll()); 47 | if (!doc.isObject()) 48 | return; 49 | auto obj = doc.object(); 50 | auto song = obj["song"].toObject(); 51 | auto time = obj["time"].toObject(); 52 | 53 | m_current.set(meta::STATUS, obj["playing"].toBool() ? state_playing : state_stopped); 54 | m_current.set(meta::TITLE, song["title"].toString()); 55 | m_current.set(meta::ARTIST, QStringList(song["artist"].toString())); 56 | m_current.set(meta::ALBUM, song["album"].toString()); 57 | m_current.set(meta::COVER, song["albumArt"].toString()); 58 | 59 | m_current.set(meta::DURATION, time["total"].toInt()); 60 | m_current.set(meta::PROGRESS, time["current"].toInt()); 61 | file.close(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/query/gpmdp_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../util/constants.hpp" 21 | #include "music_source.hpp" 22 | 23 | class gpmdp_source : public music_source { 24 | QString m_path; 25 | 26 | public: 27 | gpmdp_source(); 28 | 29 | void load() override {}; 30 | void refresh() override; 31 | bool execute_capability(capability) override { return false; }; 32 | bool enabled() const override { return true; }; 33 | }; 34 | -------------------------------------------------------------------------------- /src/query/icecast_source.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "icecast_source.hpp" 20 | #include "../gui/widgets/icecast.hpp" 21 | #include "../util/config.hpp" 22 | #include "../util/utility.hpp" 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | icecast_source::icecast_source() 29 | : music_source(S_SOURCE_ICECAST, T_SOURCE_ICECAST, new icecast) 30 | { 31 | supported_metadata({ meta::TITLE }); 32 | } 33 | 34 | void icecast_source::load() 35 | { 36 | music_source::load(); 37 | CDEF_STR(CFG_ICECAST_URL, ""); 38 | m_url = utf8_to_qt(CGET_STR(CFG_ICECAST_URL)) + "/status-json.xsl"; 39 | m_logged_response_too_big = false; 40 | } 41 | 42 | void icecast_source::refresh() 43 | { 44 | static char error_buffer[CURL_ERROR_SIZE]; 45 | 46 | if (m_logged_response_too_big || m_url.isEmpty()) 47 | return; 48 | 49 | begin_refresh(); 50 | auto* curl = curl_easy_init(); 51 | if (curl) { 52 | error_buffer[0] = '\0'; 53 | std::string response; 54 | curl_easy_setopt(curl, CURLOPT_URL, qt_to_utf8(m_url)); 55 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, util::write_callback); 56 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); 57 | curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer); 58 | auto result = curl_easy_perform(curl); 59 | curl_easy_cleanup(curl); 60 | 61 | if (result == CURLE_OK) { 62 | // Pretty arbitrary, but I have tested this with some stations 63 | // and they respond with ~1MB of data which we will not parse 64 | if (response.length() > 1024 * 512) { 65 | m_logged_response_too_big = true; 66 | berr("The IceCast server at %s responded with %zu bytes of data " 67 | "which is too long and therefore will not be processed", 68 | qt_to_utf8(m_url), response.length()); 69 | return; 70 | } 71 | QJsonParseError err; 72 | auto doc = QJsonDocument::fromJson(response.c_str(), &err); 73 | 74 | if (doc.isNull() || !doc.isObject()) { 75 | berr("Failed to parse json response from IceCast server: %s", qt_to_utf8(err.errorString())); 76 | } else { 77 | auto stats = doc.object()["icestats"].toObject(); 78 | if (!stats.isEmpty()) { 79 | auto source = stats["source"].toObject(); 80 | if (source["title"].isString()) { 81 | m_current.set(meta::TITLE, source["title"].toString()); 82 | m_current.set(meta::STATUS, state_playing); 83 | } 84 | } 85 | } 86 | } else { 87 | auto epoch = QDateTime::currentSecsSinceEpoch(); 88 | if (m_last_log == 0 || m_last_log - epoch > 10) { 89 | m_last_log = epoch; 90 | berr("Failed to retrieve information from IceCast server %s: cURL error '%s' (%i)", 91 | qt_to_utf8(m_url), curl_easy_strerror(result), result); 92 | if (strlen(error_buffer) > 0) 93 | berr("Additional curl error message: %s", error_buffer); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/query/icecast_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../util/constants.hpp" 21 | #include "music_source.hpp" 22 | #include 23 | 24 | class icecast_source : public music_source { 25 | QString m_url {}; 26 | qint64 m_last_log {}; 27 | bool m_logged_response_too_big { false }; 28 | 29 | public: 30 | icecast_source(); 31 | 32 | void load() override; 33 | void refresh() override; 34 | bool execute_capability(capability) override { return false; }; 35 | bool enabled() const override { return true; }; 36 | }; 37 | -------------------------------------------------------------------------------- /src/query/lastfm_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../util/constants.hpp" 21 | #include "music_source.hpp" 22 | 23 | class lastfm_source : public music_source { 24 | QString m_username, m_api_key; 25 | bool m_custom_api_key = false; 26 | uint64_t m_next_refresh = 0; 27 | void parse_song(const QJsonObject& s); 28 | 29 | public: 30 | lastfm_source(); 31 | 32 | void load() override; 33 | void refresh() override; 34 | bool execute_capability(capability c) override; 35 | bool enabled() const override; 36 | }; 37 | -------------------------------------------------------------------------------- /src/query/mpd_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../util/constants.hpp" 21 | #include "music_source.hpp" 22 | 23 | #include 24 | 25 | class mpd_source : public music_source { 26 | bool m_stopped = false; 27 | QString m_address; 28 | QString m_base_folder; 29 | QString m_song_file_path; 30 | uint16_t m_port; 31 | bool m_local; 32 | mpd_connection* m_connection {}; 33 | int m_connection_error_count {}; 34 | 35 | public: 36 | mpd_source(); 37 | ~mpd_source() { close_connection(); } 38 | 39 | void load() override; 40 | void refresh() override; 41 | bool execute_capability(capability c) override; 42 | bool enabled() const override; 43 | void handle_cover() override; 44 | void handle_lyrics() override; 45 | void reset_info() override; 46 | 47 | private: 48 | void ensure_connection(); 49 | 50 | void close_connection() 51 | { 52 | if (m_connection) 53 | mpd_connection_free(m_connection); 54 | m_connection = nullptr; 55 | } 56 | uint64_t m_last_error_log = 0; 57 | }; 58 | -------------------------------------------------------------------------------- /src/query/mpris_source.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | ** This file is part of the tuna project. 3 | ** Copyright 2023 univrsal . 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 3 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 16 | ** along with this program. If not, see . 17 | **/ 18 | 19 | #include "music_source.hpp" 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | class mpris_source : public music_source { 26 | friend class mpris; 27 | bool m_stopped = false; 28 | DBusConnection* m_dbus_connection {}; 29 | 30 | bool init_dbus_session(); 31 | bool dbus_add_matches(); 32 | bool dbus_register_names(); 33 | char* dbus_get_name_owner(const char*); 34 | 35 | DBusHandlerResult handle_dbus(DBusMessage*); 36 | DBusHandlerResult handle_mpris(DBusMessage*); 37 | 38 | void parse_array(DBusMessageIter* iter, QString const& player, int level = 0); 39 | void parse_metadata(DBusMessageIter* iter, QString const& player, int level = 0); 40 | 41 | inline void ensure_entry(QString const& player) 42 | { 43 | if (!m_info.contains(player)) 44 | m_info[player] = {}; 45 | } 46 | 47 | std::mutex m_internal_mutex; 48 | std::thread m_internal_thread; 49 | std::atomic m_thread_flag; 50 | 51 | struct SongInfo { 52 | song metadata {}; 53 | int64_t update_time {}; 54 | }; 55 | 56 | QMap m_players {}; 57 | QMap m_info {}; 58 | QString m_selected_player {}; 59 | bool init_dbus(); 60 | 61 | public: 62 | mpris_source(); 63 | ~mpris_source(); 64 | 65 | void load() override; 66 | void refresh() override; 67 | void internal_refresh(); 68 | bool execute_capability(capability) override { return false; } 69 | bool enabled() const override { return true; } 70 | 71 | DBusHandlerResult handle_message(DBusConnection*, DBusMessage*); 72 | }; 73 | -------------------------------------------------------------------------------- /src/query/spotify_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "music_source.hpp" 22 | #include 23 | #include 24 | 25 | class spotify_source : public music_source { 26 | bool m_logged_in = false; 27 | bool m_last_state = false; 28 | QString m_token = ""; 29 | QString m_creds = ""; 30 | QString m_auth_code = ""; 31 | QString m_refresh_token = ""; 32 | 33 | /* epoch time in seconds */ 34 | int64_t m_token_termination = 0; 35 | 36 | int64_t m_curl_timeout_ms = 1000; 37 | 38 | uint64_t m_timeout_length = 0, /* Rate limit timeout length */ 39 | m_timout_start = 0; /* Timeout start */ 40 | void parse_track_json(const QJsonValue& track); 41 | void build_credentials(); 42 | 43 | public: 44 | spotify_source(); 45 | 46 | bool enabled() const override; 47 | void load() override; 48 | void refresh() override; 49 | bool execute_capability(capability c) override; 50 | bool do_refresh_token(QString& log); 51 | bool new_token(QString& log); 52 | void set_auth_code(const QString& auth_code) { m_auth_code = auth_code; } 53 | bool is_logged_in() const { return m_logged_in; } 54 | int token_termination() const { return m_token_termination; } 55 | const QString& auth_code() const { return m_auth_code; } 56 | const QString& token() const { return m_token; } 57 | const QString& refresh_token() const { return m_refresh_token; } 58 | }; 59 | -------------------------------------------------------------------------------- /src/query/vlc_obs_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "music_source.hpp" 21 | #include 22 | #include 23 | #include 24 | 25 | class vlc_obs_source : public music_source { 26 | std::string m_target_source_name {}; 27 | std::string m_target_scene {}; 28 | OBSWeakSourceAutoRelease m_weak_src {}; 29 | bool reload(); 30 | 31 | void load_vlc_source(); 32 | 33 | std::string get_target_source_name(); 34 | std::string get_current_scene_name(); 35 | int m_index = 0; 36 | 37 | // Gets the currently tracked obs vlc source with increased ref count 38 | obs_source_t* get_source() 39 | { 40 | auto* src = obs_weak_source_get_source(m_weak_src); 41 | if (!src) 42 | m_weak_src = {}; 43 | return src; 44 | } 45 | 46 | public: 47 | vlc_obs_source(); 48 | ~vlc_obs_source(); 49 | 50 | void load() override; 51 | void refresh() override; 52 | bool execute_capability(capability c) override; 53 | bool enabled() const override; 54 | 55 | void next_vlc_source(); 56 | void prev_vlc_source(); 57 | void set_gui_values() override; 58 | }; 59 | -------------------------------------------------------------------------------- /src/query/web_source.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "web_source.hpp" 20 | #include "../util/constants.hpp" 21 | #include "../util/web_server.hpp" 22 | 23 | web_source::web_source() 24 | : music_source(S_SOURCE_WEB, T_SOURCE_WEB) 25 | { 26 | supported_metadata({ meta::ARTIST, meta::TITLE, meta::ALBUM, meta::PROGRESS, meta::DURATION, meta::COVER }); 27 | } 28 | 29 | void web_source::refresh() 30 | { 31 | begin_refresh(); 32 | m_current.clear(); 33 | std::lock_guard lock(web_thread::current_song_mutex); 34 | m_current = web_thread::current_song; 35 | } 36 | 37 | bool web_source::execute_capability(capability) 38 | { 39 | return false; 40 | } 41 | 42 | bool web_source::enabled() const 43 | { 44 | return true; 45 | } 46 | -------------------------------------------------------------------------------- /src/query/web_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "music_source.hpp" 21 | 22 | class web_source : public music_source { 23 | public: 24 | web_source(); 25 | 26 | void refresh() override; 27 | bool execute_capability(capability c) override; 28 | bool enabled() const override; 29 | }; 30 | -------------------------------------------------------------------------------- /src/query/window_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "music_source.hpp" 22 | #include 23 | #include 24 | #include 25 | 26 | class window_source : public music_source { 27 | QString m_title = ""; 28 | QString m_process_name = ""; 29 | QString m_search = "", m_replace = "", m_pause = ""; 30 | uint16_t m_cut_begin = 0, m_cut_end; 31 | bool m_regex = false, m_use_process_name; 32 | 33 | QString get_title(const std::vector& windows); 34 | QString get_title(const std::vector>& processes); 35 | 36 | public: 37 | window_source(); 38 | 39 | void load() override; 40 | void refresh() override; 41 | bool execute_capability(capability c) override; 42 | bool enabled() const override; 43 | }; 44 | -------------------------------------------------------------------------------- /src/query/wmc_source.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "music_source.hpp" 21 | #include 22 | #include 23 | #include 24 | 25 | #pragma comment(lib, "windowsapp") 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace winrt; 37 | using namespace Windows::Graphics::Imaging; 38 | using namespace Windows::Media::Control; 39 | using namespace Windows::Storage::Streams; 40 | using namespace Windows::Foundation::Collections; 41 | 42 | class wmc_source : public music_source { 43 | friend class wmc; 44 | GlobalSystemMediaTransportControlsSessionManager m_session_manager { nullptr }; 45 | std::string m_selected_player {}; 46 | std::vector m_registered_players {}; 47 | std::mutex m_internal_mutex; 48 | std::map m_info {}; 49 | // player name -> { cover is updated, cover image, cover raw data } 50 | std::map>>> m_covers {}; 51 | 52 | void save_cover(QImage const& image); 53 | 54 | public: 55 | wmc_source(); 56 | ~wmc_source() = default; 57 | 58 | void refresh() override; 59 | void handle_media_property_change(GlobalSystemMediaTransportControlsSession session, MediaPropertiesChangedEventArgs const& arg); 60 | void handle_media_playback_info_change(GlobalSystemMediaTransportControlsSession session, PlaybackInfoChangedEventArgs const& args); 61 | void handle_timeline_property_change(GlobalSystemMediaTransportControlsSession session, TimelinePropertiesChangedEventArgs const& args); 62 | void update_players(); 63 | bool execute_capability(capability c) override; 64 | bool enabled() const { return true; } 65 | void request_manager(); 66 | void handle_cover() override 67 | { /* NO-OP */ 68 | } 69 | 70 | void load() override; 71 | 72 | // bool enabled() const override; 73 | // void handle_cover() override; 74 | // void reset_info() override; 75 | }; 76 | -------------------------------------------------------------------------------- /src/source/progress.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../query/music_source.hpp" 21 | #include 22 | namespace obs_sources { 23 | 24 | class progress_source { 25 | uint32_t m_cx = 300, m_cy = 30; 26 | uint32_t m_fg {}, m_bg {}; 27 | obs_source_t* m_source = nullptr; 28 | float m_progress = 0.f; 29 | float m_bounce_progress = 0.f; 30 | 31 | /* Song progress grabbed from current music source */ 32 | int32_t m_synced_progress = 0; 33 | int32_t m_duration = 0; 34 | /* Song progress adjusted with frame time */ 35 | float m_adjusted_progress = 0.f; 36 | bool m_bounce_up = true; 37 | play_state m_state = state_unknown; 38 | bool m_use_bg = true; 39 | bool m_hide_paused = false; 40 | 41 | public: 42 | progress_source(obs_source_t* src, obs_data_t* settings); 43 | ~progress_source(); 44 | 45 | inline void update(obs_data_t* settings); 46 | inline void tick(float seconds); 47 | inline void render(gs_effect_t* effect); 48 | 49 | uint32_t get_width() const { return m_cx; } 50 | uint32_t get_height() const { return m_cy; } 51 | }; 52 | 53 | extern void register_progress(); 54 | } 55 | -------------------------------------------------------------------------------- /src/util/cover_tag_handler.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include 21 | 22 | namespace cover { 23 | /* Tries to get the song embbeded in the file */ 24 | extern bool find_embedded_cover(const QString& path); 25 | 26 | /* Tries to find the cover in the folder that the file is located in */ 27 | extern bool find_local_cover(const QString& path, QString& cover_out); 28 | 29 | /* Turns /home/usr/file.flac into /home/usr/ */ 30 | extern void get_file_folder(QString& path); 31 | } 32 | -------------------------------------------------------------------------------- /src/util/creds.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #ifndef SPOTIFY_CREDENTIALS 22 | # error No Spotify Credentials defined. Define SPOTIFY_CREDENTIALS with {client_id}:{secret} 23 | /* Define your spotify credentials like this; 24 | #define SPOTIFY_CREDENTIALS "{client_id}:{secret}" 25 | */ 26 | #endif 27 | -------------------------------------------------------------------------------- /src/util/format.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../query/song.hpp" 21 | #include "utility.hpp" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | class song; 28 | 29 | namespace format { 30 | 31 | void init(); 32 | bool execute(QString& out); 33 | 34 | class specifier { 35 | protected: 36 | QString m_id {}; 37 | std::function m_data_getter {}; 38 | std::vector m_required_caps {}; 39 | 40 | public: 41 | virtual ~specifier() = default; 42 | specifier() = default; 43 | 44 | specifier(const char* id, std::vector caps, std::function data_getter = nullptr) 45 | : m_id(id) 46 | , m_required_caps(caps) 47 | { 48 | if (data_getter) { 49 | m_data_getter = data_getter; 50 | } else { 51 | m_data_getter = [this](song const& s) -> QString { 52 | return s.get(m_required_caps[0]); 53 | }; 54 | } 55 | } 56 | 57 | specifier(const char* id, meta::type cap, std::function data_getter = nullptr) 58 | : m_id(id) 59 | , m_required_caps({ cap }) 60 | { 61 | if (data_getter) { 62 | m_data_getter = data_getter; 63 | } else { 64 | m_data_getter = [this](song const& s) -> QString { 65 | return s.get(m_required_caps[0]); 66 | }; 67 | } 68 | } 69 | 70 | const QString& get_id() const { return m_id; } 71 | QString get_name() const 72 | { 73 | auto Tmp = "tuna.format." + m_id; 74 | return utf8_to_qt(obs_module_text(qt_to_utf8(Tmp))); 75 | } 76 | 77 | QString get_data(song const& s) const { return m_data_getter(s); } 78 | 79 | virtual bool for_encoding() const { return true; } 80 | 81 | std::vector const& get_required_caps() const { return m_required_caps; } 82 | }; 83 | 84 | class static_specifier : public specifier { 85 | public: 86 | static_specifier(const char* id, std::function data_getter) 87 | : specifier(id, meta::NONE, data_getter) 88 | { 89 | } 90 | bool for_encoding() const override { return false; } 91 | }; 92 | 93 | extern const std::vector>& get_specifiers(); 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/util/lyrics_handler.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #include "lyrics_handler.hpp" 20 | #include "utility.hpp" 21 | #include 22 | #include 23 | namespace lyrics { 24 | 25 | bool download_missing_lyrics(const song&) 26 | { 27 | /* TODO */ 28 | return false; 29 | } 30 | 31 | bool get_embedded(TagLib::FileRef fr) 32 | { 33 | bool found = false; 34 | TagLib::PropertyMap tags = fr.file()->properties(); 35 | 36 | for (TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) { 37 | for (TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) { 38 | if (utf8_to_qt(i->first.toCString(true)).toLower().contains("lyrics")) { 39 | return util::write_lyrics(utf8_to_qt((*j).toCString(true))); 40 | } 41 | } 42 | } 43 | return found; 44 | } 45 | 46 | bool find_embedded_lyrics(const QString& path) 47 | { 48 | #ifdef _WIN32 49 | // Windoze can't into utf8 50 | const auto wstr = path.toStdWString(); 51 | const TagLib::FileRef fr(wstr.c_str(), false); 52 | #else 53 | const TagLib::FileRef fr(qt_to_utf8(path), false); 54 | #endif 55 | 56 | if (fr.isNull()) 57 | return false; 58 | 59 | return get_embedded(fr); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/util/lyrics_handler.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include 21 | 22 | class song; 23 | 24 | namespace lyrics { 25 | 26 | extern bool download_missing_lyrics(song const&); 27 | 28 | extern bool find_embedded_lyrics(QString const&); 29 | } 30 | -------------------------------------------------------------------------------- /src/util/tuna_thread.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include "../query/song.hpp" 22 | #include 23 | #include 24 | #include 25 | 26 | namespace tuna_thread { 27 | extern std::atomic thread_flag; 28 | extern std::mutex thread_mutex; 29 | extern std::mutex copy_mutex; 30 | extern std::thread thread_handle; 31 | extern song copy; 32 | 33 | bool start(); 34 | 35 | void stop(); 36 | 37 | void thread_method(); 38 | } // namespace thread 39 | -------------------------------------------------------------------------------- /src/util/utility.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define utf8_to_qt(_str) QString::fromUtf8(_str) 27 | #define qt_to_utf8(_str) _str.toUtf8().constData() 28 | 29 | #define write_log(log_level, format, ...) blog(log_level, "[tuna] " format, ##__VA_ARGS__) 30 | 31 | #if defined(_DEBUG) 32 | # define bdebug(format, ...) write_log(LOG_INFO, format, ##__VA_ARGS__) // apparently debug level isn't logged by default, so we just use info level for debug builds 33 | #else 34 | # define bdebug(format, ...) write_log(LOG_DEBUG, format, ##__VA_ARGS__) 35 | #endif 36 | #define binfo(format, ...) write_log(LOG_INFO, format, ##__VA_ARGS__) 37 | #define bwarn(format, ...) write_log(LOG_WARNING, format, ##__VA_ARGS__) 38 | #define berr(format, ...) write_log(LOG_ERROR, format, ##__VA_ARGS__) 39 | 40 | #define SECOND_TO_NS 1000000000 41 | 42 | #define UTIL_MAX(a, b) ((a) > (b) ? (a) : (b)) 43 | 44 | class song; 45 | 46 | class QJsonDocument; 47 | 48 | namespace util { 49 | 50 | extern bool have_vlc_source; 51 | 52 | extern bool curl_download(const char* url, const char* path); 53 | 54 | QJsonDocument curl_get_json(const char* url); 55 | 56 | extern bool download_cover(const QString& url); 57 | 58 | extern void reset_cover(); 59 | 60 | extern void reset_lyrics(); 61 | 62 | extern bool write_lyrics(QString const& lyrics); 63 | 64 | extern void download_lyrics(const song& song); 65 | 66 | extern void handle_outputs(const song& song); 67 | 68 | extern int64_t epoch(); 69 | 70 | extern bool window_pos_valid(QRect rect); 71 | 72 | extern size_t write_callback(char* ptr, size_t size, size_t nmemb, std::string* str); 73 | 74 | /* Redirected from util/threading.h because it clashes with mongoose */ 75 | extern void set_thread_name(const char* name); 76 | 77 | extern QString remove_extensions(QString const& str); 78 | 79 | extern QString file_from_path(QString const& file); 80 | 81 | extern bool open_config(const char* name, QJsonDocument&); 82 | extern bool save_config(const char* name, const QJsonDocument&); 83 | 84 | extern void create_config_folder(); 85 | } // namespace util 86 | -------------------------------------------------------------------------------- /src/util/web_server.hpp: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * This file is part of tuna 3 | * git.vrsal.xyz/alex/tuna 4 | * Copyright 2023 univrsal . 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 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 16 | * along with this program. If not, see . 17 | *************************************************************************/ 18 | 19 | #pragma once 20 | #include "../query/song.hpp" 21 | #include 22 | #include 23 | #include 24 | 25 | /* This thread runs a server that hosts music information in a JSON file */ 26 | namespace web_thread { 27 | extern std::thread thread_handle; 28 | extern std::mutex current_song_mutex; 29 | extern song current_song; 30 | extern std::atomic thread_flag; 31 | bool start(); 32 | void stop(); 33 | void thread_method(); 34 | } 35 | -------------------------------------------------------------------------------- /src/util/window/window_helper.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is part of obs-studio 3 | * which is licensed under the GPL 2.0 4 | * See COPYING or https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 5 | * https://github.com/obsproject/obs-studio/blob/master/UI/frontend-plugins/frontend-tools/auto-scene-switcher.hpp 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | void GetWindowList(std::vector& windows); 15 | void GetWindowAndExeList(std::vector>& list); 16 | -------------------------------------------------------------------------------- /src/util/window/window_helper_mac.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is part of obs-studio 3 | * which is licensed under the GPL 2.0 4 | * See COPYING or https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 5 | * https://raw.githubusercontent.com/obsproject/obs-studio/master/UI/frontend-plugins/frontend-tools/auto-scene-switcher-osx.mm 6 | * https://github.com/obsproject/obs-studio/blob/master/plugins/mac-capture/window-utils.m 7 | */ 8 | 9 | #include "window_helper.hpp" 10 | #import 11 | #import 12 | #include 13 | 14 | #define WINDOW_NAME ((NSString *)kCGWindowName) 15 | #define WINDOW_NUMBER ((NSString *)kCGWindowNumber) 16 | #define OWNER_NAME ((NSString *)kCGWindowOwnerName) 17 | #define OWNER_PID ((NSNumber *)kCGWindowOwnerPID) 18 | 19 | using namespace std; 20 | 21 | static NSComparator win_info_cmp = ^(NSDictionary *o1, NSDictionary *o2) { 22 | NSComparisonResult res = [o1[OWNER_NAME] compare:o2[OWNER_NAME]]; 23 | if (res != NSOrderedSame) 24 | return res; 25 | 26 | res = [o1[OWNER_PID] compare:o2[OWNER_PID]]; 27 | if (res != NSOrderedSame) 28 | return res; 29 | 30 | res = [o1[WINDOW_NAME] compare:o2[WINDOW_NAME]]; 31 | if (res != NSOrderedSame) 32 | return res; 33 | 34 | return [o1[WINDOW_NUMBER] compare:o2[WINDOW_NUMBER]]; 35 | }; 36 | 37 | NSArray *enumerate_windows(void) { 38 | NSArray *arr = CFBridgingRelease( 39 | CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID)); 40 | return [arr sortedArrayUsingComparator:win_info_cmp]; 41 | } 42 | 43 | static inline const char *make_name(NSString *owner, NSString *name) { 44 | if (!owner.length) 45 | return ""; 46 | 47 | NSString *str = [NSString stringWithFormat:@"[%@] %@", owner, name]; 48 | return str.UTF8String; 49 | } 50 | 51 | static inline pair create_pair(NSString *owner, NSString *name, 52 | bool &ok) { 53 | ok = false; 54 | if (!owner.length || !name.length) 55 | return pair("n/a", "n/a"); 56 | string o = string([owner UTF8String], 57 | [owner lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); 58 | string n = string([name UTF8String], 59 | [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); 60 | 61 | if (!o.empty() && !n.empty()) 62 | ok = true; 63 | return pair(o, n); 64 | } 65 | 66 | void GetWindowList(vector &windows) { 67 | @autoreleasepool { 68 | for (NSDictionary *d in enumerate_windows()) { 69 | windows.emplace_back(make_name(d[OWNER_NAME], d[WINDOW_NAME])); 70 | } 71 | } 72 | } 73 | 74 | void GetWindowAndExeList(vector> &list) { 75 | @autoreleasepool { 76 | for (NSDictionary *d in enumerate_windows()) { 77 | bool ok = false; 78 | auto pair = create_pair(d[OWNER_NAME], d[WINDOW_NAME], ok); 79 | if (ok) 80 | list.emplace_back(pair); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/util/window/window_helper_win.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is part of obs-studio 3 | * which is licensed under the GPL 2.0 4 | * See COPYING or https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 5 | * https://github.com/obsproject/obs-studio/blob/master/UI/frontend-plugins/frontend-tools/auto-scene-switcher-win.cpp 6 | */ 7 | 8 | #include "window_helper.hpp" 9 | #include 10 | #include 11 | 12 | static bool GetWindowTitle(HWND window, std::string& title) 13 | { 14 | std::size_t len = (size_t)GetWindowTextLengthW(window); 15 | std::wstring wtitle; 16 | 17 | wtitle.resize(len); 18 | if (!GetWindowTextW(window, &wtitle[0], (int)len + 1)) 19 | return false; 20 | 21 | len = os_wcs_to_utf8(wtitle.c_str(), 0, nullptr, 0); 22 | title.resize(len); 23 | os_wcs_to_utf8(wtitle.c_str(), 0, &title[0], len + 1); 24 | return true; 25 | } 26 | 27 | static bool GetWindowExe(HWND window, std::string& exe) 28 | { 29 | bool result = false; 30 | DWORD proc_id = 0; 31 | HANDLE h = NULL; 32 | GetWindowThreadProcessId(window, &proc_id); 33 | h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, proc_id); 34 | if (h) { 35 | DWORD l = 1024; 36 | size_t len = 1024; 37 | wchar_t buf[1024]; 38 | result = QueryFullProcessImageNameW(h, NULL, buf, &l); 39 | if (result) { 40 | len = os_wcs_to_utf8(buf, 0, nullptr, 0); 41 | exe.resize(len); 42 | os_wcs_to_utf8(buf, 0, &exe[0], len + 1); 43 | } 44 | CloseHandle(h); 45 | } 46 | return result; 47 | } 48 | 49 | static bool WindowValid(HWND window) 50 | { 51 | LONG_PTR styles, ex_styles; 52 | RECT rect; 53 | DWORD id; 54 | 55 | if (!IsWindowVisible(window)) 56 | return false; 57 | GetWindowThreadProcessId(window, &id); 58 | if (id == GetCurrentProcessId()) 59 | return false; 60 | 61 | GetClientRect(window, &rect); 62 | styles = GetWindowLongPtr(window, GWL_STYLE); 63 | ex_styles = GetWindowLongPtr(window, GWL_EXSTYLE); 64 | 65 | if (ex_styles & WS_EX_TOOLWINDOW) 66 | return false; 67 | if (styles & WS_CHILD) 68 | return false; 69 | 70 | return true; 71 | } 72 | 73 | void GetWindowList(std::vector& windows) 74 | { 75 | HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); 76 | 77 | while (window) { 78 | std::string title; 79 | if (WindowValid(window) && GetWindowTitle(window, title)) 80 | windows.emplace_back(title); 81 | window = GetNextWindow(window, GW_HWNDNEXT); 82 | } 83 | } 84 | 85 | void GetWindowAndExeList(std::vector>& list) 86 | { 87 | HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); 88 | 89 | while (window) { 90 | std::string title, exe; 91 | if (WindowValid(window) && GetWindowTitle(window, title) && GetWindowExe(window, exe)) { 92 | list.emplace_back(std::pair(exe, title)); 93 | } 94 | window = GetNextWindow(window, GW_HWNDNEXT); 95 | } 96 | } 97 | --------------------------------------------------------------------------------