Skip to content
Closed
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
121 changes: 121 additions & 0 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Desktop Installers

# Builds the desktop app installers for macOS, Windows and Linux.
# Push a tag like `desktop-v4.8.0` to also publish them as a GitHub release.
on:
workflow_dispatch:
push:
branches: [master, desktop-port]
tags: ["desktop-v*"]
paths:
- "desktopApp/**"
- "shared/**"
- "library/**"
- "gradle/**"
- ".github/workflows/desktop.yml"
pull_request:
paths:
- "desktopApp/**"
- "shared/**"
- ".github/workflows/desktop.yml"

permissions:
contents: read

jobs:
package:
strategy:
fail-fast: false
matrix:
include:
- name: macos-arm64
os: macos-15
tasks: packageDmg
- name: macos-x64
os: macos-15-intel
tasks: packageDmg
- name: windows-x64
os: windows-latest
tasks: packageMsi packageExe
- name: linux-x64
os: ubuntu-latest
tasks: packageDeb packageRpm

name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6

# jpackage builds the installer with the JDK that runs Gradle, and bundles its runtime
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21

- name: Install Linux packaging tools
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y rpm fakeroot

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
with:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache-read-only: ${{ github.ref != 'refs/heads/master' }}

- name: Build installers
shell: bash
run: |
chmod +x gradlew
tasks=""
for task in ${{ matrix.tasks }}; do tasks="$tasks :desktopApp:$task"; done
# Release builds take their version from the tag, which is what the in-app updater compares
version=""
if [[ "$GITHUB_REF" == refs/tags/desktop-v* ]]; then version="-PdesktopVersion=${GITHUB_REF#refs/tags/desktop-v}"; fi
./gradlew $tasks $version -PdesktopUpdateRepo=${{ github.repository }} --no-configuration-cache

- name: Upload installers
uses: actions/upload-artifact@v7
with:
name: CloudStream-${{ matrix.name }}
path: |
desktopApp/build/compose/binaries/main/dmg/*.dmg
desktopApp/build/compose/binaries/main/msi/*.msi
desktopApp/build/compose/binaries/main/exe/*.exe
desktopApp/build/compose/binaries/main/deb/*.deb
desktopApp/build/compose/binaries/main/rpm/*.rpm
if-no-files-found: error

release:
if: startsWith(github.ref, 'refs/tags/desktop-v')
needs: package
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v5
with:
path: installers

# Installers from different architectures share a file name, so prefix them with the build name
- name: Name installers by platform
run: |
mkdir release
for dir in installers/*/; do
name=$(basename "$dir")
find "$dir" -type f | while read -r file; do
cp "$file" "release/${name}-$(basename "$file")"
done
done
ls -la release

- name: Publish release
uses: softprops/action-gh-release@v2
with:
files: release/*
generate_release_notes: true
body: |
Unsigned builds. VLC must be installed for the built-in player.
Installed apps update themselves to new releases automatically.
macOS: if it says the app is damaged, run `xattr -cr /Applications/CloudStream.app`.
Windows: SmartScreen may warn, choose "More info" → "Run anyway".
134 changes: 120 additions & 14 deletions desktopApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ plugins {
kotlin {
jvm {}
sourceSets {
all {
languageSettings {
optIn("com.lagradost.cloudstream3.InternalAPI")
optIn("com.lagradost.cloudstream3.Prerelease")
}
}
jvmMain.dependencies {
implementation(libs.bundles.compose)
implementation(libs.kotlinx.coroutines.core)
Expand All @@ -20,40 +26,140 @@ kotlin {
}
implementation(project(":shared"))
implementation(project(":library"))

// The library only exposes these as implementation details, but we use them directly
implementation(libs.nicehttp)
implementation(libs.jackson.module.kotlin)
implementation(libs.kotlin.reflect)
implementation(libs.coil.network.ktor3)
implementation(libs.ktor.client.java)

// Video playback through the system LibVLC
implementation(libs.vlcj)
// Converts Android-only (.cs3 / dex) extensions to JVM bytecode
implementation(libs.dex2jar.translator)
}
}
}

// java.lang.System::load has been called by org.jetbrains.skiko.LibraryLoader in an unnamed module
/**
* Extensions are built for Android and reference Android classes in code paths desktop never runs
* (settings screens). The bytecode verifier would reject those classes up front, so it is turned off
* for classes outside the JDK, missing classes then only fail if that code actually runs.
*/
val extensionJvmArgs = arrayOf("-XX:+UnlockDiagnosticVMOptions", "-XX:-BytecodeVerificationRemote")

tasks.withType<JavaExec> {
jvmArgs("--enable-native-access=ALL-UNNAMED")
// java.lang.System::load has been called by org.jetbrains.skiko.LibraryLoader in an unnamed module
jvmArgs("--enable-native-access=ALL-UNNAMED", *extensionJvmArgs)
}

