mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-19 09:08:26 +02:00
Autoformatting files
This commit is contained in:
parent
9492975a74
commit
b2ebeafed5
6 changed files with 1411 additions and 893 deletions
|
@ -8,10 +8,10 @@ class MediaItemHistory(
|
||||||
var mediaDisplayTitle: String,
|
var mediaDisplayTitle: String,
|
||||||
var libraryItemId: String,
|
var libraryItemId: String,
|
||||||
var episodeId: String?,
|
var episodeId: String?,
|
||||||
var isLocal:Boolean,
|
var isLocal: Boolean,
|
||||||
var serverConnectionConfigId:String?,
|
var serverConnectionConfigId: String?,
|
||||||
var serverAddress:String?,
|
var serverAddress: String?,
|
||||||
var serverUserId:String?,
|
var serverUserId: String?,
|
||||||
var createdAt: Long,
|
var createdAt: Long,
|
||||||
var events:MutableList<MediaItemEvent>,
|
var events: MutableList<MediaItemEvent>,
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.audiobookshelf.app.BuildConfig
|
||||||
import com.audiobookshelf.app.R
|
import com.audiobookshelf.app.R
|
||||||
import com.audiobookshelf.app.device.DeviceManager
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
import com.audiobookshelf.app.media.MediaProgressSyncData
|
import com.audiobookshelf.app.media.MediaProgressSyncData
|
||||||
|
import com.audiobookshelf.app.player.*
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
import com.google.android.exoplayer2.MediaItem
|
import com.google.android.exoplayer2.MediaItem
|
||||||
|
@ -19,61 +20,75 @@ import com.google.android.exoplayer2.MediaMetadata
|
||||||
import com.google.android.gms.cast.MediaInfo
|
import com.google.android.gms.cast.MediaInfo
|
||||||
import com.google.android.gms.cast.MediaQueueItem
|
import com.google.android.gms.cast.MediaQueueItem
|
||||||
import com.google.android.gms.common.images.WebImage
|
import com.google.android.gms.common.images.WebImage
|
||||||
import com.audiobookshelf.app.player.*
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
class PlaybackSession(
|
class PlaybackSession(
|
||||||
var id:String,
|
var id: String,
|
||||||
var userId:String?,
|
var userId: String?,
|
||||||
var libraryItemId:String?,
|
var libraryItemId: String?,
|
||||||
var episodeId:String?,
|
var episodeId: String?,
|
||||||
var mediaType:String,
|
var mediaType: String,
|
||||||
var mediaMetadata:MediaTypeMetadata,
|
var mediaMetadata: MediaTypeMetadata,
|
||||||
var deviceInfo:DeviceInfo,
|
var deviceInfo: DeviceInfo,
|
||||||
var chapters:List<BookChapter>,
|
var chapters: List<BookChapter>,
|
||||||
var displayTitle: String?,
|
var displayTitle: String?,
|
||||||
var displayAuthor: String?,
|
var displayAuthor: String?,
|
||||||
var coverPath:String?,
|
var coverPath: String?,
|
||||||
var duration:Double,
|
var duration: Double,
|
||||||
var playMethod:Int,
|
var playMethod: Int,
|
||||||
var startedAt:Long,
|
var startedAt: Long,
|
||||||
var updatedAt:Long,
|
var updatedAt: Long,
|
||||||
var timeListening:Long,
|
var timeListening: Long,
|
||||||
var audioTracks:MutableList<AudioTrack>,
|
var audioTracks: MutableList<AudioTrack>,
|
||||||
var currentTime:Double,
|
var currentTime: Double,
|
||||||
var libraryItem:LibraryItem?,
|
var libraryItem: LibraryItem?,
|
||||||
var localLibraryItem:LocalLibraryItem?,
|
var localLibraryItem: LocalLibraryItem?,
|
||||||
var localEpisodeId:String?,
|
var localEpisodeId: String?,
|
||||||
var serverConnectionConfigId:String?,
|
var serverConnectionConfigId: String?,
|
||||||
var serverAddress:String?,
|
var serverAddress: String?,
|
||||||
var mediaPlayer:String?
|
var mediaPlayer: String?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE
|
val isHLS
|
||||||
|
get() = playMethod == PLAYMETHOD_TRANSCODE
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isDirectPlay get() = playMethod == PLAYMETHOD_DIRECTPLAY
|
val isDirectPlay
|
||||||
|
get() = playMethod == PLAYMETHOD_DIRECTPLAY
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isLocal get() = playMethod == PLAYMETHOD_LOCAL
|
val isLocal
|
||||||
|
get() = playMethod == PLAYMETHOD_LOCAL
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isPodcastEpisode get() = mediaType == "podcast"
|
val isPodcastEpisode
|
||||||
|
get() = mediaType == "podcast"
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val currentTimeMs get() = (currentTime * 1000L).toLong()
|
val currentTimeMs
|
||||||
|
get() = (currentTime * 1000L).toLong()
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val totalDurationMs get() = (getTotalDuration() * 1000L).toLong()
|
val totalDurationMs
|
||||||
|
get() = (getTotalDuration() * 1000L).toLong()
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val localLibraryItemId get() = localLibraryItem?.id ?: ""
|
val localLibraryItemId
|
||||||
|
get() = localLibraryItem?.id ?: ""
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val localMediaProgressId get() = if (localEpisodeId.isNullOrEmpty()) localLibraryItemId else "$localLibraryItemId-$localEpisodeId"
|
val localMediaProgressId
|
||||||
|
get() =
|
||||||
|
if (localEpisodeId.isNullOrEmpty()) localLibraryItemId
|
||||||
|
else "$localLibraryItemId-$localEpisodeId"
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val progress get() = currentTime / getTotalDuration()
|
val progress
|
||||||
|
get() = currentTime / getTotalDuration()
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isLocalLibraryItemOnly get() = localLibraryItemId != "" && libraryItemId == null
|
val isLocalLibraryItemOnly
|
||||||
|
get() = localLibraryItemId != "" && libraryItemId == null
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val mediaItemId get() = if (isLocalLibraryItemOnly) localMediaProgressId else if (episodeId.isNullOrEmpty()) libraryItemId ?: "" else "$libraryItemId-$episodeId"
|
val mediaItemId
|
||||||
|
get() =
|
||||||
|
if (isLocalLibraryItemOnly) localMediaProgressId
|
||||||
|
else if (episodeId.isNullOrEmpty()) libraryItemId ?: "" else "$libraryItemId-$episodeId"
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCurrentTrackIndex():Int {
|
fun getCurrentTrackIndex(): Int {
|
||||||
for (i in 0 until audioTracks.size) {
|
for (i in 0 until audioTracks.size) {
|
||||||
val track = audioTracks[i]
|
val track = audioTracks[i]
|
||||||
if (currentTimeMs >= track.startOffsetMs && (track.endOffsetMs > currentTimeMs)) {
|
if (currentTimeMs >= track.startOffsetMs && (track.endOffsetMs > currentTimeMs)) {
|
||||||
|
@ -84,7 +99,7 @@ class PlaybackSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getNextTrackIndex():Int {
|
fun getNextTrackIndex(): Int {
|
||||||
for (i in 0 until audioTracks.size) {
|
for (i in 0 until audioTracks.size) {
|
||||||
val track = audioTracks[i]
|
val track = audioTracks[i]
|
||||||
if (currentTimeMs < track.startOffsetMs) {
|
if (currentTimeMs < track.startOffsetMs) {
|
||||||
|
@ -95,67 +110,74 @@ class PlaybackSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getChapterForTime(time:Long):BookChapter? {
|
fun getChapterForTime(time: Long): BookChapter? {
|
||||||
if (chapters.isEmpty()) return null
|
if (chapters.isEmpty()) return null
|
||||||
return chapters.find { time >= it.startMs && it.endMs > time}
|
return chapters.find { time >= it.startMs && it.endMs > time }
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCurrentTrackEndTime():Long {
|
fun getCurrentTrackEndTime(): Long {
|
||||||
val currentTrack = audioTracks[this.getCurrentTrackIndex()]
|
val currentTrack = audioTracks[this.getCurrentTrackIndex()]
|
||||||
return currentTrack.startOffsetMs + currentTrack.durationMs
|
return currentTrack.startOffsetMs + currentTrack.durationMs
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getNextChapterForTime(time:Long):BookChapter? {
|
fun getNextChapterForTime(time: Long): BookChapter? {
|
||||||
if (chapters.isEmpty()) return null
|
if (chapters.isEmpty()) return null
|
||||||
return chapters.find { time < it.startMs } // First chapter where start time is > then time
|
return chapters.find { time < it.startMs } // First chapter where start time is > then time
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getNextTrackEndTime():Long {
|
fun getNextTrackEndTime(): Long {
|
||||||
val currentTrack = audioTracks[this.getNextTrackIndex()]
|
val currentTrack = audioTracks[this.getNextTrackIndex()]
|
||||||
return currentTrack.startOffsetMs + currentTrack.durationMs
|
return currentTrack.startOffsetMs + currentTrack.durationMs
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCurrentTrackTimeMs():Long {
|
fun getCurrentTrackTimeMs(): Long {
|
||||||
val currentTrack = audioTracks[this.getCurrentTrackIndex()]
|
val currentTrack = audioTracks[this.getCurrentTrackIndex()]
|
||||||
val time = currentTime - currentTrack.startOffset
|
val time = currentTime - currentTrack.startOffset
|
||||||
return (time * 1000L).toLong()
|
return (time * 1000L).toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getTrackStartOffsetMs(index:Int):Long {
|
fun getTrackStartOffsetMs(index: Int): Long {
|
||||||
if (index < 0 || index >= audioTracks.size) return 0L
|
if (index < 0 || index >= audioTracks.size) return 0L
|
||||||
val currentTrack = audioTracks[index]
|
val currentTrack = audioTracks[index]
|
||||||
return (currentTrack.startOffset * 1000L).toLong()
|
return (currentTrack.startOffset * 1000L).toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getTotalDuration():Double {
|
fun getTotalDuration(): Double {
|
||||||
var total = 0.0
|
var total = 0.0
|
||||||
audioTracks.forEach { total += it.duration }
|
audioTracks.forEach { total += it.duration }
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCoverUri(ctx:Context): Uri {
|
fun getCoverUri(ctx: Context): Uri {
|
||||||
if (localLibraryItem?.coverContentUrl != null) {
|
if (localLibraryItem?.coverContentUrl != null) {
|
||||||
var coverUri = Uri.parse(localLibraryItem?.coverContentUrl.toString())
|
var coverUri = Uri.parse(localLibraryItem?.coverContentUrl.toString())
|
||||||
if (coverUri.toString().startsWith("file:")) {
|
if (coverUri.toString().startsWith("file:")) {
|
||||||
coverUri = FileProvider.getUriForFile(ctx, "${BuildConfig.APPLICATION_ID}.fileprovider", coverUri.toFile())
|
coverUri =
|
||||||
|
FileProvider.getUriForFile(
|
||||||
|
ctx,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.fileprovider",
|
||||||
|
coverUri.toFile()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return coverUri ?: Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
return coverUri
|
||||||
|
?: Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverPath == null) return Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
if (coverPath == null)
|
||||||
|
return Uri.parse("android.resource://${BuildConfig.APPLICATION_ID}/" + R.drawable.icon)
|
||||||
return Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")
|
return Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getContentUri(audioTrack:AudioTrack): Uri {
|
fun getContentUri(audioTrack: AudioTrack): Uri {
|
||||||
if (isLocal) return Uri.parse(audioTrack.contentUrl) // Local content url
|
if (isLocal) return Uri.parse(audioTrack.contentUrl) // Local content url
|
||||||
return Uri.parse("$serverAddress${audioTrack.contentUrl}?token=${DeviceManager.token}")
|
return Uri.parse("$serverAddress${audioTrack.contentUrl}?token=${DeviceManager.token}")
|
||||||
}
|
}
|
||||||
|
@ -164,7 +186,8 @@ class PlaybackSession(
|
||||||
fun getMediaMetadataCompat(ctx: Context): MediaMetadataCompat {
|
fun getMediaMetadataCompat(ctx: Context): MediaMetadataCompat {
|
||||||
val coverUri = getCoverUri(ctx)
|
val coverUri = getCoverUri(ctx)
|
||||||
|
|
||||||
val metadataBuilder = MediaMetadataCompat.Builder()
|
val metadataBuilder =
|
||||||
|
MediaMetadataCompat.Builder()
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, displayTitle)
|
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, displayTitle)
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle)
|
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle)
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor)
|
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor)
|
||||||
|
@ -176,14 +199,19 @@ class PlaybackSession(
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
|
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, coverUri.toString())
|
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, coverUri.toString())
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, coverUri.toString())
|
.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, coverUri.toString())
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, coverUri.toString())
|
.putString(
|
||||||
|
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI,
|
||||||
|
coverUri.toString()
|
||||||
|
)
|
||||||
|
|
||||||
// Local covers get bitmap
|
// Local covers get bitmap
|
||||||
if (localLibraryItem?.coverContentUrl != null) {
|
if (localLibraryItem?.coverContentUrl != null) {
|
||||||
val bitmap = if (Build.VERSION.SDK_INT < 28) {
|
val bitmap =
|
||||||
|
if (Build.VERSION.SDK_INT < 28) {
|
||||||
MediaStore.Images.Media.getBitmap(ctx.contentResolver, coverUri)
|
MediaStore.Images.Media.getBitmap(ctx.contentResolver, coverUri)
|
||||||
} else {
|
} else {
|
||||||
val source: ImageDecoder.Source = ImageDecoder.createSource(ctx.contentResolver, coverUri)
|
val source: ImageDecoder.Source =
|
||||||
|
ImageDecoder.createSource(ctx.contentResolver, coverUri)
|
||||||
ImageDecoder.decodeBitmap(source)
|
ImageDecoder.decodeBitmap(source)
|
||||||
}
|
}
|
||||||
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
|
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
|
||||||
|
@ -194,10 +222,11 @@ class PlaybackSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getExoMediaMetadata(ctx:Context): MediaMetadata {
|
fun getExoMediaMetadata(ctx: Context): MediaMetadata {
|
||||||
val coverUri = getCoverUri(ctx)
|
val coverUri = getCoverUri(ctx)
|
||||||
|
|
||||||
val metadataBuilder = MediaMetadata.Builder()
|
val metadataBuilder =
|
||||||
|
MediaMetadata.Builder()
|
||||||
.setTitle(displayTitle)
|
.setTitle(displayTitle)
|
||||||
.setDisplayTitle(displayTitle)
|
.setDisplayTitle(displayTitle)
|
||||||
.setArtist(displayAuthor)
|
.setArtist(displayAuthor)
|
||||||
|
@ -212,8 +241,8 @@ class PlaybackSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getMediaItems(ctx:Context):List<MediaItem> {
|
fun getMediaItems(ctx: Context): List<MediaItem> {
|
||||||
val mediaItems:MutableList<MediaItem> = mutableListOf()
|
val mediaItems: MutableList<MediaItem> = mutableListOf()
|
||||||
|
|
||||||
for (audioTrack in audioTracks) {
|
for (audioTrack in audioTracks) {
|
||||||
val mediaMetadata = this.getExoMediaMetadata(ctx)
|
val mediaMetadata = this.getExoMediaMetadata(ctx)
|
||||||
|
@ -221,50 +250,105 @@ class PlaybackSession(
|
||||||
val mimeType = audioTrack.mimeType
|
val mimeType = audioTrack.mimeType
|
||||||
|
|
||||||
val queueItem = getQueueItem(audioTrack) // Queue item used in exo player CastManager
|
val queueItem = getQueueItem(audioTrack) // Queue item used in exo player CastManager
|
||||||
val mediaItem = MediaItem.Builder().setUri(mediaUri).setTag(queueItem).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
|
val mediaItem =
|
||||||
|
MediaItem.Builder()
|
||||||
|
.setUri(mediaUri)
|
||||||
|
.setTag(queueItem)
|
||||||
|
.setMediaMetadata(mediaMetadata)
|
||||||
|
.setMimeType(mimeType)
|
||||||
|
.build()
|
||||||
mediaItems.add(mediaItem)
|
mediaItems.add(mediaItem)
|
||||||
}
|
}
|
||||||
return mediaItems
|
return mediaItems
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCastMediaMetadata(audioTrack:AudioTrack):com.google.android.gms.cast.MediaMetadata {
|
fun getCastMediaMetadata(audioTrack: AudioTrack): com.google.android.gms.cast.MediaMetadata {
|
||||||
val castMetadata = com.google.android.gms.cast.MediaMetadata(com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER)
|
val castMetadata =
|
||||||
|
com.google.android.gms.cast.MediaMetadata(
|
||||||
|
com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER
|
||||||
|
)
|
||||||
|
|
||||||
coverPath?.let {
|
coverPath?.let {
|
||||||
castMetadata.addImage(WebImage(Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")))
|
castMetadata.addImage(
|
||||||
|
WebImage(
|
||||||
|
Uri.parse(
|
||||||
|
"$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_TITLE, displayTitle ?: "")
|
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_TITLE, displayTitle ?: "")
|
||||||
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_ARTIST, displayAuthor ?: "")
|
castMetadata.putString(
|
||||||
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_ALBUM_TITLE, displayAuthor ?: "")
|
com.google.android.gms.cast.MediaMetadata.KEY_ARTIST,
|
||||||
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_CHAPTER_TITLE, audioTrack.title)
|
displayAuthor ?: ""
|
||||||
|
)
|
||||||
|
castMetadata.putString(
|
||||||
|
com.google.android.gms.cast.MediaMetadata.KEY_ALBUM_TITLE,
|
||||||
|
displayAuthor ?: ""
|
||||||
|
)
|
||||||
|
castMetadata.putString(
|
||||||
|
com.google.android.gms.cast.MediaMetadata.KEY_CHAPTER_TITLE,
|
||||||
|
audioTrack.title
|
||||||
|
)
|
||||||
|
|
||||||
castMetadata.putInt(com.google.android.gms.cast.MediaMetadata.KEY_TRACK_NUMBER, audioTrack.index)
|
castMetadata.putInt(
|
||||||
|
com.google.android.gms.cast.MediaMetadata.KEY_TRACK_NUMBER,
|
||||||
|
audioTrack.index
|
||||||
|
)
|
||||||
return castMetadata
|
return castMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getQueueItem(audioTrack:AudioTrack):MediaQueueItem {
|
fun getQueueItem(audioTrack: AudioTrack): MediaQueueItem {
|
||||||
val castMetadata = getCastMediaMetadata(audioTrack)
|
val castMetadata = getCastMediaMetadata(audioTrack)
|
||||||
|
|
||||||
val mediaUri = getContentUri(audioTrack)
|
val mediaUri = getContentUri(audioTrack)
|
||||||
|
|
||||||
val mediaInfo = MediaInfo.Builder(mediaUri.toString()).apply {
|
val mediaInfo =
|
||||||
|
MediaInfo.Builder(mediaUri.toString())
|
||||||
|
.apply {
|
||||||
setContentUrl(mediaUri.toString())
|
setContentUrl(mediaUri.toString())
|
||||||
setContentType(audioTrack.mimeType)
|
setContentType(audioTrack.mimeType)
|
||||||
setMetadata(castMetadata)
|
setMetadata(castMetadata)
|
||||||
setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||||
}.build()
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
return MediaQueueItem.Builder(mediaInfo).apply {
|
return MediaQueueItem.Builder(mediaInfo)
|
||||||
setPlaybackDuration(audioTrack.duration)
|
.apply { setPlaybackDuration(audioTrack.duration) }
|
||||||
}.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun clone():PlaybackSession {
|
fun clone(): PlaybackSession {
|
||||||
return PlaybackSession(id,userId,libraryItemId,episodeId,mediaType,mediaMetadata,deviceInfo,chapters,displayTitle,displayAuthor,coverPath,duration,playMethod,startedAt,updatedAt,timeListening,audioTracks,currentTime,libraryItem,localLibraryItem,localEpisodeId,serverConnectionConfigId,serverAddress, mediaPlayer)
|
return PlaybackSession(
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
libraryItemId,
|
||||||
|
episodeId,
|
||||||
|
mediaType,
|
||||||
|
mediaMetadata,
|
||||||
|
deviceInfo,
|
||||||
|
chapters,
|
||||||
|
displayTitle,
|
||||||
|
displayAuthor,
|
||||||
|
coverPath,
|
||||||
|
duration,
|
||||||
|
playMethod,
|
||||||
|
startedAt,
|
||||||
|
updatedAt,
|
||||||
|
timeListening,
|
||||||
|
audioTracks,
|
||||||
|
currentTime,
|
||||||
|
libraryItem,
|
||||||
|
localLibraryItem,
|
||||||
|
localEpisodeId,
|
||||||
|
serverConnectionConfigId,
|
||||||
|
serverAddress,
|
||||||
|
mediaPlayer
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
@ -275,7 +359,25 @@ class PlaybackSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getNewLocalMediaProgress():LocalMediaProgress {
|
fun getNewLocalMediaProgress(): LocalMediaProgress {
|
||||||
return LocalMediaProgress(localMediaProgressId,localLibraryItemId,localEpisodeId,getTotalDuration(),progress,currentTime,false,null,null,updatedAt,startedAt,null,serverConnectionConfigId,serverAddress,userId,libraryItemId,episodeId)
|
return LocalMediaProgress(
|
||||||
|
localMediaProgressId,
|
||||||
|
localLibraryItemId,
|
||||||
|
localEpisodeId,
|
||||||
|
getTotalDuration(),
|
||||||
|
progress,
|
||||||
|
currentTime,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
updatedAt,
|
||||||
|
startedAt,
|
||||||
|
null,
|
||||||
|
serverConnectionConfigId,
|
||||||
|
serverAddress,
|
||||||
|
userId,
|
||||||
|
libraryItemId,
|
||||||
|
episodeId
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,41 +22,43 @@ class DbManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDeviceData(): DeviceData {
|
fun getDeviceData(): DeviceData {
|
||||||
return Paper.book("device").read("data") ?: DeviceData(mutableListOf(), null, DeviceSettings.default(), null)
|
return Paper.book("device").read("data")
|
||||||
|
?: DeviceData(mutableListOf(), null, DeviceSettings.default(), null)
|
||||||
}
|
}
|
||||||
fun saveDeviceData(deviceData: DeviceData) {
|
fun saveDeviceData(deviceData: DeviceData) {
|
||||||
Paper.book("device").write("data", deviceData)
|
Paper.book("device").write("data", deviceData)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocalLibraryItems(mediaType:String? = null):MutableList<LocalLibraryItem> {
|
fun getLocalLibraryItems(mediaType: String? = null): MutableList<LocalLibraryItem> {
|
||||||
val localLibraryItems:MutableList<LocalLibraryItem> = mutableListOf()
|
val localLibraryItems: MutableList<LocalLibraryItem> = mutableListOf()
|
||||||
Paper.book("localLibraryItems").allKeys.forEach {
|
Paper.book("localLibraryItems").allKeys.forEach {
|
||||||
val localLibraryItem: LocalLibraryItem? = Paper.book("localLibraryItems").read(it)
|
val localLibraryItem: LocalLibraryItem? = Paper.book("localLibraryItems").read(it)
|
||||||
if (localLibraryItem != null && (mediaType.isNullOrEmpty() || mediaType == localLibraryItem.mediaType)) {
|
if (localLibraryItem != null &&
|
||||||
|
(mediaType.isNullOrEmpty() || mediaType == localLibraryItem.mediaType)
|
||||||
|
) {
|
||||||
localLibraryItems.add(localLibraryItem)
|
localLibraryItems.add(localLibraryItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return localLibraryItems
|
return localLibraryItems
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocalLibraryItemsInFolder(folderId:String):List<LocalLibraryItem> {
|
fun getLocalLibraryItemsInFolder(folderId: String): List<LocalLibraryItem> {
|
||||||
val localLibraryItems = getLocalLibraryItems()
|
val localLibraryItems = getLocalLibraryItems()
|
||||||
return localLibraryItems.filter {
|
return localLibraryItems.filter { it.folderId == folderId }
|
||||||
it.folderId == folderId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocalLibraryItemByLId(libraryItemId:String): LocalLibraryItem? {
|
fun getLocalLibraryItemByLId(libraryItemId: String): LocalLibraryItem? {
|
||||||
return getLocalLibraryItems().find { it.libraryItemId == libraryItemId }
|
return getLocalLibraryItems().find { it.libraryItemId == libraryItemId }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocalLibraryItem(localLibraryItemId:String): LocalLibraryItem? {
|
fun getLocalLibraryItem(localLibraryItemId: String): LocalLibraryItem? {
|
||||||
return Paper.book("localLibraryItems").read(localLibraryItemId)
|
return Paper.book("localLibraryItems").read(localLibraryItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocalLibraryItemWithEpisode(podcastEpisodeId:String): LibraryItemWithEpisode? {
|
fun getLocalLibraryItemWithEpisode(podcastEpisodeId: String): LibraryItemWithEpisode? {
|
||||||
var podcastEpisode: PodcastEpisode? = null
|
var podcastEpisode: PodcastEpisode? = null
|
||||||
val localLibraryItem = getLocalLibraryItems("podcast").find { localLibraryItem ->
|
val localLibraryItem =
|
||||||
|
getLocalLibraryItems("podcast").find { localLibraryItem ->
|
||||||
val podcast = localLibraryItem.media as Podcast
|
val podcast = localLibraryItem.media as Podcast
|
||||||
podcastEpisode = podcast.episodes?.find { it.id == podcastEpisodeId }
|
podcastEpisode = podcast.episodes?.find { it.id == podcastEpisodeId }
|
||||||
podcastEpisode != null
|
podcastEpisode != null
|
||||||
|
@ -68,14 +70,12 @@ class DbManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeLocalLibraryItem(localLibraryItemId:String) {
|
fun removeLocalLibraryItem(localLibraryItemId: String) {
|
||||||
Paper.book("localLibraryItems").delete(localLibraryItemId)
|
Paper.book("localLibraryItems").delete(localLibraryItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveLocalLibraryItems(localLibraryItems:List<LocalLibraryItem>) {
|
fun saveLocalLibraryItems(localLibraryItems: List<LocalLibraryItem>) {
|
||||||
localLibraryItems.map {
|
localLibraryItems.map { Paper.book("localLibraryItems").write(it.id, it) }
|
||||||
Paper.book("localLibraryItems").write(it.id, it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveLocalLibraryItem(localLibraryItem: LocalLibraryItem) {
|
fun saveLocalLibraryItem(localLibraryItem: LocalLibraryItem) {
|
||||||
|
@ -83,28 +83,24 @@ class DbManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveLocalFolder(localFolder: LocalFolder) {
|
fun saveLocalFolder(localFolder: LocalFolder) {
|
||||||
Paper.book("localFolders").write(localFolder.id,localFolder)
|
Paper.book("localFolders").write(localFolder.id, localFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocalFolder(folderId:String): LocalFolder? {
|
fun getLocalFolder(folderId: String): LocalFolder? {
|
||||||
return Paper.book("localFolders").read(folderId)
|
return Paper.book("localFolders").read(folderId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllLocalFolders():List<LocalFolder> {
|
fun getAllLocalFolders(): List<LocalFolder> {
|
||||||
val localFolders:MutableList<LocalFolder> = mutableListOf()
|
val localFolders: MutableList<LocalFolder> = mutableListOf()
|
||||||
Paper.book("localFolders").allKeys.forEach { localFolderId ->
|
Paper.book("localFolders").allKeys.forEach { localFolderId ->
|
||||||
Paper.book("localFolders").read<LocalFolder>(localFolderId)?.let {
|
Paper.book("localFolders").read<LocalFolder>(localFolderId)?.let { localFolders.add(it) }
|
||||||
localFolders.add(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return localFolders
|
return localFolders
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeLocalFolder(folderId:String) {
|
fun removeLocalFolder(folderId: String) {
|
||||||
val localLibraryItems = getLocalLibraryItemsInFolder(folderId)
|
val localLibraryItems = getLocalLibraryItemsInFolder(folderId)
|
||||||
localLibraryItems.forEach {
|
localLibraryItems.forEach { Paper.book("localLibraryItems").delete(it.id) }
|
||||||
Paper.book("localLibraryItems").delete(it.id)
|
|
||||||
}
|
|
||||||
Paper.book("localFolders").delete(folderId)
|
Paper.book("localFolders").delete(folderId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,29 +108,28 @@ class DbManager {
|
||||||
Paper.book("downloadItems").write(downloadItem.id, downloadItem)
|
Paper.book("downloadItems").write(downloadItem.id, downloadItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeDownloadItem(downloadItemId:String) {
|
fun removeDownloadItem(downloadItemId: String) {
|
||||||
Paper.book("downloadItems").delete(downloadItemId)
|
Paper.book("downloadItems").delete(downloadItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDownloadItems():List<DownloadItem> {
|
fun getDownloadItems(): List<DownloadItem> {
|
||||||
val downloadItems:MutableList<DownloadItem> = mutableListOf()
|
val downloadItems: MutableList<DownloadItem> = mutableListOf()
|
||||||
Paper.book("downloadItems").allKeys.forEach { downloadItemId ->
|
Paper.book("downloadItems").allKeys.forEach { downloadItemId ->
|
||||||
Paper.book("downloadItems").read<DownloadItem>(downloadItemId)?.let {
|
Paper.book("downloadItems").read<DownloadItem>(downloadItemId)?.let { downloadItems.add(it) }
|
||||||
downloadItems.add(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return downloadItems
|
return downloadItems
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveLocalMediaProgress(mediaProgress: LocalMediaProgress) {
|
fun saveLocalMediaProgress(mediaProgress: LocalMediaProgress) {
|
||||||
Paper.book("localMediaProgress").write(mediaProgress.id,mediaProgress)
|
Paper.book("localMediaProgress").write(mediaProgress.id, mediaProgress)
|
||||||
}
|
}
|
||||||
// For books this will just be the localLibraryItemId for podcast episodes this will be "{localLibraryItemId}-{episodeId}"
|
// For books this will just be the localLibraryItemId for podcast episodes this will be
|
||||||
fun getLocalMediaProgress(localMediaProgressId:String): LocalMediaProgress? {
|
// "{localLibraryItemId}-{episodeId}"
|
||||||
|
fun getLocalMediaProgress(localMediaProgressId: String): LocalMediaProgress? {
|
||||||
return Paper.book("localMediaProgress").read(localMediaProgressId)
|
return Paper.book("localMediaProgress").read(localMediaProgressId)
|
||||||
}
|
}
|
||||||
fun getAllLocalMediaProgress():List<LocalMediaProgress> {
|
fun getAllLocalMediaProgress(): List<LocalMediaProgress> {
|
||||||
val mediaProgress:MutableList<LocalMediaProgress> = mutableListOf()
|
val mediaProgress: MutableList<LocalMediaProgress> = mutableListOf()
|
||||||
Paper.book("localMediaProgress").allKeys.forEach { localMediaProgressId ->
|
Paper.book("localMediaProgress").allKeys.forEach { localMediaProgressId ->
|
||||||
Paper.book("localMediaProgress").read<LocalMediaProgress>(localMediaProgressId)?.let {
|
Paper.book("localMediaProgress").read<LocalMediaProgress>(localMediaProgressId)?.let {
|
||||||
mediaProgress.add(it)
|
mediaProgress.add(it)
|
||||||
|
@ -142,7 +137,7 @@ class DbManager {
|
||||||
}
|
}
|
||||||
return mediaProgress
|
return mediaProgress
|
||||||
}
|
}
|
||||||
fun removeLocalMediaProgress(localMediaProgressId:String) {
|
fun removeLocalMediaProgress(localMediaProgressId: String) {
|
||||||
Paper.book("localMediaProgress").delete(localMediaProgressId)
|
Paper.book("localMediaProgress").delete(localMediaProgressId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,35 +153,50 @@ class DbManager {
|
||||||
var hasUpdates = false
|
var hasUpdates = false
|
||||||
|
|
||||||
// Check local files
|
// Check local files
|
||||||
lli.localFiles = lli.localFiles.filter { localFile ->
|
lli.localFiles =
|
||||||
|
lli.localFiles.filter { localFile ->
|
||||||
val file = File(localFile.absolutePath)
|
val file = File(localFile.absolutePath)
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
Log.d(tag, "cleanLocalLibraryItems: Local file ${localFile.absolutePath} was removed from library item ${lli.media.metadata.title}")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalLibraryItems: Local file ${localFile.absolutePath} was removed from library item ${lli.media.metadata.title}"
|
||||||
|
)
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
}
|
}
|
||||||
file.exists()
|
file.exists()
|
||||||
} as MutableList<LocalFile>
|
} as
|
||||||
|
MutableList<LocalFile>
|
||||||
|
|
||||||
// Check audio tracks and episodes
|
// Check audio tracks and episodes
|
||||||
if (lli.isPodcast) {
|
if (lli.isPodcast) {
|
||||||
val podcast = lli.media as Podcast
|
val podcast = lli.media as Podcast
|
||||||
podcast.episodes = podcast.episodes?.filter { ep ->
|
podcast.episodes =
|
||||||
|
podcast.episodes?.filter { ep ->
|
||||||
if (lli.localFiles.find { lf -> lf.id == ep.audioTrack?.localFileId } == null) {
|
if (lli.localFiles.find { lf -> lf.id == ep.audioTrack?.localFileId } == null) {
|
||||||
Log.d(tag, "cleanLocalLibraryItems: Podcast episode ${ep.title} was removed from library item ${lli.media.metadata.title}")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalLibraryItems: Podcast episode ${ep.title} was removed from library item ${lli.media.metadata.title}"
|
||||||
|
)
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
}
|
}
|
||||||
ep.audioTrack != null && lli.localFiles.find { lf -> lf.id == ep.audioTrack?.localFileId } != null
|
ep.audioTrack != null &&
|
||||||
} as MutableList<PodcastEpisode>
|
lli.localFiles.find { lf -> lf.id == ep.audioTrack?.localFileId } != null
|
||||||
|
} as
|
||||||
|
MutableList<PodcastEpisode>
|
||||||
} else {
|
} else {
|
||||||
val book = lli.media as Book
|
val book = lli.media as Book
|
||||||
book.tracks = book.tracks?.filter { track ->
|
book.tracks =
|
||||||
|
book.tracks?.filter { track ->
|
||||||
if (lli.localFiles.find { lf -> lf.id == track.localFileId } == null) {
|
if (lli.localFiles.find { lf -> lf.id == track.localFileId } == null) {
|
||||||
Log.d(tag, "cleanLocalLibraryItems: Audio track ${track.title} was removed from library item ${lli.media.metadata.title}")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalLibraryItems: Audio track ${track.title} was removed from library item ${lli.media.metadata.title}"
|
||||||
|
)
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
}
|
}
|
||||||
lli.localFiles.find { lf -> lf.id == track.localFileId } != null
|
lli.localFiles.find { lf -> lf.id == track.localFileId } != null
|
||||||
} as MutableList<AudioTrack>
|
} as
|
||||||
|
MutableList<AudioTrack>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cover still there
|
// Check cover still there
|
||||||
|
@ -194,7 +204,10 @@ class DbManager {
|
||||||
val coverFile = File(it)
|
val coverFile = File(it)
|
||||||
|
|
||||||
if (!coverFile.exists()) {
|
if (!coverFile.exists()) {
|
||||||
Log.d(tag, "cleanLocalLibraryItems: Cover $it was removed from library item ${lli.media.metadata.title}")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalLibraryItems: Cover $it was removed from library item ${lli.media.metadata.title}"
|
||||||
|
)
|
||||||
lli.coverAbsolutePath = null
|
lli.coverAbsolutePath = null
|
||||||
lli.coverContentUrl = null
|
lli.coverContentUrl = null
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
|
@ -215,11 +228,18 @@ class DbManager {
|
||||||
localMediaProgress.forEach {
|
localMediaProgress.forEach {
|
||||||
val matchingLLI = localLibraryItems.find { lli -> lli.id == it.localLibraryItemId }
|
val matchingLLI = localLibraryItems.find { lli -> lli.id == it.localLibraryItemId }
|
||||||
if (!it.id.startsWith("local")) {
|
if (!it.id.startsWith("local")) {
|
||||||
// A bug on the server when syncing local media progress was replacing the media progress id causing duplicate progress. Remove them.
|
// A bug on the server when syncing local media progress was replacing the media progress id
|
||||||
Log.d(tag, "cleanLocalMediaProgress: Invalid local media progress does not start with 'local' (fixed on server 2.0.24)")
|
// causing duplicate progress. Remove them.
|
||||||
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalMediaProgress: Invalid local media progress does not start with 'local' (fixed on server 2.0.24)"
|
||||||
|
)
|
||||||
Paper.book("localMediaProgress").delete(it.id)
|
Paper.book("localMediaProgress").delete(it.id)
|
||||||
} else if (matchingLLI == null) {
|
} else if (matchingLLI == null) {
|
||||||
Log.d(tag, "cleanLocalMediaProgress: No matching local library item for local media progress ${it.id} - removing")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalMediaProgress: No matching local library item for local media progress ${it.id} - removing"
|
||||||
|
)
|
||||||
Paper.book("localMediaProgress").delete(it.id)
|
Paper.book("localMediaProgress").delete(it.id)
|
||||||
} else if (matchingLLI.isPodcast) {
|
} else if (matchingLLI.isPodcast) {
|
||||||
if (it.localEpisodeId.isNullOrEmpty()) {
|
if (it.localEpisodeId.isNullOrEmpty()) {
|
||||||
|
@ -229,7 +249,10 @@ class DbManager {
|
||||||
val podcast = matchingLLI.media as Podcast
|
val podcast = matchingLLI.media as Podcast
|
||||||
val matchingLEp = podcast.episodes?.find { ep -> ep.id == it.localEpisodeId }
|
val matchingLEp = podcast.episodes?.find { ep -> ep.id == it.localEpisodeId }
|
||||||
if (matchingLEp == null) {
|
if (matchingLEp == null) {
|
||||||
Log.d(tag, "cleanLocalMediaProgress: Podcast media progress for episode ${it.localEpisodeId} not found - removing")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"cleanLocalMediaProgress: Podcast media progress for episode ${it.localEpisodeId} not found - removing"
|
||||||
|
)
|
||||||
Paper.book("localMediaProgress").delete(it.id)
|
Paper.book("localMediaProgress").delete(it.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,20 +261,20 @@ class DbManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveMediaItemHistory(mediaItemHistory: MediaItemHistory) {
|
fun saveMediaItemHistory(mediaItemHistory: MediaItemHistory) {
|
||||||
Paper.book("mediaItemHistory").write(mediaItemHistory.id,mediaItemHistory)
|
Paper.book("mediaItemHistory").write(mediaItemHistory.id, mediaItemHistory)
|
||||||
}
|
}
|
||||||
fun getMediaItemHistory(id:String): MediaItemHistory? {
|
fun getMediaItemHistory(id: String): MediaItemHistory? {
|
||||||
return Paper.book("mediaItemHistory").read(id)
|
return Paper.book("mediaItemHistory").read(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun savePlaybackSession(playbackSession: PlaybackSession) {
|
fun savePlaybackSession(playbackSession: PlaybackSession) {
|
||||||
Paper.book("playbackSession").write(playbackSession.id,playbackSession)
|
Paper.book("playbackSession").write(playbackSession.id, playbackSession)
|
||||||
}
|
}
|
||||||
fun removePlaybackSession(playbackSessionId:String) {
|
fun removePlaybackSession(playbackSessionId: String) {
|
||||||
Paper.book("playbackSession").delete(playbackSessionId)
|
Paper.book("playbackSession").delete(playbackSessionId)
|
||||||
}
|
}
|
||||||
fun getPlaybackSessions():List<PlaybackSession> {
|
fun getPlaybackSessions(): List<PlaybackSession> {
|
||||||
val sessions:MutableList<PlaybackSession> = mutableListOf()
|
val sessions: MutableList<PlaybackSession> = mutableListOf()
|
||||||
Paper.book("playbackSession").allKeys.forEach { playbackSessionId ->
|
Paper.book("playbackSession").allKeys.forEach { playbackSessionId ->
|
||||||
Paper.book("playbackSession").read<PlaybackSession>(playbackSessionId)?.let {
|
Paper.book("playbackSession").read<PlaybackSession>(playbackSessionId)?.let {
|
||||||
sessions.add(it)
|
sessions.add(it)
|
||||||
|
|
|
@ -36,23 +36,37 @@ object MediaEventManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekEvent(playbackSession: PlaybackSession, syncResult: SyncResult?) {
|
fun seekEvent(playbackSession: PlaybackSession, syncResult: SyncResult?) {
|
||||||
Log.i(tag, "Seek Event for media \"${playbackSession.displayTitle}\", currentTime=${playbackSession.currentTime}")
|
Log.i(
|
||||||
|
tag,
|
||||||
|
"Seek Event for media \"${playbackSession.displayTitle}\", currentTime=${playbackSession.currentTime}"
|
||||||
|
)
|
||||||
addPlaybackEvent("Seek", playbackSession, syncResult)
|
addPlaybackEvent("Seek", playbackSession, syncResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun syncEvent(mediaProgress: MediaProgressWrapper, description: String) {
|
fun syncEvent(mediaProgress: MediaProgressWrapper, description: String) {
|
||||||
Log.i(tag, "Sync Event for media item id \"${mediaProgress.mediaItemId}\", currentTime=${mediaProgress.currentTime}")
|
Log.i(
|
||||||
|
tag,
|
||||||
|
"Sync Event for media item id \"${mediaProgress.mediaItemId}\", currentTime=${mediaProgress.currentTime}"
|
||||||
|
)
|
||||||
addSyncEvent("Sync", mediaProgress, description)
|
addSyncEvent("Sync", mediaProgress, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSyncEvent(eventName:String, mediaProgress:MediaProgressWrapper, description: String) {
|
private fun addSyncEvent(
|
||||||
|
eventName: String,
|
||||||
|
mediaProgress: MediaProgressWrapper,
|
||||||
|
description: String
|
||||||
|
) {
|
||||||
val mediaItemHistory = getMediaItemHistoryMediaItem(mediaProgress.mediaItemId)
|
val mediaItemHistory = getMediaItemHistoryMediaItem(mediaProgress.mediaItemId)
|
||||||
if (mediaItemHistory == null) {
|
if (mediaItemHistory == null) {
|
||||||
Log.w(tag, "addSyncEvent: Media Item History not created yet for media item id ${mediaProgress.mediaItemId}")
|
Log.w(
|
||||||
|
tag,
|
||||||
|
"addSyncEvent: Media Item History not created yet for media item id ${mediaProgress.mediaItemId}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val mediaItemEvent = MediaItemEvent(
|
val mediaItemEvent =
|
||||||
|
MediaItemEvent(
|
||||||
name = eventName,
|
name = eventName,
|
||||||
type = "Sync",
|
type = "Sync",
|
||||||
description = description,
|
description = description,
|
||||||
|
@ -68,10 +82,17 @@ object MediaEventManager {
|
||||||
clientEventEmitter?.onMediaItemHistoryUpdated(mediaItemHistory)
|
clientEventEmitter?.onMediaItemHistoryUpdated(mediaItemHistory)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addPlaybackEvent(eventName:String, playbackSession:PlaybackSession, syncResult: SyncResult?) {
|
private fun addPlaybackEvent(
|
||||||
val mediaItemHistory = getMediaItemHistoryMediaItem(playbackSession.mediaItemId) ?: createMediaItemHistoryForSession(playbackSession)
|
eventName: String,
|
||||||
|
playbackSession: PlaybackSession,
|
||||||
|
syncResult: SyncResult?
|
||||||
|
) {
|
||||||
|
val mediaItemHistory =
|
||||||
|
getMediaItemHistoryMediaItem(playbackSession.mediaItemId)
|
||||||
|
?: createMediaItemHistoryForSession(playbackSession)
|
||||||
|
|
||||||
val mediaItemEvent = MediaItemEvent(
|
val mediaItemEvent =
|
||||||
|
MediaItemEvent(
|
||||||
name = eventName,
|
name = eventName,
|
||||||
type = "Playback",
|
type = "Playback",
|
||||||
description = "",
|
description = "",
|
||||||
|
@ -87,15 +108,20 @@ object MediaEventManager {
|
||||||
clientEventEmitter?.onMediaItemHistoryUpdated(mediaItemHistory)
|
clientEventEmitter?.onMediaItemHistoryUpdated(mediaItemHistory)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMediaItemHistoryMediaItem(mediaItemId: String) : MediaItemHistory? {
|
private fun getMediaItemHistoryMediaItem(mediaItemId: String): MediaItemHistory? {
|
||||||
return DeviceManager.dbManager.getMediaItemHistory(mediaItemId)
|
return DeviceManager.dbManager.getMediaItemHistory(mediaItemId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMediaItemHistoryForSession(playbackSession: PlaybackSession):MediaItemHistory {
|
private fun createMediaItemHistoryForSession(playbackSession: PlaybackSession): MediaItemHistory {
|
||||||
Log.i(tag, "Creating new media item history for media \"${playbackSession.displayTitle}\"")
|
Log.i(tag, "Creating new media item history for media \"${playbackSession.displayTitle}\"")
|
||||||
val isLocalOnly = playbackSession.isLocalLibraryItemOnly
|
val isLocalOnly = playbackSession.isLocalLibraryItemOnly
|
||||||
val libraryItemId = if (isLocalOnly) playbackSession.localLibraryItemId else playbackSession.libraryItemId ?: ""
|
val libraryItemId =
|
||||||
val episodeId:String? = if (isLocalOnly && playbackSession.localEpisodeId != null) playbackSession.localEpisodeId else playbackSession.episodeId
|
if (isLocalOnly) playbackSession.localLibraryItemId
|
||||||
|
else playbackSession.libraryItemId ?: ""
|
||||||
|
val episodeId: String? =
|
||||||
|
if (isLocalOnly && playbackSession.localEpisodeId != null)
|
||||||
|
playbackSession.localEpisodeId
|
||||||
|
else playbackSession.episodeId
|
||||||
return MediaItemHistory(
|
return MediaItemHistory(
|
||||||
id = playbackSession.mediaItemId,
|
id = playbackSession.mediaItemId,
|
||||||
mediaDisplayTitle = playbackSession.displayTitle ?: "Unset",
|
mediaDisplayTitle = playbackSession.displayTitle ?: "Unset",
|
||||||
|
@ -106,6 +132,7 @@ object MediaEventManager {
|
||||||
playbackSession.serverAddress,
|
playbackSession.serverAddress,
|
||||||
playbackSession.userId,
|
playbackSession.userId,
|
||||||
createdAt = System.currentTimeMillis(),
|
createdAt = System.currentTimeMillis(),
|
||||||
events = mutableListOf())
|
events = mutableListOf()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,36 +13,43 @@ import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
data class MediaProgressSyncData(
|
data class MediaProgressSyncData(
|
||||||
var timeListened:Long, // seconds
|
var timeListened: Long, // seconds
|
||||||
var duration:Double, // seconds
|
var duration: Double, // seconds
|
||||||
var currentTime:Double // seconds
|
var currentTime: Double // seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SyncResult(
|
data class SyncResult(
|
||||||
var serverSyncAttempted:Boolean,
|
var serverSyncAttempted: Boolean,
|
||||||
var serverSyncSuccess:Boolean?,
|
var serverSyncSuccess: Boolean?,
|
||||||
var serverSyncMessage:String?
|
var serverSyncMessage: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
class MediaProgressSyncer(val playerNotificationService: PlayerNotificationService, private val apiHandler: ApiHandler) {
|
class MediaProgressSyncer(
|
||||||
|
val playerNotificationService: PlayerNotificationService,
|
||||||
|
private val apiHandler: ApiHandler
|
||||||
|
) {
|
||||||
private val tag = "MediaProgressSync"
|
private val tag = "MediaProgressSync"
|
||||||
private val METERED_CONNECTION_SYNC_INTERVAL = 60000
|
private val METERED_CONNECTION_SYNC_INTERVAL = 60000
|
||||||
|
|
||||||
private var listeningTimerTask: TimerTask? = null
|
private var listeningTimerTask: TimerTask? = null
|
||||||
var listeningTimerRunning:Boolean = false
|
var listeningTimerRunning: Boolean = false
|
||||||
|
|
||||||
private var lastSyncTime:Long = 0
|
private var lastSyncTime: Long = 0
|
||||||
private var failedSyncs:Int = 0
|
private var failedSyncs: Int = 0
|
||||||
|
|
||||||
var currentPlaybackSession: PlaybackSession? = null // copy of pb session currently syncing
|
var currentPlaybackSession: PlaybackSession? = null // copy of pb session currently syncing
|
||||||
var currentLocalMediaProgress: LocalMediaProgress? = null
|
var currentLocalMediaProgress: LocalMediaProgress? = null
|
||||||
|
|
||||||
private val currentDisplayTitle get() = currentPlaybackSession?.displayTitle ?: "Unset"
|
private val currentDisplayTitle
|
||||||
val currentIsLocal get() = currentPlaybackSession?.isLocal == true
|
get() = currentPlaybackSession?.displayTitle ?: "Unset"
|
||||||
val currentSessionId get() = currentPlaybackSession?.id ?: ""
|
val currentIsLocal
|
||||||
private val currentPlaybackDuration get() = currentPlaybackSession?.duration ?: 0.0
|
get() = currentPlaybackSession?.isLocal == true
|
||||||
|
val currentSessionId
|
||||||
|
get() = currentPlaybackSession?.id ?: ""
|
||||||
|
private val currentPlaybackDuration
|
||||||
|
get() = currentPlaybackSession?.duration ?: 0.0
|
||||||
|
|
||||||
fun start(playbackSession:PlaybackSession) {
|
fun start(playbackSession: PlaybackSession) {
|
||||||
if (listeningTimerRunning) {
|
if (listeningTimerRunning) {
|
||||||
Log.d(tag, "start: Timer already running for $currentDisplayTitle")
|
Log.d(tag, "start: Timer already running for $currentDisplayTitle")
|
||||||
if (playbackSession.id != currentSessionId) {
|
if (playbackSession.id != currentSessionId) {
|
||||||
|
@ -62,16 +69,24 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
listeningTimerRunning = true
|
listeningTimerRunning = true
|
||||||
lastSyncTime = System.currentTimeMillis()
|
lastSyncTime = System.currentTimeMillis()
|
||||||
currentPlaybackSession = playbackSession.clone()
|
currentPlaybackSession = playbackSession.clone()
|
||||||
Log.d(tag, "start: init last sync time $lastSyncTime with playback session id=${currentPlaybackSession?.id}")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"start: init last sync time $lastSyncTime with playback session id=${currentPlaybackSession?.id}"
|
||||||
|
)
|
||||||
|
|
||||||
listeningTimerTask = Timer("ListeningTimer", false).schedule(15000L, 15000L) {
|
listeningTimerTask =
|
||||||
|
Timer("ListeningTimer", false).schedule(15000L, 15000L) {
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
if (playerNotificationService.currentPlayer.isPlaying) {
|
if (playerNotificationService.currentPlayer.isPlaying) {
|
||||||
// Set auto sleep timer if enabled and within start/end time
|
// Set auto sleep timer if enabled and within start/end time
|
||||||
playerNotificationService.sleepTimerManager.checkAutoSleepTimer()
|
playerNotificationService.sleepTimerManager.checkAutoSleepTimer()
|
||||||
|
|
||||||
// Only sync with server on unmetered connection every 15s OR sync with server if last sync time is >= 60s
|
// Only sync with server on unmetered connection every 15s OR sync with server if
|
||||||
val shouldSyncServer = PlayerNotificationService.isUnmeteredNetwork || System.currentTimeMillis() - lastSyncTime >= METERED_CONNECTION_SYNC_INTERVAL
|
// last sync time is >= 60s
|
||||||
|
val shouldSyncServer =
|
||||||
|
PlayerNotificationService.isUnmeteredNetwork ||
|
||||||
|
System.currentTimeMillis() - lastSyncTime >=
|
||||||
|
METERED_CONNECTION_SYNC_INTERVAL
|
||||||
|
|
||||||
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||||
if (currentTime > 0) {
|
if (currentTime > 0) {
|
||||||
|
@ -88,14 +103,14 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(playbackSession:PlaybackSession) {
|
fun play(playbackSession: PlaybackSession) {
|
||||||
Log.d(tag, "play ${playbackSession.displayTitle}")
|
Log.d(tag, "play ${playbackSession.displayTitle}")
|
||||||
MediaEventManager.playEvent(playbackSession)
|
MediaEventManager.playEvent(playbackSession)
|
||||||
|
|
||||||
start(playbackSession)
|
start(playbackSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop(shouldSync:Boolean? = true, cb: () -> Unit) {
|
fun stop(shouldSync: Boolean? = true, cb: () -> Unit) {
|
||||||
if (!listeningTimerRunning) {
|
if (!listeningTimerRunning) {
|
||||||
reset()
|
reset()
|
||||||
return cb()
|
return cb()
|
||||||
|
@ -106,7 +121,8 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
listeningTimerRunning = false
|
listeningTimerRunning = false
|
||||||
Log.d(tag, "stop: Stopping listening for $currentDisplayTitle")
|
Log.d(tag, "stop: Stopping listening for $currentDisplayTitle")
|
||||||
|
|
||||||
val currentTime = if (shouldSync == true) playerNotificationService.getCurrentTimeSeconds() else 0.0
|
val currentTime =
|
||||||
|
if (shouldSync == true) playerNotificationService.getCurrentTimeSeconds() else 0.0
|
||||||
if (currentTime > 0) { // Current time should always be > 0 on stop
|
if (currentTime > 0) { // Current time should always be > 0 on stop
|
||||||
sync(true, currentTime) { syncResult ->
|
sync(true, currentTime) { syncResult ->
|
||||||
currentPlaybackSession?.let { playbackSession ->
|
currentPlaybackSession?.let { playbackSession ->
|
||||||
|
@ -197,12 +213,15 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
it.updatedAt = mediaProgress.lastUpdate
|
it.updatedAt = mediaProgress.lastUpdate
|
||||||
it.currentTime = mediaProgress.currentTime
|
it.currentTime = mediaProgress.currentTime
|
||||||
|
|
||||||
MediaEventManager.syncEvent(mediaProgress, "Received from server get media progress request while playback session open")
|
MediaEventManager.syncEvent(
|
||||||
|
mediaProgress,
|
||||||
|
"Received from server get media progress request while playback session open"
|
||||||
|
)
|
||||||
saveLocalProgress(it)
|
saveLocalProgress(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sync(shouldSyncServer:Boolean, currentTime:Double, cb: (SyncResult?) -> Unit) {
|
fun sync(shouldSyncServer: Boolean, currentTime: Double, cb: (SyncResult?) -> Unit) {
|
||||||
if (lastSyncTime <= 0) {
|
if (lastSyncTime <= 0) {
|
||||||
Log.e(tag, "Last sync time is not set $lastSyncTime")
|
Log.e(tag, "Last sync time is not set $lastSyncTime")
|
||||||
return cb(null)
|
return cb(null)
|
||||||
|
@ -214,11 +233,14 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
}
|
}
|
||||||
val listeningTimeToAdd = diffSinceLastSync / 1000L
|
val listeningTimeToAdd = diffSinceLastSync / 1000L
|
||||||
|
|
||||||
val syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime)
|
val syncData = MediaProgressSyncData(listeningTimeToAdd, currentPlaybackDuration, currentTime)
|
||||||
currentPlaybackSession?.syncData(syncData)
|
currentPlaybackSession?.syncData(syncData)
|
||||||
|
|
||||||
if (currentPlaybackSession?.progress?.isNaN() == true) {
|
if (currentPlaybackSession?.progress?.isNaN() == true) {
|
||||||
Log.e(tag, "Current Playback Session invalid progress ${currentPlaybackSession?.progress} | Current Time: ${currentPlaybackSession?.currentTime} | Duration: ${currentPlaybackSession?.getTotalDuration()}")
|
Log.e(
|
||||||
|
tag,
|
||||||
|
"Current Playback Session invalid progress ${currentPlaybackSession?.progress} | Current Time: ${currentPlaybackSession?.currentTime} | Duration: ${currentPlaybackSession?.getTotalDuration()}"
|
||||||
|
)
|
||||||
return cb(null)
|
return cb(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,11 +260,20 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
saveLocalProgress(it)
|
saveLocalProgress(it)
|
||||||
lastSyncTime = System.currentTimeMillis()
|
lastSyncTime = System.currentTimeMillis()
|
||||||
|
|
||||||
Log.d(tag, "Sync local device current serverConnectionConfigId=${DeviceManager.serverConnectionConfig?.id}")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"Sync local device current serverConnectionConfigId=${DeviceManager.serverConnectionConfig?.id}"
|
||||||
|
)
|
||||||
|
|
||||||
// Local library item is linked to a server library item
|
// Local library item is linked to a server library item
|
||||||
// Send sync to server also if connected to this server and local item belongs to this server
|
// Send sync to server also if connected to this server and local item belongs to this
|
||||||
if (hasNetworkConnection && shouldSyncServer && !it.libraryItemId.isNullOrEmpty() && it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
|
// server
|
||||||
|
if (hasNetworkConnection &&
|
||||||
|
shouldSyncServer &&
|
||||||
|
!it.libraryItemId.isNullOrEmpty() &&
|
||||||
|
it.serverConnectionConfigId != null &&
|
||||||
|
DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId
|
||||||
|
) {
|
||||||
apiHandler.sendLocalProgressSync(it) { syncSuccess, errorMsg ->
|
apiHandler.sendLocalProgressSync(it) { syncSuccess, errorMsg ->
|
||||||
if (syncSuccess) {
|
if (syncSuccess) {
|
||||||
failedSyncs = 0
|
failedSyncs = 0
|
||||||
|
@ -254,7 +285,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
playerNotificationService.alertSyncFailing() // Show alert in client
|
playerNotificationService.alertSyncFailing() // Show alert in client
|
||||||
failedSyncs = 0
|
failedSyncs = 0
|
||||||
}
|
}
|
||||||
Log.e(tag, "Local Progress sync failed ($failedSyncs) to send to server $currentDisplayTitle for time $currentTime with session id=${it.id}")
|
Log.e(
|
||||||
|
tag,
|
||||||
|
"Local Progress sync failed ($failedSyncs) to send to server $currentDisplayTitle for time $currentTime with session id=${it.id}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
cb(SyncResult(true, syncSuccess, errorMsg))
|
cb(SyncResult(true, syncSuccess, errorMsg))
|
||||||
|
@ -278,7 +312,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
playerNotificationService.alertSyncFailing() // Show alert in client
|
playerNotificationService.alertSyncFailing() // Show alert in client
|
||||||
failedSyncs = 0
|
failedSyncs = 0
|
||||||
}
|
}
|
||||||
Log.e(tag, "Progress sync failed ($failedSyncs) to send to server $currentDisplayTitle for time $currentTime with session id=${currentSessionId}")
|
Log.e(
|
||||||
|
tag,
|
||||||
|
"Progress sync failed ($failedSyncs) to send to server $currentDisplayTitle for time $currentTime with session id=${currentSessionId}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
cb(SyncResult(true, syncSuccess, errorMsg))
|
cb(SyncResult(true, syncSuccess, errorMsg))
|
||||||
}
|
}
|
||||||
|
@ -287,9 +324,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveLocalProgress(playbackSession:PlaybackSession) {
|
private fun saveLocalProgress(playbackSession: PlaybackSession) {
|
||||||
if (currentLocalMediaProgress == null) {
|
if (currentLocalMediaProgress == null) {
|
||||||
val mediaProgress = DeviceManager.dbManager.getLocalMediaProgress(playbackSession.localMediaProgressId)
|
val mediaProgress =
|
||||||
|
DeviceManager.dbManager.getLocalMediaProgress(playbackSession.localMediaProgressId)
|
||||||
if (mediaProgress == null) {
|
if (mediaProgress == null) {
|
||||||
currentLocalMediaProgress = playbackSession.getNewLocalMediaProgress()
|
currentLocalMediaProgress = playbackSession.getNewLocalMediaProgress()
|
||||||
} else {
|
} else {
|
||||||
|
@ -306,12 +344,14 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||||
} else {
|
} else {
|
||||||
DeviceManager.dbManager.saveLocalMediaProgress(it)
|
DeviceManager.dbManager.saveLocalMediaProgress(it)
|
||||||
playerNotificationService.clientEventEmitter?.onLocalMediaProgressUpdate(it)
|
playerNotificationService.clientEventEmitter?.onLocalMediaProgressUpdate(it)
|
||||||
Log.d(tag, "Saved Local Progress Current Time: ID ${it.id} | ${it.currentTime} | Duration ${it.duration} | Progress ${it.progressPercent}%")
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"Saved Local Progress Current Time: ID ${it.id} | ${it.currentTime} | Duration ${it.duration} | Progress ${it.progressPercent}%"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
currentPlaybackSession = null
|
currentPlaybackSession = null
|
||||||
currentLocalMediaProgress = null
|
currentLocalMediaProgress = null
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue