mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 00:10:32 +05:30
Merge pull request #5286 from Bnyro/master
feat: automatically play the next downloaded video in offline audio mode
This commit is contained in:
commit
e61be4e3ff
@ -2,12 +2,15 @@ package com.github.libretube.services
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||
import com.github.libretube.LibreTubeApp.Companion.PLAYER_CHANNEL_NAME
|
||||
@ -33,6 +36,23 @@ import kotlinx.coroutines.withContext
|
||||
class OfflinePlayerService : LifecycleService() {
|
||||
private var player: ExoPlayer? = null
|
||||
private var nowPlayingNotification: NowPlayingNotification? = null
|
||||
private lateinit var videoId: String
|
||||
private var downloadsWithItems: List<DownloadWithItems> = emptyList()
|
||||
|
||||
private val playerListener = object : Player.Listener {
|
||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||
super.onPlaybackStateChanged(playbackState)
|
||||
|
||||
// automatically go to the next video/audio when the current one ended
|
||||
if (playbackState == Player.STATE_ENDED) {
|
||||
val currentIndex = downloadsWithItems.indexOfFirst { it.download.videoId == videoId }
|
||||
downloadsWithItems.getOrNull(currentIndex + 1)?.let {
|
||||
this@OfflinePlayerService.videoId = it.download.videoId
|
||||
startAudioPlayer(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@ -47,52 +67,55 @@ class OfflinePlayerService : LifecycleService() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val videoId = intent?.getStringExtra(IntentData.videoId)
|
||||
videoId = intent?.getStringExtra(IntentData.videoId)!!
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val downloadWithItems = DatabaseHolder.Database.downloadDao().findById(videoId!!)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (startAudioPlayer(downloadWithItems)) {
|
||||
nowPlayingNotification = NowPlayingNotification(
|
||||
this@OfflinePlayerService,
|
||||
player!!,
|
||||
true
|
||||
)
|
||||
lifecycleScope.launch {
|
||||
downloadsWithItems = withContext(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.downloadDao().getAll()
|
||||
}
|
||||
val downloadWithItems = downloadsWithItems.first { it.download.videoId == videoId }
|
||||
|
||||
createPlayerAndNotification()
|
||||
|
||||
// destroy the service if there was no success playing the selected audio/video
|
||||
if (!startAudioPlayer(downloadWithItems)) onDestroy()
|
||||
}
|
||||
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
private fun createPlayerAndNotification() {
|
||||
val trackSelector = DefaultTrackSelector(this@OfflinePlayerService)
|
||||
trackSelector.updateParameters {
|
||||
setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, true)
|
||||
}
|
||||
|
||||
player = PlayerHelper.createPlayer(this@OfflinePlayerService, trackSelector, true)
|
||||
// prevent android from putting LibreTube to sleep when locked
|
||||
player!!.setWakeMode(C.WAKE_MODE_LOCAL)
|
||||
player!!.addListener(playerListener)
|
||||
|
||||
nowPlayingNotification = NowPlayingNotification(this, player!!, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to start an audio player with the given download items
|
||||
* @param downloadWithItems The database download to play from
|
||||
* @return whether starting the audio player succeeded
|
||||
*/
|
||||
private fun startAudioPlayer(downloadWithItems: DownloadWithItems): Boolean {
|
||||
val notificationData = PlayerNotificationData(
|
||||
title = downloadWithItems.download.title,
|
||||
uploaderName = downloadWithItems.download.uploader,
|
||||
thumbnailPath = downloadWithItems.download.thumbnailPath
|
||||
)
|
||||
nowPlayingNotification?.updatePlayerNotification(videoId, notificationData)
|
||||
} else {
|
||||
onDestroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to start an audio player with the given download items
|
||||
* @param downloadWithItem The database download to play from
|
||||
* @return whether starting the audio player succeeded
|
||||
*/
|
||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||
private fun startAudioPlayer(downloadWithItem: DownloadWithItems): Boolean {
|
||||
val trackSelector = DefaultTrackSelector(this)
|
||||
trackSelector.updateParameters {
|
||||
setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, true)
|
||||
}
|
||||
|
||||
player = PlayerHelper.createPlayer(this, trackSelector, true)
|
||||
// prevent android from putting LibreTube to sleep when locked
|
||||
player!!.setWakeMode(C.WAKE_MODE_LOCAL)
|
||||
|
||||
val audioItem = downloadWithItem.downloadItems.filter { it.path.exists() }
|
||||
val audioItem = downloadWithItems.downloadItems.filter { it.path.exists() }
|
||||
.firstOrNull { it.type == FileType.AUDIO }
|
||||
?: // in some rare cases, video files can contain audio
|
||||
downloadWithItem.downloadItems.firstOrNull { it.type == FileType.VIDEO }
|
||||
downloadWithItems.downloadItems.firstOrNull { it.type == FileType.VIDEO }
|
||||
?: return false
|
||||
|
||||
val mediaItem = MediaItem.Builder()
|
||||
@ -102,6 +125,7 @@ class OfflinePlayerService : LifecycleService() {
|
||||
player?.setMediaItem(mediaItem)
|
||||
player?.playWhenReady = PlayerHelper.playAutomatically
|
||||
player?.prepare()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,9 @@ class DownloadsAdapter(
|
||||
items.forEach {
|
||||
it.path.deleteIfExists()
|
||||
}
|
||||
runCatching {
|
||||
download.thumbnailPath?.deleteIfExists()
|
||||
}
|
||||
|
||||
runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.downloadDao().deleteDownload(download)
|
||||
|
Loading…
x
Reference in New Issue
Block a user