mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-23 19:48:20 +00:00
Compare commits
6 Commits
feat/vlc-4
...
0.51.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24d04c1003 | ||
|
|
7da52441ab | ||
|
|
70268e6120 | ||
|
|
96fbb9fe1f | ||
|
|
3b104b91fc | ||
|
|
e4134d6f9a |
2
app.json
2
app.json
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"android": {
|
||||
"jsEngine": "hermes",
|
||||
"versionCode": 90,
|
||||
"versionCode": 91,
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/images/icon-android-plain.png",
|
||||
"monochromeImage": "./assets/images/icon-android-themed.png",
|
||||
|
||||
@@ -5,42 +5,92 @@ import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat
|
||||
|
||||
class DownloadService : Service() {
|
||||
private val TAG = "DownloadService"
|
||||
private val NOTIFICATION_ID = 1001
|
||||
private val CHANNEL_ID = "download_channel"
|
||||
|
||||
|
||||
// Time threshold to detect if we're in boot context (10 minutes after boot)
|
||||
private val BOOT_THRESHOLD_MS = 10 * 60 * 1000L
|
||||
|
||||
private val binder = DownloadServiceBinder()
|
||||
private var activeDownloadCount = 0
|
||||
private var currentDownloadTitle = "Preparing download..."
|
||||
private var currentProgress = 0
|
||||
|
||||
private var isForegroundStarted = false
|
||||
|
||||
inner class DownloadServiceBinder : Binder() {
|
||||
fun getService(): DownloadService = this@DownloadService
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d(TAG, "DownloadService created")
|
||||
createNotificationChannel()
|
||||
}
|
||||
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder {
|
||||
Log.d(TAG, "DownloadService bound")
|
||||
return binder
|
||||
}
|
||||
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d(TAG, "DownloadService started")
|
||||
startForeground(NOTIFICATION_ID, createNotification())
|
||||
|
||||
// On Android 15+, dataSync foreground services cannot be started from BOOT_COMPLETED context
|
||||
// Check if we're likely in a boot context and skip foreground start if so
|
||||
if (Build.VERSION.SDK_INT >= 35 && isLikelyBootContext()) {
|
||||
Log.w(TAG, "Skipping foreground start - likely boot context on Android 15+")
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
startForegroundSafely()
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're likely in a boot context by checking system uptime.
|
||||
* If the system has been up for less than the threshold, we might be in boot context.
|
||||
*/
|
||||
private fun isLikelyBootContext(): Boolean {
|
||||
val uptimeMs = SystemClock.elapsedRealtime()
|
||||
return uptimeMs < BOOT_THRESHOLD_MS
|
||||
}
|
||||
|
||||
/**
|
||||
* Start foreground service safely with proper service type for Android 14+
|
||||
*/
|
||||
private fun startForegroundSafely() {
|
||||
if (isForegroundStarted) return
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
ServiceCompat.startForeground(
|
||||
this,
|
||||
NOTIFICATION_ID,
|
||||
createNotification(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, createNotification())
|
||||
}
|
||||
isForegroundStarted = true
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to start foreground service", e)
|
||||
// If we can't start foreground, stop the service
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(TAG, "DownloadService destroyed")
|
||||
@@ -86,7 +136,7 @@ class DownloadService : Service() {
|
||||
activeDownloadCount++
|
||||
Log.d(TAG, "Download started, active count: $activeDownloadCount")
|
||||
if (activeDownloadCount == 1) {
|
||||
startForeground(NOTIFICATION_ID, createNotification())
|
||||
startForegroundSafely()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +144,10 @@ class DownloadService : Service() {
|
||||
activeDownloadCount = maxOf(0, activeDownloadCount - 1)
|
||||
Log.d(TAG, "Download stopped, active count: $activeDownloadCount")
|
||||
if (activeDownloadCount == 0) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
if (isForegroundStarted) {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
isForegroundStarted = false
|
||||
}
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.videolan.android:libvlc-all:4.0.0-eap23'
|
||||
implementation 'io.github.mengzhidaren:vlc-android-sdk:3.6.3'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
}
|
||||
|
||||
|
||||
@@ -63,16 +63,16 @@ class VlcPlayerModule : Module() {
|
||||
view.seekTo(time)
|
||||
}
|
||||
|
||||
AsyncFunction("setAudioTrack") { view: VlcPlayerView, trackId: String ->
|
||||
view.setAudioTrack(trackId)
|
||||
AsyncFunction("setAudioTrack") { view: VlcPlayerView, trackIndex: Int ->
|
||||
view.setAudioTrack(trackIndex)
|
||||
}
|
||||
|
||||
AsyncFunction("getAudioTracks") { view: VlcPlayerView ->
|
||||
view.getAudioTracks()
|
||||
}
|
||||
|
||||
AsyncFunction("setSubtitleTrack") { view: VlcPlayerView, trackId: String ->
|
||||
view.setSubtitleTrack(trackId)
|
||||
AsyncFunction("setSubtitleTrack") { view: VlcPlayerView, trackIndex: Int ->
|
||||
view.setSubtitleTrack(trackIndex)
|
||||
}
|
||||
|
||||
AsyncFunction("getSubtitleTracks") { view: VlcPlayerView ->
|
||||
|
||||
@@ -234,7 +234,6 @@ class VlcPlayerView(context: Context, appContext: AppContext) : ExpoView(context
|
||||
libVLC = LibVLC(context, initOptions)
|
||||
mediaPlayer = MediaPlayer(libVLC)
|
||||
mediaPlayer?.attachViews(videoLayout, null, false, false)
|
||||
mediaPlayer?.setVideoScale(MediaPlayer.ScaleType.SURFACE_BEST_FIT)
|
||||
mediaPlayer?.setEventListener(this)
|
||||
|
||||
log.debug("Loading network file: $uri")
|
||||
@@ -295,26 +294,38 @@ class VlcPlayerView(context: Context, appContext: AppContext) : ExpoView(context
|
||||
}
|
||||
}
|
||||
|
||||
fun setAudioTrack(trackId: String) {
|
||||
// TODO: VLC 4.0 API - need to find correct method
|
||||
log.debug("setAudioTrack called with $trackId - not yet implemented for VLC 4.0")
|
||||
fun setAudioTrack(trackIndex: Int) {
|
||||
mediaPlayer?.setAudioTrack(trackIndex)
|
||||
}
|
||||
|
||||
fun getAudioTracks(): List<Map<String, Any>>? {
|
||||
// TODO: VLC 4.0 API - need to find correct method
|
||||
log.debug("getAudioTracks - not yet implemented for VLC 4.0")
|
||||
return emptyList()
|
||||
log.debug("getAudioTracks ${mediaPlayer?.audioTracks}")
|
||||
val trackDescriptions = mediaPlayer?.audioTracks ?: return null
|
||||
|
||||
return trackDescriptions.map { trackDescription ->
|
||||
mapOf("name" to trackDescription.name, "index" to trackDescription.id)
|
||||
}
|
||||
}
|
||||
|
||||
fun setSubtitleTrack(trackId: String) {
|
||||
// TODO: VLC 4.0 API - need to find correct method
|
||||
log.debug("setSubtitleTrack called with $trackId - not yet implemented for VLC 4.0")
|
||||
fun setSubtitleTrack(trackIndex: Int) {
|
||||
mediaPlayer?.setSpuTrack(trackIndex)
|
||||
}
|
||||
|
||||
// fun getSubtitleTracks(): List<Map<String, Any>>? {
|
||||
// return mediaPlayer?.getSpuTracks()?.map { trackDescription ->
|
||||
// mapOf("name" to trackDescription.name, "index" to trackDescription.id)
|
||||
// }
|
||||
// }
|
||||
|
||||
fun getSubtitleTracks(): List<Map<String, Any>>? {
|
||||
// TODO: VLC 4.0 API - need to find correct method
|
||||
log.debug("getSubtitleTracks - not yet implemented for VLC 4.0")
|
||||
return emptyList()
|
||||
val subtitleTracks = mediaPlayer?.spuTracks?.map { trackDescription ->
|
||||
mapOf("name" to trackDescription.name, "index" to trackDescription.id)
|
||||
}
|
||||
|
||||
// Debug statement to print the result
|
||||
log.debug("Subtitle Tracks: $subtitleTracks")
|
||||
|
||||
return subtitleTracks
|
||||
}
|
||||
|
||||
fun setSubtitleURL(subtitleURL: String, name: String) {
|
||||
|
||||
Reference in New Issue
Block a user