Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

# Build on every push and pull request so cross-platform breakage (e.g. Linux-only
# /tmp paths, resource-prefix changes) is caught before a release, not after.
on:
push:
branches: [main]
pull_request:

jobs:
build:
name: Build on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: '6.5.3'
# qtmultimedia for playback; qtsvg provides Qt6::Svg (find_package fails without it).
modules: 'qtmultimedia qtsvg'

- name: Install dependencies (Linux)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libsqlite3-dev libasound2-dev libavahi-client-dev

- name: Configure
run: cmake -B build -S . -DCMAKE_BUILD_TYPE=Release

- name: Build
run: cmake --build build --config Release --parallel 4
13 changes: 10 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ jobs:
uses: jurplel/install-qt-action@v4
with:
version: '6.5.3'
modules: 'qtmultimedia'
# qtmultimedia for playback; qtsvg provides Qt6::Svg (find_package fails without it).
modules: 'qtmultimedia qtsvg'

- name: Install dependencies (Linux)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libsqlite3-dev libasound2-dev
sudo apt-get install -y libsqlite3-dev libasound2-dev libavahi-client-dev

- name: Configure and build
run: |
Expand All @@ -48,6 +49,9 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
tar -czf ${{ matrix.asset_name }} -C build tidal-wave TidalWave
# Also build a .deb with the correct runtime dependencies declared.
(cd build && cpack -G DEB)
mv build/tidal-wave-*-Linux.deb ./tidal-wave-linux-x86_64.deb

- name: Package binary (macOS)
if: matrix.os == 'macos-latest'
Expand All @@ -67,6 +71,9 @@ jobs:
- name: Upload binaries to Release
uses: softprops/action-gh-release@v1
with:
files: ${{ matrix.asset_name }}
# The .deb glob only matches on Linux; unmatched globs are ignored.
files: |
${{ matrix.asset_name }}
*.deb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
/build/
# Any build directory (build/, build_custom/, build_dl/, …)
/build*/