compose.resources {
packageOfResClass = "com.lagradost.cloudstream4.desktop.generated.resources"
generateResClass = always
}

// CI passes the version from the release tag (desktop-v1.2.3 -> 1.2.3) so updates compare correctly
val appVersion = (findProperty("desktopVersion") as String?)?.takeIf { it.isNotBlank() } ?: libs.versions.versionName.get()
// GitHub repository whose releases the app updates itself from
val updateRepository = (findProperty("desktopUpdateRepo") as String?) ?: "atomic-crafter/cloudstream"

val generateBuildInfo by tasks.registering {
val output = layout.buildDirectory.dir("generated/buildinfo/kotlin")
val version = appVersion
val repository = updateRepository
inputs.property("version", version)
inputs.property("repository", repository)
outputs.dir(output)
doLast {
val file = output.get().file("com/lagradost/cloudstream4/desktop/BuildInfo.kt").asFile
file.parentFile.mkdirs()
file.writeText(
"""
|package com.lagradost.cloudstream4.desktop
|
|object BuildInfo {
| const val VERSION = "$version"
| const val UPDATE_REPOSITORY = "$repository"
|}
|""".trimMargin()
)
}
}

kotlin.sourceSets.getByName("jvmMain").kotlin.srcDir(generateBuildInfo)

compose.desktop {
application {
mainClass = "com.lagradost.cloudstream4.MainKt"
jvmArgs += listOf(
"--enable-native-access=ALL-UNNAMED",
"-Xmx2G",
*extensionJvmArgs,
)

nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
targetFormats(
TargetFormat.Dmg,
TargetFormat.Msi,
TargetFormat.Exe,
TargetFormat.Deb,
TargetFormat.Rpm,
)
packageName = "CloudStream"
packageVersion = "1.0.0"
// Native installers require MAJOR.MINOR.PATCH with MAJOR > 0
packageVersion = appVersion
description = "Stream and browse media using CloudStream extensions"
vendor = "recloudstream"
copyright = "GPL-3.0"
licenseFile.set(rootProject.file("LICENSE"))

// From suggestRuntimeModules, plus what extensions commonly need at runtime
// (DNS, XML, every charset and locale for parsing sites, extra crypto)
modules(
"java.instrument",
"java.management",
"java.naming",
"java.net.http",
"java.prefs",
"java.scripting",
"java.security.jgss",
"java.sql",
"java.xml",
"jdk.charsets",
"jdk.crypto.ec",
"jdk.dynalink",
"jdk.httpserver",
"jdk.localedata",
"jdk.management",
"jdk.net",
"jdk.unsupported",
"jdk.zipfs",
)

val iconsRoot = project.file("desktop-icons")
val iconsRoot = project.file("src/desktop-icons")
macOS {
// iconFile.set(iconsRoot.resolve("icon-mac.icns"))
bundleID = "com.lagradost.cloudstream4"
iconFile.set(iconsRoot.resolve("icon-mac.icns"))
appCategory = "public.app-category.entertainment"
}
windows {
iconFile.set(iconsRoot.resolve("icon-windows.ico"))
// menuGroup = "Compose Examples"
// see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html
// upgradeUuid = ""
menuGroup = "CloudStream"
shortcut = true
dirChooser = true
perUserInstall = true
// Must stay the same between versions for upgrades to replace the old install
upgradeUuid = "8d2b7a3e-4d6c-4f1e-9a57-3c0f3e9b6a21"
}
linux {
iconFile.set(iconsRoot.resolve("icon-linux.png"))
packageName = "cloudstream"
debMaintainer = "cloudstream@users.noreply.github.com"
menuGroup = "AudioVideo"
appCategory = "Video"
shortcut = true
}
}

//buildTypes.release.proguard {
// configurationFiles.from(project.file("rules.pro"))
//}
}
}
}
Binary file modified desktopApp/src/desktop-icons/icon-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktopApp/src/desktop-icons/icon-mac.icns
Binary file not shown.
7 changes: 7 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/add.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<vector android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M24,38.35Q23.25,38.35 22.725,37.825Q22.2,37.3 22.2,36.5V25.8H11.45Q10.7,25.8 10.175,25.25Q9.65,24.7 9.65,23.95Q9.65,23.2 10.175,22.675Q10.7,22.15 11.45,22.15H22.2V11.4Q22.2,10.65 22.725,10.125Q23.25,9.6 24,9.6Q24.75,9.6 25.3,10.125Q25.85,10.65 25.85,11.4V22.15H36.55Q37.3,22.15 37.85,22.675Q38.4,23.2 38.4,23.95Q38.4,24.75 37.85,25.275Q37.3,25.8 36.55,25.8H25.85V36.5Q25.85,37.3 25.3,37.825Q24.75,38.35 24,38.35Z"/>
</vector>
10 changes: 10 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/arrow_back.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:autoMirrored="true">
<path
android:fillColor="#fff"
android:pathData="M313,520L509,716Q521,728 520.5,744Q520,760 508,772Q496,783 480,783.5Q464,784 452,772L188,508Q182,502 179.5,495Q177,488 177,480Q177,472 179.5,465Q182,458 188,452L452,188Q463,177 479.5,177Q496,177 508,188Q520,200 520,216.5Q520,233 508,245L313,440L760,440Q777,440 788.5,451.5Q800,463 800,480Q800,497 788.5,508.5Q777,520 760,520L313,520Z"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<vector android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M24,30.55Q23.65,30.55 23.325,30.4Q23,30.25 22.75,29.95L12.8,20.05Q12.3,19.55 12.325,18.725Q12.35,17.9 12.85,17.4Q13.45,16.8 14.175,16.875Q14.9,16.95 15.45,17.45L24,26.05L32.6,17.45Q33.1,16.95 33.9,16.9Q34.7,16.85 35.25,17.45Q35.8,17.95 35.75,18.75Q35.7,19.55 35.2,20.05L25.3,29.95Q25,30.25 24.675,30.4Q24.35,30.55 24,30.55Z"/>
</vector>
11 changes: 11 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/audio.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">

