refactor: merge OfflinePlayerService with VideoOfflinePlayerService

This commit is contained in:
Bnyro 2025-03-31 18:58:38 +02:00
parent caaa095faf
commit 09660cd5d6
No known key found for this signature in database
7 changed files with 84 additions and 116 deletions

View File

@ -17,7 +17,6 @@ import com.github.libretube.parcelable.PlayerData
import com.github.libretube.services.AbstractPlayerService import com.github.libretube.services.AbstractPlayerService
import com.github.libretube.services.OfflinePlayerService import com.github.libretube.services.OfflinePlayerService
import com.github.libretube.services.OnlinePlayerService import com.github.libretube.services.OnlinePlayerService
import com.github.libretube.services.VideoOfflinePlayerService
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.activities.NoInternetActivity import com.github.libretube.ui.activities.NoInternetActivity
import com.github.libretube.ui.fragments.DownloadTab import com.github.libretube.ui.fragments.DownloadTab
@ -65,8 +64,7 @@ object BackgroundHelper {
fun stopBackgroundPlay(context: Context) { fun stopBackgroundPlay(context: Context) {
arrayOf( arrayOf(
OnlinePlayerService::class.java, OnlinePlayerService::class.java,
OfflinePlayerService::class.java, OfflinePlayerService::class.java
VideoOfflinePlayerService::class.java
).forEach { ).forEach {
val intent = Intent(context, it) val intent = Intent(context, it)
context.stopService(intent) context.stopService(intent)
@ -104,7 +102,8 @@ object BackgroundHelper {
IntentData.videoId to videoId, IntentData.videoId to videoId,
IntentData.shuffle to shuffle, IntentData.shuffle to shuffle,
IntentData.downloadTab to downloadTab, IntentData.downloadTab to downloadTab,
IntentData.noInternet to noInternet IntentData.noInternet to noInternet,
IntentData.audioOnly to true
) )
startMediaService(context, OfflinePlayerService::class.java, arguments) startMediaService(context, OfflinePlayerService::class.java, arguments)

View File

@ -15,7 +15,6 @@ import androidx.media3.common.ForwardingPlayer
import androidx.media3.common.MediaMetadata import androidx.media3.common.MediaMetadata
import androidx.media3.common.PlaybackException import androidx.media3.common.PlaybackException
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.util.Log
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
@ -119,13 +118,6 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
onServiceCreated(args) onServiceCreated(args)
notificationProvider?.intentActivity = getIntentActivity() notificationProvider?.intentActivity = getIntentActivity()
if (isAudioOnlyPlayer) {
trackSelector?.updateParameters {
setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, true)
}
}
Log.e("custom start", "custom start")
if (::videoId.isInitialized) startPlayback() if (::videoId.isInitialized) startPlayback()
} }
} }

View File