# IDE files
.idea/
Expand Down
80 changes: 77 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.20)
project(tidal-wave VERSION 0.2.1 LANGUAGES CXX)
project(tidal-wave VERSION 0.3.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand All @@ -23,13 +23,20 @@ find_package(Qt6 6.4 REQUIRED COMPONENTS

qt_standard_project_setup()

# Chromecast support (Linux only) needs Avahi for mDNS discovery.
if(UNIX AND NOT APPLE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(AVAHI REQUIRED IMPORTED_TARGET avahi-client)
endif()

set(SOURCES
src/main.cpp
src/api/TidalApi.cpp
src/api/Auth.cpp
src/api/TidalClient.cpp
src/api/TidalBridge.cpp
src/player/Player.cpp
src/player/Downloader.cpp
src/mpris/MprisPlayer.cpp
src/ui/Application.cpp
src/ui/ImageProvider.cpp
Expand All @@ -42,11 +49,32 @@ set(HEADERS
src/api/TidalClient.h
src/api/TidalBridge.h
src/player/Player.h
src/player/Downloader.h
src/mpris/MprisPlayer.h
src/ui/Application.h
src/ui/ImageProvider.h
)

# Chromecast backend (Linux only — depends on Avahi).
if(UNIX AND NOT APPLE)
list(APPEND SOURCES
src/cast/CastMessage.cpp
src/cast/CastDiscovery.cpp
src/cast/CastSession.cpp
src/cast/CastMediaServer.cpp
src/cast/CastMediaPrep.cpp
src/cast/CastManager.cpp
)
list(APPEND HEADERS
src/cast/CastMessage.h
src/cast/CastDiscovery.h
src/cast/CastSession.h
src/cast/CastMediaServer.h
src/cast/CastMediaPrep.h
src/cast/CastManager.h
)
endif()

qt_add_executable(tidal-wave
${SOURCES}
${HEADERS}
Expand All @@ -62,7 +90,7 @@ qt_add_qml_module(tidal-wave
VERSION 1.0
QML_FILES
qml/Theme.qml
qml/main.qml
qml/Main.qml
qml/components/PlayerBar.qml
qml/components/SideBar.qml
qml/components/TrackRow.qml
Expand Down Expand Up @@ -93,7 +121,7 @@ qt_add_qml_module(tidal-wave
assets/icon.png
)

target_include_directories(tidal-wave PRIVATE src src/api src/player src/mpris src/ui)
target_include_directories(tidal-wave PRIVATE src src/api src/player src/mpris src/ui src/cast)

target_link_libraries(tidal-wave PRIVATE
Qt6::Core
Expand All @@ -110,6 +138,11 @@ target_link_libraries(tidal-wave PRIVATE
Qt6::Concurrent
)

# Avahi for Chromecast discovery (Linux only).
if(UNIX AND NOT APPLE)
target_link_libraries(tidal-wave PRIVATE PkgConfig::AVAHI)
endif()

set_target_properties(tidal-wave PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER com.tidalwave.client
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
Expand All @@ -118,8 +151,49 @@ set_target_properties(tidal-wave PROPERTIES
WIN32_EXECUTABLE TRUE
)

include(GNUInstallDirs)

install(TARGETS tidal-wave
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

# Desktop integration (Linux). Installs a real .desktop entry and hicolor icons
# so the app appears in menus and the tray/window icon resolve by name — this
# replaces the runtime icon self-install workaround (loadAppIcon()) with a
# proper package-installed icon.
if(UNIX AND NOT APPLE)
install(FILES packaging/tidal-wave.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES assets/icon.png
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps
RENAME tidal-wave.png)
install(FILES assets/icon.svg
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps
RENAME tidal-wave.svg)
endif()

# ── Packaging (.deb via CPack) ───────────────────────────────────────────────
# Build with: cpack -G DEB --config build/CPackConfig.cmake (or `cpack -G DEB`
# from the build dir). Depends list covers the Qt6 runtime, the easy-to-miss QML
# runtime modules, the SQLite driver, ALSA, and ffmpeg (used for downloads).
if(UNIX AND NOT APPLE)
set(CPACK_PACKAGE_NAME "tidal-wave")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A native desktop client for Tidal")
set(CPACK_PACKAGE_CONTACT "Linus Linhof <[email protected]>")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/user/tidal-wave")

set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_CONTACT}")
set(CPACK_DEBIAN_PACKAGE_SECTION "sound")
set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"libqt6core6, libqt6gui6, libqt6widgets6, libqt6quick6, libqt6qml6, libqt6network6, libqt6dbus6, libqt6multimedia6, libqt6sql6, libqt6svg6, libqt6sql6-sqlite, qml6-module-qtquick, qml6-module-qtquick-controls, qml6-module-qtquick-layouts, qml6-module-qtquick-window, qml6-module-qtquick-templates, qml6-module-qtqml-workerscript, libasound2, libavahi-client3")
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "ffmpeg")
# Let dpkg-shlibdeps refine the library deps automatically where possible.
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)

include(CPack)
endif()
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Tidal Wave is a native, lightweight desktop client for the Tidal music streaming
* **Media Keys and MPRIS2**: Full Linux media player integration via D-Bus MPRIS2, supporting lockscreen controls, system volume widgets, and media keys.
* **Secure Authentication**: Implements Tidal OAuth device login flow with secure local session caching using SQLite.
* **Custom Audio Player**: Native streaming audio engine utilizing QMediaPlayer and QAudioOutput with selectable stream qualities.
* **Chromecast Output** (Linux): Cast audio to Chromecast / Google Home devices — native mDNS discovery (Avahi) and CASTV2 control, with a built-in HTTP server that streams the current track (FLAC up to 96 kHz, or AAC) directly to the device. Downloads/downsamples on the fly so every quality tier casts.
* **Persistent Navigation State**: Separate loaders retain individual page states when jumping between Home, Search, and My Collection views.
* **Queue Panel**: Full queue management including track ordering, shuffle, and cycle repeat modes.
* **System Tray Integration**: Background playback support with system tray control options to show, hide, and quit the application.
Expand Down Expand Up @@ -47,12 +48,26 @@ I used Claude Code over the course of 3 days to generate most of the code for th
| Alt + Left | Go back |
| Ctrl + , | Open Settings |

## Installation (Linux)

The easiest way to install on Debian/Ubuntu-based distros is the prebuilt **`.deb`** from the
[latest GitHub Release](https://github.com/immineal/tidal-wave/releases/latest). It declares all
runtime dependencies, so `apt` pulls in the Qt6 runtime, QML modules, and everything else for you —
no more hunting down packages by hand:

```bash
sudo apt install ./tidal-wave-linux-x86_64.deb
```

For downloads and Chromecast transcoding, also install `ffmpeg` (a `Recommends`, so most setups get
it automatically). To build from source instead, see below.

## Prerequisites

* C++20-compliant compiler (GCC 11+, Clang 13+, MSVC 2022+)
* CMake 3.20+
* Qt 6 SDK (6.4+), specifically the following modules: Core, Gui, Widgets, Quick, Qml, QmlModels, Network, DBus, Multimedia, Sql, Svg, Concurrent
* On Linux: libsqlite3-dev and libasound2-dev (or similar ALSA development libraries)
* On Linux: libsqlite3-dev, libasound2-dev (or similar ALSA development libraries), and libavahi-client-dev (for Chromecast device discovery)

## Building from Source

Expand All @@ -73,6 +88,22 @@ On Windows:
build\Release\tidal-wave.exe
```

## Building a Debian package (.deb)

The build ships a CPack configuration that produces a `.deb` with the correct runtime
dependencies declared (so users no longer have to hunt down packages by hand), plus a
desktop entry and hicolor icons:

```bash
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release --parallel 4
cd build && cpack -G DEB
sudo apt install ./tidal-wave-*-Linux.deb # pulls in Qt6 runtime, QML modules, ffmpeg, etc.
```

`ffmpeg` is a `Recommends` (only needed for the download feature); everything else is a
hard `Depends`, including the easy-to-miss `qml6-module-*` runtime modules.

## License

This project is licensed under the GNU GPL v3 License. See the LICENSE file for details.
13 changes: 13 additions & 0 deletions packaging/tidal-wave.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Tidal Wave
GenericName=Music Player
Comment=A native desktop client for Tidal
Exec=tidal-wave
Icon=tidal-wave
Terminal=false
Categories=AudioVideo;Audio;Player;
Keywords=music;streaming;tidal;audio;hifi;
StartupNotify=true
StartupWMClass=tidal-wave
File renamed without changes.
Loading
Loading