<path
android:fillColor="#FFFFFF"
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9v7c0,1.1 0.9,2 2,2h4v-8H5v-1c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v1h-4v8h4c1.1,0 2,-0.9 2,-2v-7C21,7.03 16.97,3 12,3z" />

</vector>
7 changes: 7 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/bookmark.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<vector android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M11.75,41.85Q10.85,42.25 10.05,41.725Q9.25,41.2 9.25,40.25V8.5Q9.25,7.05 10.35,5.95Q11.45,4.85 12.9,4.85H35.1Q36.55,4.85 37.65,5.95Q38.75,7.05 38.75,8.5V40.25Q38.75,41.2 37.95,41.725Q37.15,42.25 36.25,41.85L24,36.65Z"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M17,3L7,3c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,5h10v13z"/>
</vector>
9 changes: 9 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/clock.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="m612,668 l56,-56 -148,-148v-184h-80v216l172,172ZM480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,480ZM480,800q133,0 226.5,-93.5T800,480q0,-133 -93.5,-226.5T480,160q-133,0 -226.5,93.5T160,480q0,133 93.5,226.5T480,800Z"
android:fillColor="#e3e3e3"/>
</vector>
7 changes: 7 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/delete.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<vector android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M12.95,42.8Q11.5,42.8 10.4,41.7Q9.3,40.6 9.3,39.15V10.4H8.75Q8,10.4 7.475,9.85Q6.95,9.3 6.95,8.55Q6.95,7.8 7.475,7.275Q8,6.75 8.75,6.75H17.35Q17.35,6 17.875,5.525Q18.4,5.05 19.15,5.05H28.9Q29.65,5.05 30.175,5.525Q30.7,6 30.7,6.75H39.3Q40.05,6.75 40.6,7.275Q41.15,7.8 41.15,8.55Q41.15,9.35 40.6,9.875Q40.05,10.4 39.3,10.4H38.75V39.15Q38.75,40.6 37.65,41.7Q36.55,42.8 35.1,42.8ZM12.95,10.4V39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15H35.1Q35.1,39.15 35.1,39.15Q35.1,39.15 35.1,39.15V10.4ZM17.85,33Q17.85,33.75 18.375,34.275Q18.9,34.8 19.65,34.8Q20.4,34.8 20.95,34.275Q21.5,33.75 21.5,33V16.5Q21.5,15.75 20.95,15.2Q20.4,14.65 19.65,14.65Q18.9,14.65 18.375,15.2Q17.85,15.75 17.85,16.5ZM26.6,33Q26.6,33.75 27.125,34.275Q27.65,34.8 28.4,34.8Q29.15,34.8 29.7,34.275Q30.25,33.75 30.25,33V16.5Q30.25,15.75 29.7,15.2Q29.15,14.65 28.4,14.65Q27.65,14.65 27.125,15.2Q26.6,15.75 26.6,16.5ZM12.95,10.4V39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15V10.4Z"/>
</vector>
14 changes: 14 additions & 0 deletions desktopApp/src/jvmMain/composeResources/drawable/download.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="850"
android:viewportHeight="850">
<path
android:name="path_2"
android:pathData="M 233.86 666.14 L 616.54 666.14 M 425.2 539.08 L 425.2 159.6 M 410.14 554.17 L 581.98 454.96 M 268.41 454.96 L 440.25 554.17"
android:strokeColor="#FFFFFF"
android:strokeWidth="60"
android:strokeMiterLimit="10"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z"/>
</vector>
Loading
Loading