mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-09-09 11:21:47 +02:00
Autoformatting files
This commit is contained in:
@@ -12,6 +12,7 @@ import com.audiobookshelf.app.BuildConfig
|
||||
import com.audiobookshelf.app.R
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
import com.audiobookshelf.app.media.MediaProgressSyncData
|
||||
import com.audiobookshelf.app.player.*
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
@@ -19,7 +20,6 @@ import com.google.android.exoplayer2.MediaMetadata
|
||||
import com.google.android.gms.cast.MediaInfo
|
||||
import com.google.android.gms.cast.MediaQueueItem
|
||||
import com.google.android.gms.common.images.WebImage
|
||||
import com.audiobookshelf.app.player.*
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class PlaybackSession(
|
||||
@@ -50,27 +50,42 @@ class PlaybackSession(
|
||||
) {
|
||||
|
||||
@get:JsonIgnore
|
||||
val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE
|
||||
val isHLS
|
||||
get() = playMethod == PLAYMETHOD_TRANSCODE
|
||||
@get:JsonIgnore
|
||||
val isDirectPlay get() = playMethod == PLAYMETHOD_DIRECTPLAY
|
||||
val isDirectPlay
|
||||
get() = playMethod == PLAYMETHOD_DIRECTPLAY
|
||||
@get:JsonIgnore
|
||||
val isLocal get() = playMethod == PLAYMETHOD_LOCAL
|
||||
val isLocal
|
||||
get() = playMethod == PLAYMETHOD_LOCAL
|
||||
@get:JsonIgnore
|
||||
val isPodcastEpisode get() = mediaType == "podcast"
|
||||
val isPodcastEpisode
|
||||
get() = mediaType == "podcast"
|
||||
@get:JsonIgnore
|
||||
val currentTimeMs get() = (currentTime * 1000L).toLong()
|
||||
val currentTimeMs
|
||||
get() = (currentTime * 1000L).toLong()
|
||||
@get:JsonIgnore
|
||||
val totalDurationMs get() = (getTotalDuration() * 1000L).toLong()
|
||||
val totalDurationMs
|
||||
get() = (getTotalDuration() * 1000L).toLong()
|
||||
@get:JsonIgnore
|
||||
val localLibraryItemId get() = localLibraryItem?.id ?: ""
|
||||
val localLibraryItemId
|
||||
get() = localLibraryItem?.id ?: ""
|
||||
@get:JsonIgnore
|
||||
val localMediaProgressId get() = if (localEpisodeId.isNullOrEmpty()) localLibraryItemId else "$localLibraryItemId-$localEpisodeId"
|
||||
val localMediaProgressId
|
||||
get() =
|
||||
if (localEpisodeId.isNullOrEmpty()) localLibraryItemId
|
||||
else "$localLibraryItemId-$localEpisodeId"
|
||||
@get:JsonIgnore
|
||||
val progress get() = currentTime / getTotalDuration()
|
||||
val progress
|
||||
get() = currentTime / getTotalDuration()
|
||||
@get:JsonIgnore
|
||||
val isLocalLibraryItemOnly get() = localLibraryItemId != "" && libraryItemId == null
|
||||
val isLocalLibraryItemOnly
|
||||
get() = localLibraryItemId != "" && libraryItemId == null
|
||||
@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
|
||||
fun getCurrentTrackIndex(): Int {
|
||||
@@ -144,13 +159,20 @@ class PlaybackSession(
|
||||
if (localLibraryItem?.coverContentUrl != null) {
|
||||
var coverUri = Uri.parse(localLibraryItem?.coverContentUrl.toString())
|
||||
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}")
|
||||
}
|
||||
|
||||
@@ -164,7 +186,8 @@ class PlaybackSession(
|
||||
fun getMediaMetadataCompat(ctx: Context): MediaMetadataCompat {
|
||||
val coverUri = getCoverUri(ctx)
|
||||
|
||||
val metadataBuilder = MediaMetadataCompat.Builder()
|
||||
val metadataBuilder =
|
||||
MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, displayTitle)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor)
|
||||
@@ -176,14 +199,19 @@ class PlaybackSession(
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_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
|
||||
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)
|
||||
} else {
|
||||
val source: ImageDecoder.Source = ImageDecoder.createSource(ctx.contentResolver, coverUri)
|
||||
val source: ImageDecoder.Source =
|
||||
ImageDecoder.createSource(ctx.contentResolver, coverUri)
|
||||
ImageDecoder.decodeBitmap(source)
|
||||
}
|
||||
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
|
||||
@@ -197,7 +225,8 @@ class PlaybackSession(
|
||||
fun getExoMediaMetadata(ctx: Context): MediaMetadata {
|
||||
val coverUri = getCoverUri(ctx)
|
||||
|
||||
val metadataBuilder = MediaMetadata.Builder()
|
||||
val metadataBuilder =
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(displayTitle)
|
||||
.setDisplayTitle(displayTitle)
|
||||
.setArtist(displayAuthor)
|
||||
@@ -221,7 +250,13 @@ class PlaybackSession(
|
||||
val mimeType = audioTrack.mimeType
|
||||
|
||||
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)
|
||||
}
|
||||
return mediaItems
|
||||
@@ -229,18 +264,39 @@ class PlaybackSession(
|
||||
|
||||
@JsonIgnore
|
||||
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 {
|
||||
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_ARTIST, 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.putString(
|
||||
com.google.android.gms.cast.MediaMetadata.KEY_ARTIST,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -250,21 +306,49 @@ class PlaybackSession(
|
||||
|
||||
val mediaUri = getContentUri(audioTrack)
|
||||
|
||||
val mediaInfo = MediaInfo.Builder(mediaUri.toString()).apply {
|
||||
val mediaInfo =
|
||||
MediaInfo.Builder(mediaUri.toString())
|
||||
.apply {
|
||||
setContentUrl(mediaUri.toString())
|
||||
setContentType(audioTrack.mimeType)
|
||||
setMetadata(castMetadata)
|
||||
setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||
}.build()
|
||||
}
|
||||
.build()
|
||||
|
||||
return MediaQueueItem.Builder(mediaInfo).apply {
|
||||
setPlaybackDuration(audioTrack.duration)
|
||||
}.build()
|
||||
return MediaQueueItem.Builder(mediaInfo)
|
||||
.apply { setPlaybackDuration(audioTrack.duration) }
|
||||
.build()
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
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
|
||||
@@ -276,6 +360,24 @@ class PlaybackSession(
|
||||
|
||||
@JsonIgnore
|
||||
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,7 +22,8 @@ class DbManager {
|
||||
}
|
||||
|
||||
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) {
|
||||
Paper.book("device").write("data", deviceData)
|
||||
@@ -32,7 +33,9 @@ class DbManager {
|
||||
val localLibraryItems: MutableList<LocalLibraryItem> = mutableListOf()
|
||||
Paper.book("localLibraryItems").allKeys.forEach {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -41,9 +44,7 @@ class DbManager {
|
||||
|
||||
fun getLocalLibraryItemsInFolder(folderId: String): List<LocalLibraryItem> {
|
||||
val localLibraryItems = getLocalLibraryItems()
|
||||
return localLibraryItems.filter {
|
||||
it.folderId == folderId
|
||||
}
|
||||
return localLibraryItems.filter { it.folderId == folderId }
|
||||
}
|
||||
|
||||
fun getLocalLibraryItemByLId(libraryItemId: String): LocalLibraryItem? {
|
||||
@@ -56,7 +57,8 @@ class DbManager {
|
||||
|
||||
fun getLocalLibraryItemWithEpisode(podcastEpisodeId: String): LibraryItemWithEpisode? {
|
||||
var podcastEpisode: PodcastEpisode? = null
|
||||
val localLibraryItem = getLocalLibraryItems("podcast").find { localLibraryItem ->
|
||||
val localLibraryItem =
|
||||
getLocalLibraryItems("podcast").find { localLibraryItem ->
|
||||
val podcast = localLibraryItem.media as Podcast
|
||||
podcastEpisode = podcast.episodes?.find { it.id == podcastEpisodeId }
|
||||
podcastEpisode != null
|
||||
@@ -73,9 +75,7 @@ class DbManager {
|
||||
}
|
||||
|
||||
fun saveLocalLibraryItems(localLibraryItems: List<LocalLibraryItem>) {
|
||||
localLibraryItems.map {
|
||||
Paper.book("localLibraryItems").write(it.id, it)
|
||||
}
|
||||
localLibraryItems.map { Paper.book("localLibraryItems").write(it.id, it) }
|
||||
}
|
||||
|
||||
fun saveLocalLibraryItem(localLibraryItem: LocalLibraryItem) {
|
||||
@@ -93,18 +93,14 @@ class DbManager {
|
||||
fun getAllLocalFolders(): List<LocalFolder> {
|
||||
val localFolders: MutableList<LocalFolder> = mutableListOf()
|
||||
Paper.book("localFolders").allKeys.forEach { localFolderId ->
|
||||
Paper.book("localFolders").read<LocalFolder>(localFolderId)?.let {
|
||||
localFolders.add(it)
|
||||
}
|
||||
Paper.book("localFolders").read<LocalFolder>(localFolderId)?.let { localFolders.add(it) }
|
||||
}
|
||||
return localFolders
|
||||
}
|
||||
|
||||
fun removeLocalFolder(folderId: String) {
|
||||
val localLibraryItems = getLocalLibraryItemsInFolder(folderId)
|
||||
localLibraryItems.forEach {
|
||||
Paper.book("localLibraryItems").delete(it.id)
|
||||
}
|
||||
localLibraryItems.forEach { Paper.book("localLibraryItems").delete(it.id) }
|
||||
Paper.book("localFolders").delete(folderId)
|
||||
}
|
||||
|
||||
@@ -119,9 +115,7 @@ class DbManager {
|
||||
fun getDownloadItems(): List<DownloadItem> {
|
||||
val downloadItems: MutableList<DownloadItem> = mutableListOf()
|
||||
Paper.book("downloadItems").allKeys.forEach { downloadItemId ->
|
||||
Paper.book("downloadItems").read<DownloadItem>(downloadItemId)?.let {
|
||||
downloadItems.add(it)
|
||||
}
|
||||
Paper.book("downloadItems").read<DownloadItem>(downloadItemId)?.let { downloadItems.add(it) }
|
||||
}
|
||||
return downloadItems
|
||||
}
|
||||
@@ -129,7 +123,8 @@ class DbManager {
|
||||
fun saveLocalMediaProgress(mediaProgress: LocalMediaProgress) {
|
||||
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
|
||||
// "{localLibraryItemId}-{episodeId}"
|
||||
fun getLocalMediaProgress(localMediaProgressId: String): LocalMediaProgress? {
|
||||
return Paper.book("localMediaProgress").read(localMediaProgressId)
|
||||
}
|
||||
@@ -158,35 +153,50 @@ class DbManager {
|
||||
var hasUpdates = false
|
||||
|
||||
// Check local files
|
||||
lli.localFiles = lli.localFiles.filter { localFile ->
|
||||
|
||||
lli.localFiles =
|
||||
lli.localFiles.filter { localFile ->
|
||||
val file = File(localFile.absolutePath)
|
||||
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
|
||||
}
|
||||
file.exists()
|
||||
} as MutableList<LocalFile>
|
||||
} as
|
||||
MutableList<LocalFile>
|
||||
|
||||
// Check audio tracks and episodes
|
||||
if (lli.isPodcast) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
ep.audioTrack != null && lli.localFiles.find { lf -> lf.id == ep.audioTrack?.localFileId } != null
|
||||
} as MutableList<PodcastEpisode>
|
||||
ep.audioTrack != null &&
|
||||
lli.localFiles.find { lf -> lf.id == ep.audioTrack?.localFileId } != null
|
||||
} as
|
||||
MutableList<PodcastEpisode>
|
||||
} else {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
lli.localFiles.find { lf -> lf.id == track.localFileId } != null
|
||||
} as MutableList<AudioTrack>
|
||||
} as
|
||||
MutableList<AudioTrack>
|
||||
}
|
||||
|
||||
// Check cover still there
|
||||
@@ -194,7 +204,10 @@ class DbManager {
|
||||
val coverFile = File(it)
|
||||
|
||||
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.coverContentUrl = null
|
||||
hasUpdates = true
|
||||
@@ -215,11 +228,18 @@ class DbManager {
|
||||
localMediaProgress.forEach {
|
||||
val matchingLLI = localLibraryItems.find { lli -> lli.id == it.localLibraryItemId }
|
||||
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.
|
||||
Log.d(tag, "cleanLocalMediaProgress: Invalid local media progress does not start with 'local' (fixed on server 2.0.24)")
|
||||
// A bug on the server when syncing local media progress was replacing the media progress id
|
||||
// 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)
|
||||
} 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)
|
||||
} else if (matchingLLI.isPodcast) {
|
||||
if (it.localEpisodeId.isNullOrEmpty()) {
|
||||
@@ -229,7 +249,10 @@ class DbManager {
|
||||
val podcast = matchingLLI.media as Podcast
|
||||
val matchingLEp = podcast.episodes?.find { ep -> ep.id == it.localEpisodeId }
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,23 +36,37 @@ object MediaEventManager {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
private fun addSyncEvent(eventName:String, mediaProgress:MediaProgressWrapper, description: String) {
|
||||
private fun addSyncEvent(
|
||||
eventName: String,
|
||||
mediaProgress: MediaProgressWrapper,
|
||||
description: String
|
||||
) {
|
||||
val mediaItemHistory = getMediaItemHistoryMediaItem(mediaProgress.mediaItemId)
|
||||
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
|
||||
}
|
||||
|
||||
val mediaItemEvent = MediaItemEvent(
|
||||
val mediaItemEvent =
|
||||
MediaItemEvent(
|
||||
name = eventName,
|
||||
type = "Sync",
|
||||
description = description,
|
||||
@@ -68,10 +82,17 @@ object MediaEventManager {
|
||||
clientEventEmitter?.onMediaItemHistoryUpdated(mediaItemHistory)
|
||||
}
|
||||
|
||||
private fun addPlaybackEvent(eventName:String, playbackSession:PlaybackSession, syncResult: SyncResult?) {
|
||||
val mediaItemHistory = getMediaItemHistoryMediaItem(playbackSession.mediaItemId) ?: createMediaItemHistoryForSession(playbackSession)
|
||||
private fun addPlaybackEvent(
|
||||
eventName: String,
|
||||
playbackSession: PlaybackSession,
|
||||
syncResult: SyncResult?
|
||||
) {
|
||||
val mediaItemHistory =
|
||||
getMediaItemHistoryMediaItem(playbackSession.mediaItemId)
|
||||
?: createMediaItemHistoryForSession(playbackSession)
|
||||
|
||||
val mediaItemEvent = MediaItemEvent(
|
||||
val mediaItemEvent =
|
||||
MediaItemEvent(
|
||||
name = eventName,
|
||||
type = "Playback",
|
||||
description = "",
|
||||
@@ -94,8 +115,13 @@ object MediaEventManager {
|
||||
private fun createMediaItemHistoryForSession(playbackSession: PlaybackSession): MediaItemHistory {
|
||||
Log.i(tag, "Creating new media item history for media \"${playbackSession.displayTitle}\"")
|
||||
val isLocalOnly = playbackSession.isLocalLibraryItemOnly
|
||||
val libraryItemId = if (isLocalOnly) playbackSession.localLibraryItemId else playbackSession.libraryItemId ?: ""
|
||||
val episodeId:String? = if (isLocalOnly && playbackSession.localEpisodeId != null) playbackSession.localEpisodeId else playbackSession.episodeId
|
||||
val libraryItemId =
|
||||
if (isLocalOnly) playbackSession.localLibraryItemId
|
||||
else playbackSession.libraryItemId ?: ""
|
||||
val episodeId: String? =
|
||||
if (isLocalOnly && playbackSession.localEpisodeId != null)
|
||||
playbackSession.localEpisodeId
|
||||
else playbackSession.episodeId
|
||||
return MediaItemHistory(
|
||||
id = playbackSession.mediaItemId,
|
||||
mediaDisplayTitle = playbackSession.displayTitle ?: "Unset",
|
||||
@@ -106,6 +132,7 @@ object MediaEventManager {
|
||||
playbackSession.serverAddress,
|
||||
playbackSession.userId,
|
||||
createdAt = System.currentTimeMillis(),
|
||||
events = mutableListOf())
|
||||
events = mutableListOf()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ data class SyncResult(
|
||||
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 METERED_CONNECTION_SYNC_INTERVAL = 60000
|
||||
|
||||
@@ -37,10 +40,14 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
var currentPlaybackSession: PlaybackSession? = null // copy of pb session currently syncing
|
||||
var currentLocalMediaProgress: LocalMediaProgress? = null
|
||||
|
||||
private val currentDisplayTitle get() = currentPlaybackSession?.displayTitle ?: "Unset"
|
||||
val currentIsLocal get() = currentPlaybackSession?.isLocal == true
|
||||
val currentSessionId get() = currentPlaybackSession?.id ?: ""
|
||||
private val currentPlaybackDuration get() = currentPlaybackSession?.duration ?: 0.0
|
||||
private val currentDisplayTitle
|
||||
get() = currentPlaybackSession?.displayTitle ?: "Unset"
|
||||
val currentIsLocal
|
||||
get() = currentPlaybackSession?.isLocal == true
|
||||
val currentSessionId
|
||||
get() = currentPlaybackSession?.id ?: ""
|
||||
private val currentPlaybackDuration
|
||||
get() = currentPlaybackSession?.duration ?: 0.0
|
||||
|
||||
fun start(playbackSession: PlaybackSession) {
|
||||
if (listeningTimerRunning) {
|
||||
@@ -62,16 +69,24 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
listeningTimerRunning = true
|
||||
lastSyncTime = System.currentTimeMillis()
|
||||
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() {
|
||||
if (playerNotificationService.currentPlayer.isPlaying) {
|
||||
// Set auto sleep timer if enabled and within start/end time
|
||||
playerNotificationService.sleepTimerManager.checkAutoSleepTimer()
|
||||
|
||||
// Only sync with server on unmetered connection every 15s OR sync with server if last sync time is >= 60s
|
||||
val shouldSyncServer = PlayerNotificationService.isUnmeteredNetwork || System.currentTimeMillis() - lastSyncTime >= METERED_CONNECTION_SYNC_INTERVAL
|
||||
// Only sync with server on unmetered connection every 15s OR sync with server if
|
||||
// last sync time is >= 60s
|
||||
val shouldSyncServer =
|
||||
PlayerNotificationService.isUnmeteredNetwork ||
|
||||
System.currentTimeMillis() - lastSyncTime >=
|
||||
METERED_CONNECTION_SYNC_INTERVAL
|
||||
|
||||
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||
if (currentTime > 0) {
|
||||
@@ -106,7 +121,8 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
listeningTimerRunning = false
|
||||
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
|
||||
sync(true, currentTime) { syncResult ->
|
||||
currentPlaybackSession?.let { playbackSession ->
|
||||
@@ -197,7 +213,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
it.updatedAt = mediaProgress.lastUpdate
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -218,7 +237,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
currentPlaybackSession?.syncData(syncData)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -238,11 +260,20 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
saveLocalProgress(it)
|
||||
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
|
||||
// Send sync to server also if connected to this server and local item belongs to this server
|
||||
if (hasNetworkConnection && shouldSyncServer && !it.libraryItemId.isNullOrEmpty() && it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
|
||||
// Send sync to server also if connected to this server and local item belongs to this
|
||||
// server
|
||||
if (hasNetworkConnection &&
|
||||
shouldSyncServer &&
|
||||
!it.libraryItemId.isNullOrEmpty() &&
|
||||
it.serverConnectionConfigId != null &&
|
||||
DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId
|
||||
) {
|
||||
apiHandler.sendLocalProgressSync(it) { syncSuccess, errorMsg ->
|
||||
if (syncSuccess) {
|
||||
failedSyncs = 0
|
||||
@@ -254,7 +285,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
playerNotificationService.alertSyncFailing() // Show alert in client
|
||||
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))
|
||||
@@ -278,7 +312,10 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
playerNotificationService.alertSyncFailing() // Show alert in client
|
||||
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))
|
||||
}
|
||||
@@ -289,7 +326,8 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
|
||||
private fun saveLocalProgress(playbackSession: PlaybackSession) {
|
||||
if (currentLocalMediaProgress == null) {
|
||||
val mediaProgress = DeviceManager.dbManager.getLocalMediaProgress(playbackSession.localMediaProgressId)
|
||||
val mediaProgress =
|
||||
DeviceManager.dbManager.getLocalMediaProgress(playbackSession.localMediaProgressId)
|
||||
if (mediaProgress == null) {
|
||||
currentLocalMediaProgress = playbackSession.getNewLocalMediaProgress()
|
||||
} else {
|
||||
@@ -306,12 +344,14 @@ class MediaProgressSyncer(val playerNotificationService: PlayerNotificationServi
|
||||
} else {
|
||||
DeviceManager.dbManager.saveLocalMediaProgress(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() {
|
||||
currentPlaybackSession = null
|
||||
currentLocalMediaProgress = null
|
||||
|
||||
+532
-206
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user