@ -3,9 +3,16 @@ package com.github.libretube.services
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.media3.common.C
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.MediaItem.SubtitleConfiguration
import androidx.media3.common.MimeTypes
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.FileDataSource
import androidx.media3.exoplayer.source.MergingMediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.exoplayer.source.SingleSampleMediaSource
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.DatabaseHolder.Database import com.github.libretube.db.DatabaseHolder.Database
@ -15,6 +22,7 @@ import com.github.libretube.enums.FileType
import com.github.libretube.extensions.serializable import com.github.libretube.extensions.serializable
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toAndroidUri import com.github.libretube.extensions.toAndroidUri
import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.activities.NoInternetActivity import com.github.libretube.ui.activities.NoInternetActivity
@ -65,6 +73,7 @@ open class OfflinePlayerService : AbstractPlayerService() {
downloadTab = args.serializable(IntentData.downloadTab)!! downloadTab = args.serializable(IntentData.downloadTab)!!
shuffle = args.getBoolean(IntentData.shuffle, false) shuffle = args.getBoolean(IntentData.shuffle, false)
noInternetService = args.getBoolean(IntentData.noInternet, false) noInternetService = args.getBoolean(IntentData.noInternet, false)
isAudioOnlyPlayer = args.getBoolean(IntentData.audioOnly, false)
val videoId = if (shuffle) { val videoId = if (shuffle) {
runBlocking(Dispatchers.IO) { runBlocking(Dispatchers.IO) {
@ -75,10 +84,12 @@ open class OfflinePlayerService : AbstractPlayerService() {
} ?: return } ?: return
setVideoId(videoId) setVideoId(videoId)
PlayingQueue.clear()
exoPlayer?.addListener(playerListener) exoPlayer?.addListener(playerListener)
trackSelector?.updateParameters {
setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, isAudioOnlyPlayer)
}
PlayingQueue.clear()
fillQueue() fillQueue()
} }
@ -116,23 +127,71 @@ open class OfflinePlayerService : AbstractPlayerService() {
} }
} }
open fun setMediaItem(downloadWithItems: DownloadWithItems) { private fun setMediaItem(downloadWithItems: DownloadWithItems) {
val audioItem = downloadWithItems.downloadItems.filter { it.path.exists() } val downloadFiles = downloadWithItems.downloadItems.filter { it.path.exists() }
.firstOrNull { it.type == FileType.AUDIO }
?: // in some rare cases, video files can contain audio
downloadWithItems.downloadItems.firstOrNull { it.type == FileType.VIDEO }
if (audioItem == null) { val videoUri = downloadFiles.firstOrNull { it.type == FileType.VIDEO }?.path?.toAndroidUri()
val audioUri = downloadFiles.firstOrNull { it.type == FileType.AUDIO }?.path?.toAndroidUri()
if (isAudioOnlyPlayer && audioUri == null) {
stopSelf() stopSelf()
return return
} }
val mediaItem = MediaItem.Builder() val subtitleInfo = downloadFiles.firstOrNull { it.type == FileType.SUBTITLE }
.setUri(audioItem.path.toAndroidUri())
.setMetadata(downloadWithItems)
.build()
exoPlayer?.setMediaItem(mediaItem) val subtitle = subtitleInfo?.let {
SubtitleConfiguration.Builder(it.path.toAndroidUri())
.setMimeType(MimeTypes.APPLICATION_TTML)
.setLanguage(it.language ?: "en")
.build()
}
when {
videoUri != null && audioUri != null -> {
val videoItem = MediaItem.Builder()
.setUri(videoUri)
.setMetadata(downloadWithItems)
.setSubtitleConfigurations(listOfNotNull(subtitle))
.build()
val videoSource = ProgressiveMediaSource.Factory(FileDataSource.Factory())
.createMediaSource(videoItem)
val audioSource = ProgressiveMediaSource.Factory(FileDataSource.Factory())
.createMediaSource(MediaItem.fromUri(audioUri))
var mediaSource = MergingMediaSource(audioSource, videoSource)
if (subtitle != null) {
val subtitleSource = SingleSampleMediaSource.Factory(FileDataSource.Factory())
.createMediaSource(subtitle, C.TIME_UNSET)
mediaSource = MergingMediaSource(mediaSource, subtitleSource)
}
exoPlayer?.setMediaSource(mediaSource)
}
videoUri != null -> exoPlayer?.setMediaItem(
MediaItem.Builder()
.setUri(videoUri)
.setMetadata(downloadWithItems)
.setSubtitleConfigurations(listOfNotNull(subtitle))
.build()
)
audioUri != null -> exoPlayer?.setMediaItem(
MediaItem.Builder()
.setUri(audioUri)
.setMetadata(downloadWithItems)
.setSubtitleConfigurations(listOfNotNull(subtitle))
.build()
)
}
trackSelector?.updateParameters {
setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)
setPreferredTextLanguage(subtitle?.language)
}
} }
private suspend fun fillQueue() { private suspend fun fillQueue() {

View File

@ -4,6 +4,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.media3.common.C
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.MediaItem.SubtitleConfiguration import androidx.media3.common.MediaItem.SubtitleConfiguration
import androidx.media3.common.MimeTypes import androidx.media3.common.MimeTypes
@ -25,6 +26,7 @@ import com.github.libretube.extensions.parcelable
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.checkForSegments import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PlayerHelper.getSubtitleRoleFlags import com.github.libretube.helpers.PlayerHelper.getSubtitleRoleFlags
@ -112,13 +114,14 @@ open class OnlinePlayerService : AbstractPlayerService() {
if (!playerData.keepQueue) PlayingQueue.clear() if (!playerData.keepQueue) PlayingQueue.clear()
exoPlayer?.addListener(playerListener) exoPlayer?.addListener(playerListener)
trackSelector?.updateParameters {
setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, isAudioOnlyPlayer)
}
} }
override suspend fun startPlayback() { override suspend fun startPlayback() {
super.startPlayback() super.startPlayback()
Log.e("start", "playback")
val timestampMs = startTimestampSeconds?.times(1000) ?: 0L val timestampMs = startTimestampSeconds?.times(1000) ?: 0L
startTimestampSeconds = null startTimestampSeconds = null

View File

@ -1,85 +0,0 @@
package com.github.libretube.services
import androidx.annotation.OptIn
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaItem.SubtitleConfiguration
import androidx.media3.common.MimeTypes
import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.FileDataSource
import androidx.media3.exoplayer.source.MergingMediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.exoplayer.source.SingleSampleMediaSource
import com.github.libretube.db.obj.DownloadWithItems
import com.github.libretube.enums.FileType
import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toAndroidUri
import com.github.libretube.extensions.updateParameters
import kotlin.io.path.exists
@OptIn(UnstableApi::class)
class VideoOfflinePlayerService: OfflinePlayerService() {
override var isAudioOnlyPlayer = false
override fun setMediaItem(downloadWithItems: DownloadWithItems) {
val downloadFiles = downloadWithItems.downloadItems.filter { it.path.exists() }
val videoUri = downloadFiles.firstOrNull { it.type == FileType.VIDEO }?.path?.toAndroidUri()
val audioUri = downloadFiles.firstOrNull { it.type == FileType.AUDIO }?.path?.toAndroidUri()
val subtitleInfo = downloadFiles.firstOrNull { it.type == FileType.SUBTITLE }
val subtitle = subtitleInfo?.let {
SubtitleConfiguration.Builder(it.path.toAndroidUri())
.setMimeType(MimeTypes.APPLICATION_TTML)
.setLanguage(it.language ?: "en")
.build()
}
when {
videoUri != null && audioUri != null -> {
val videoItem = MediaItem.Builder()
.setUri(videoUri)
.setMetadata(downloadWithItems)
.setSubtitleConfigurations(listOfNotNull(subtitle))
.build()
val videoSource = ProgressiveMediaSource.Factory(FileDataSource.Factory())
.createMediaSource(videoItem)
val audioSource = ProgressiveMediaSource.Factory(FileDataSource.Factory())
.createMediaSource(MediaItem.fromUri(audioUri))
var mediaSource = MergingMediaSource(audioSource, videoSource)
if (subtitle != null) {
val subtitleSource = SingleSampleMediaSource.Factory(FileDataSource.Factory())
.createMediaSource(subtitle, C.TIME_UNSET)
mediaSource = MergingMediaSource(mediaSource, subtitleSource)
}
exoPlayer?.setMediaSource(mediaSource)
}
videoUri != null -> exoPlayer?.setMediaItem(
MediaItem.Builder()
.setUri(videoUri)
.setMetadata(downloadWithItems)
.setSubtitleConfigurations(listOfNotNull(subtitle))
.build()
)
audioUri != null -> exoPlayer?.setMediaItem(
MediaItem.Builder()
.setUri(audioUri)
.setMetadata(downloadWithItems)
.setSubtitleConfigurations(listOfNotNull(subtitle))
.build()
)
}
trackSelector?.updateParameters {
setPreferredTextRoleFlags(C.ROLE_FLAG_CAPTION)
setPreferredTextLanguage(subtitle?.language)
}
}
}

View File

@ -32,7 +32,7 @@ import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.WindowHelper import com.github.libretube.helpers.WindowHelper
import com.github.libretube.services.AbstractPlayerService import com.github.libretube.services.AbstractPlayerService
import com.github.libretube.services.VideoOfflinePlayerService import com.github.libretube.services.OfflinePlayerService
import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.fragments.DownloadTab import com.github.libretube.ui.fragments.DownloadTab
import com.github.libretube.ui.interfaces.TimeFrameReceiver import com.github.libretube.ui.interfaces.TimeFrameReceiver
@ -139,9 +139,10 @@ class OfflinePlayerActivity : BaseActivity() {
val arguments = bundleOf( val arguments = bundleOf(
IntentData.downloadTab to DownloadTab.VIDEO, IntentData.downloadTab to DownloadTab.VIDEO,
IntentData.videoId to videoId IntentData.videoId to videoId,
IntentData.audioOnly to false
) )
BackgroundHelper.startMediaService(this, VideoOfflinePlayerService::class.java, arguments) { BackgroundHelper.startMediaService(this, OfflinePlayerService::class.java, arguments) {
playerController = it playerController = it
playerController.addListener(playerListener) playerController.addListener(playerListener)
initializePlayerView() initializePlayerView()

View File

@ -95,7 +95,6 @@ class AudioPlayerFragment : Fragment(R.layout.fragment_audio_player), AudioPlaye
BackgroundHelper.startMediaService( BackgroundHelper.startMediaService(
requireContext(), requireContext(),
if (isOffline) OfflinePlayerService::class.java else OnlinePlayerService::class.java, if (isOffline) OfflinePlayerService::class.java else OnlinePlayerService::class.java,
bundleOf()
) { ) {
if (_binding == null) { if (_binding == null) {
it.sendCustomCommand(AbstractPlayerService.stopServiceCommand, Bundle.EMPTY) it.sendCustomCommand(AbstractPlayerService.stopServiceCommand, Bundle.EMPTY)