mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-09 13:28:43 +02:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c678836fb | ||
|
|
fd134097a1 | ||
|
|
2a87f1de28 |
@@ -236,32 +236,37 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
serverConfigIdUsed = DeviceManager.serverConnectionConfigId
|
serverConfigIdUsed = DeviceManager.serverConnectionConfigId
|
||||||
|
|
||||||
loadLibraries { libraries ->
|
loadLibraries { libraries ->
|
||||||
val library = libraries[0]
|
if (libraries.isEmpty()) {
|
||||||
Log.d(tag, "Loading categories for library ${library.name} - ${library.id} - ${library.mediaType}")
|
Log.w(tag, "No libraries returned from server request")
|
||||||
|
cb(cats) // Return download category only
|
||||||
|
} else {
|
||||||
|
val library = libraries[0]
|
||||||
|
Log.d(tag, "Loading categories for library ${library.name} - ${library.id} - ${library.mediaType}")
|
||||||
|
|
||||||
loadLibraryCategories(library.id) { libraryCategories ->
|
loadLibraryCategories(library.id) { libraryCategories ->
|
||||||
|
|
||||||
// Only using book or podcast library categories for now
|
// Only using book or podcast library categories for now
|
||||||
libraryCategories.forEach {
|
libraryCategories.forEach {
|
||||||
|
|
||||||
// Add items in continue listening to serverLibraryItems
|
// Add items in continue listening to serverLibraryItems
|
||||||
if (it.id == "continue-listening") {
|
if (it.id == "continue-listening") {
|
||||||
it.entities.forEach { libraryItemWrapper ->
|
it.entities.forEach { libraryItemWrapper ->
|
||||||
val libraryItem = libraryItemWrapper as LibraryItem
|
val libraryItem = libraryItemWrapper as LibraryItem
|
||||||
if (serverLibraryItems.find { li -> li.id == libraryItem.id } == null) {
|
if (serverLibraryItems.find { li -> li.id == libraryItem.id } == null) {
|
||||||
serverLibraryItems.add(libraryItem)
|
serverLibraryItems.add(libraryItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log.d(tag, "Found library category ${it.label} with type ${it.type}")
|
||||||
|
if (it.type == library.mediaType) {
|
||||||
|
// Log.d(tag, "Using library category ${it.id}")
|
||||||
|
cats.add(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log.d(tag, "Found library category ${it.label} with type ${it.type}")
|
cb(cats)
|
||||||
if (it.type == library.mediaType) {
|
|
||||||
// Log.d(tag, "Using library category ${it.id}")
|
|
||||||
cats.add(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cb(cats)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // Not connected/no internet sent downloaded cats only
|
} else { // Not connected/no internet sent downloaded cats only
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ data class MediaProgressSyncData(
|
|||||||
|
|
||||||
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 var listeningTimerTask: TimerTask? = null
|
private var listeningTimerTask: TimerTask? = null
|
||||||
var listeningTimerRunning:Boolean = false
|
var listeningTimerRunning:Boolean = false
|
||||||
@@ -46,7 +47,10 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if (playerNotificationService.getCurrentPlaybackSessionId() != currentSessionId) {
|
||||||
|
currentLocalMediaProgress = null
|
||||||
}
|
}
|
||||||
|
|
||||||
listeningTimerRunning = true
|
listeningTimerRunning = true
|
||||||
lastSyncTime = System.currentTimeMillis()
|
lastSyncTime = System.currentTimeMillis()
|
||||||
currentPlaybackSession = playerNotificationService.getCurrentPlaybackSessionCopy()
|
currentPlaybackSession = playerNotificationService.getCurrentPlaybackSessionCopy()
|
||||||
@@ -54,8 +58,11 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
listeningTimerTask = Timer("ListeningTimer", false).schedule(0L, 5000L) {
|
listeningTimerTask = Timer("ListeningTimer", false).schedule(0L, 5000L) {
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
if (playerNotificationService.currentPlayer.isPlaying) {
|
if (playerNotificationService.currentPlayer.isPlaying) {
|
||||||
|
// Only sync with server on unmetered connection every 5s 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()
|
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||||
sync(currentTime) {
|
sync(shouldSyncServer, currentTime) {
|
||||||
Log.d(tag, "Sync complete")
|
Log.d(tag, "Sync complete")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,13 +70,30 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop(cb: () -> Unit) {
|
||||||
if (!listeningTimerRunning) return
|
if (!listeningTimerRunning) return
|
||||||
Log.d(tag, "stop: Stopping listening for $currentDisplayTitle")
|
Log.d(tag, "stop: Stopping listening for $currentDisplayTitle")
|
||||||
|
|
||||||
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||||
sync(currentTime) {
|
sync(true, currentTime) {
|
||||||
reset()
|
reset()
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pause(cb: () -> Unit) {
|
||||||
|
if (!listeningTimerRunning) return
|
||||||
|
Log.d(tag, "pause: Pausing progress syncer for $currentDisplayTitle")
|
||||||
|
|
||||||
|
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||||
|
sync(true, currentTime) {
|
||||||
|
listeningTimerTask?.cancel()
|
||||||
|
listeningTimerTask = null
|
||||||
|
listeningTimerRunning = false
|
||||||
|
lastSyncTime = 0L
|
||||||
|
failedSyncs = 0
|
||||||
|
|
||||||
|
cb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,19 +101,17 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
currentPlaybackSession?.let {
|
currentPlaybackSession?.let {
|
||||||
it.updatedAt = mediaProgress.lastUpdate
|
it.updatedAt = mediaProgress.lastUpdate
|
||||||
it.currentTime = mediaProgress.currentTime
|
it.currentTime = mediaProgress.currentTime
|
||||||
|
|
||||||
DeviceManager.dbManager.saveLocalPlaybackSession(it)
|
DeviceManager.dbManager.saveLocalPlaybackSession(it)
|
||||||
saveLocalProgress(it)
|
saveLocalProgress(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sync(currentTime:Double, cb: () -> Unit) {
|
fun sync(shouldSyncServer:Boolean, currentTime:Double, cb: () -> Unit) {
|
||||||
val diffSinceLastSync = System.currentTimeMillis() - lastSyncTime
|
val diffSinceLastSync = System.currentTimeMillis() - lastSyncTime
|
||||||
if (diffSinceLastSync < 1000L) {
|
if (diffSinceLastSync < 1000L) {
|
||||||
return cb()
|
return cb()
|
||||||
}
|
}
|
||||||
val listeningTimeToAdd = diffSinceLastSync / 1000L
|
val listeningTimeToAdd = diffSinceLastSync / 1000L
|
||||||
lastSyncTime = System.currentTimeMillis()
|
|
||||||
|
|
||||||
val syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime)
|
val syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime)
|
||||||
|
|
||||||
@@ -105,10 +127,11 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
currentPlaybackSession?.let {
|
currentPlaybackSession?.let {
|
||||||
DeviceManager.dbManager.saveLocalPlaybackSession(it)
|
DeviceManager.dbManager.saveLocalPlaybackSession(it)
|
||||||
saveLocalProgress(it)
|
saveLocalProgress(it)
|
||||||
|
lastSyncTime = System.currentTimeMillis()
|
||||||
|
|
||||||
// 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 server
|
||||||
if (!it.libraryItemId.isNullOrEmpty() && it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
|
if (shouldSyncServer && !it.libraryItemId.isNullOrEmpty() && it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
|
||||||
apiHandler.sendLocalProgressSync(it) { syncSuccess ->
|
apiHandler.sendLocalProgressSync(it) { syncSuccess ->
|
||||||
Log.d(
|
Log.d(
|
||||||
tag,
|
tag,
|
||||||
@@ -132,12 +155,13 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (shouldSyncServer) {
|
||||||
apiHandler.sendProgressSync(currentSessionId, syncData) {
|
apiHandler.sendProgressSync(currentSessionId, syncData) {
|
||||||
if (it) {
|
if (it) {
|
||||||
Log.d(tag, "Progress sync data sent to server $currentDisplayTitle for time $currentTime")
|
Log.d(tag, "Progress sync data sent to server $currentDisplayTitle for time $currentTime")
|
||||||
failedSyncs = 0
|
failedSyncs = 0
|
||||||
playerNotificationService.alertSyncSuccess()
|
playerNotificationService.alertSyncSuccess()
|
||||||
|
lastSyncTime = System.currentTimeMillis()
|
||||||
} else {
|
} else {
|
||||||
failedSyncs++
|
failedSyncs++
|
||||||
if (failedSyncs == 2) {
|
if (failedSyncs == 2) {
|
||||||
@@ -148,6 +172,8 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
|
|||||||
}
|
}
|
||||||
cb()
|
cb()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
cb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,32 +71,36 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
|
|||||||
if (player.isPlaying) {
|
if (player.isPlaying) {
|
||||||
Log.d(tag, "SeekBackTime: Player is playing")
|
Log.d(tag, "SeekBackTime: Player is playing")
|
||||||
if (lastPauseTime > 0 && DeviceManager.deviceData.deviceSettings?.disableAutoRewind != true) {
|
if (lastPauseTime > 0 && DeviceManager.deviceData.deviceSettings?.disableAutoRewind != true) {
|
||||||
|
var seekBackTime = 0L
|
||||||
if (onSeekBack) onSeekBack = false
|
if (onSeekBack) onSeekBack = false
|
||||||
else {
|
else {
|
||||||
Log.d(tag, "SeekBackTime: playing started now set seek back time $lastPauseTime")
|
Log.d(tag, "SeekBackTime: playing started now set seek back time $lastPauseTime")
|
||||||
var backTime = calcPauseSeekBackTime()
|
seekBackTime = calcPauseSeekBackTime()
|
||||||
if (backTime > 0) {
|
if (seekBackTime > 0) {
|
||||||
// Current chapter is used so that seek back does not go back to the previous chapter
|
// Current chapter is used so that seek back does not go back to the previous chapter
|
||||||
val currentChapter = playerNotificationService.getCurrentBookChapter()
|
val currentChapter = playerNotificationService.getCurrentBookChapter()
|
||||||
val minSeekBackTime = currentChapter?.startMs ?: 0
|
val minSeekBackTime = currentChapter?.startMs ?: 0
|
||||||
|
|
||||||
val currentTime = playerNotificationService.getCurrentTime()
|
val currentTime = playerNotificationService.getCurrentTime()
|
||||||
val newTime = currentTime - backTime
|
val newTime = currentTime - seekBackTime
|
||||||
if (newTime < minSeekBackTime) {
|
if (newTime < minSeekBackTime) {
|
||||||
backTime = currentTime - minSeekBackTime
|
seekBackTime = currentTime - minSeekBackTime
|
||||||
}
|
}
|
||||||
Log.d(tag, "SeekBackTime $backTime")
|
Log.d(tag, "SeekBackTime $seekBackTime")
|
||||||
onSeekBack = true
|
onSeekBack = true
|
||||||
playerNotificationService.seekBackward(backTime)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if playback session still exists or sync media progress if updated
|
// Check if playback session still exists or sync media progress if updated
|
||||||
val pauseLength: Long = System.currentTimeMillis() - lastPauseTime
|
val pauseLength: Long = System.currentTimeMillis() - lastPauseTime
|
||||||
if (pauseLength > PAUSE_LEN_BEFORE_RECHECK) {
|
if (pauseLength > PAUSE_LEN_BEFORE_RECHECK) {
|
||||||
val shouldCarryOn = playerNotificationService.checkCurrentSessionProgress()
|
val shouldCarryOn = playerNotificationService.checkCurrentSessionProgress(seekBackTime)
|
||||||
if (!shouldCarryOn) return
|
if (!shouldCarryOn) return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seekBackTime > 0L) {
|
||||||
|
playerNotificationService.seekBackward(seekBackTime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(tag, "SeekBackTime: Player not playing set last pause time")
|
Log.d(tag, "SeekBackTime: Player not playing set last pause time")
|
||||||
@@ -104,12 +108,13 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start/stop progress sync interval
|
// Start/stop progress sync interval
|
||||||
Log.d(tag, "Playing ${playerNotificationService.getCurrentBookTitle()}")
|
|
||||||
if (player.isPlaying) {
|
if (player.isPlaying) {
|
||||||
player.volume = 1F // Volume on sleep timer might have decreased this
|
player.volume = 1F // Volume on sleep timer might have decreased this
|
||||||
playerNotificationService.mediaProgressSyncer.start()
|
playerNotificationService.mediaProgressSyncer.start()
|
||||||
} else {
|
} else {
|
||||||
playerNotificationService.mediaProgressSyncer.stop()
|
playerNotificationService.mediaProgressSyncer.pause {
|
||||||
|
Log.d(tag, "Media Progress Syncer paused and synced")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playerNotificationService.clientEventEmitter?.onPlayingUpdate(player.isPlaying)
|
playerNotificationService.clientEventEmitter?.onPlayingUpdate(player.isPlaying)
|
||||||
|
|||||||
+57
-10
@@ -6,6 +6,10 @@ import android.content.Intent
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.hardware.Sensor
|
import android.hardware.Sensor
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import android.net.NetworkRequest
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.support.v4.media.MediaBrowserCompat
|
import android.support.v4.media.MediaBrowserCompat
|
||||||
import android.support.v4.media.MediaDescriptionCompat
|
import android.support.v4.media.MediaDescriptionCompat
|
||||||
@@ -45,6 +49,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
companion object {
|
companion object {
|
||||||
var isStarted = false
|
var isStarted = false
|
||||||
var isClosed = false
|
var isClosed = false
|
||||||
|
var isUnmeteredNetwork = false
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientEventEmitter {
|
interface ClientEventEmitter {
|
||||||
@@ -59,6 +64,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
fun onMediaPlayerChanged(mediaPlayer:String)
|
fun onMediaPlayerChanged(mediaPlayer:String)
|
||||||
fun onProgressSyncFailing()
|
fun onProgressSyncFailing()
|
||||||
fun onProgressSyncSuccess()
|
fun onProgressSyncSuccess()
|
||||||
|
fun onNetworkMeteredChanged(isUnmetered:Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tag = "PlayerService"
|
private val tag = "PlayerService"
|
||||||
@@ -164,6 +170,15 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
super.onCreate()
|
super.onCreate()
|
||||||
ctx = this
|
ctx = this
|
||||||
|
|
||||||
|
// To listen for network change from metered to unmetered
|
||||||
|
val networkRequest = NetworkRequest.Builder()
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||||
|
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||||
|
.build()
|
||||||
|
val connectivityManager = getSystemService(ConnectivityManager::class.java) as ConnectivityManager
|
||||||
|
connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
|
||||||
|
|
||||||
DbManager.initialize(ctx)
|
DbManager.initialize(ctx)
|
||||||
|
|
||||||
// Initialize API
|
// Initialize API
|
||||||
@@ -550,10 +565,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
|
|
||||||
// Called from PlayerListener play event
|
// Called from PlayerListener play event
|
||||||
// check with server if progress has updated since last play and sync progress update
|
// check with server if progress has updated since last play and sync progress update
|
||||||
fun checkCurrentSessionProgress():Boolean {
|
fun checkCurrentSessionProgress(seekBackTime:Long):Boolean {
|
||||||
if (currentPlaybackSession == null) return true
|
if (currentPlaybackSession == null) return true
|
||||||
|
|
||||||
currentPlaybackSession?.let { playbackSession ->
|
mediaProgressSyncer.currentPlaybackSession?.let { playbackSession ->
|
||||||
if (!apiHandler.isOnline() || playbackSession.isLocalLibraryItemOnly) {
|
if (!apiHandler.isOnline() || playbackSession.isLocalLibraryItemOnly) {
|
||||||
return true // carry on
|
return true // carry on
|
||||||
}
|
}
|
||||||
@@ -575,16 +590,28 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
Log.d(tag, "checkCurrentSessionProgress: Media progress was updated since last play time updating from ${playbackSession.currentTime} to ${mediaProgress.currentTime}")
|
Log.d(tag, "checkCurrentSessionProgress: Media progress was updated since last play time updating from ${playbackSession.currentTime} to ${mediaProgress.currentTime}")
|
||||||
mediaProgressSyncer.syncFromServerProgress(mediaProgress)
|
mediaProgressSyncer.syncFromServerProgress(mediaProgress)
|
||||||
|
|
||||||
|
// Update current playback session stored in PNS since MediaProgressSyncer version is a copy
|
||||||
|
mediaProgressSyncer.currentPlaybackSession?.let { updatedPlaybackSession ->
|
||||||
|
currentPlaybackSession = updatedPlaybackSession
|
||||||
|
}
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
seekPlayer(playbackSession.currentTimeMs)
|
seekPlayer(playbackSession.currentTimeMs)
|
||||||
|
// Should already be playing
|
||||||
|
currentPlayer.volume = 1F // Volume on sleep timer might have decreased this
|
||||||
|
mediaProgressSyncer.start()
|
||||||
|
clientEventEmitter?.onPlayingUpdate(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
if (seekBackTime > 0L) {
|
||||||
|
seekBackward(seekBackTime)
|
||||||
|
}
|
||||||
|
// Should already be playing
|
||||||
|
currentPlayer.volume = 1F // Volume on sleep timer might have decreased this
|
||||||
|
mediaProgressSyncer.start()
|
||||||
|
clientEventEmitter?.onPlayingUpdate(true)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
// Should already be playing
|
|
||||||
currentPlayer.volume = 1F // Volume on sleep timer might have decreased this
|
|
||||||
mediaProgressSyncer.start()
|
|
||||||
clientEventEmitter?.onPlayingUpdate(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -607,6 +634,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
} else {
|
} else {
|
||||||
Log.d(tag, "checkCurrentSessionProgress: Playback session still available on server")
|
Log.d(tag, "checkCurrentSessionProgress: Playback session still available on server")
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
if (seekBackTime > 0L) {
|
||||||
|
seekBackward(seekBackTime)
|
||||||
|
}
|
||||||
|
|
||||||
currentPlayer.volume = 1F // Volume on sleep timer might have decreased this
|
currentPlayer.volume = 1F // Volume on sleep timer might have decreased this
|
||||||
mediaProgressSyncer.start()
|
mediaProgressSyncer.start()
|
||||||
clientEventEmitter?.onPlayingUpdate(true)
|
clientEventEmitter?.onPlayingUpdate(true)
|
||||||
@@ -673,7 +704,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
Log.d(tag, "closePlayback")
|
Log.d(tag, "closePlayback")
|
||||||
if (mediaProgressSyncer.listeningTimerRunning) {
|
if (mediaProgressSyncer.listeningTimerRunning) {
|
||||||
Log.i(tag, "About to close playback so stopping media progress syncer first")
|
Log.i(tag, "About to close playback so stopping media progress syncer first")
|
||||||
mediaProgressSyncer.stop()
|
mediaProgressSyncer.stop {
|
||||||
|
Log.d(tag, "Media Progress syncer stopped and synced")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -896,5 +929,19 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||||
|
// Network capabilities have changed for the network
|
||||||
|
override fun onCapabilitiesChanged(
|
||||||
|
network: Network,
|
||||||
|
networkCapabilities: NetworkCapabilities
|
||||||
|
) {
|
||||||
|
super.onCapabilitiesChanged(network, networkCapabilities)
|
||||||
|
val unmetered = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
|
||||||
|
Log.i(tag, "Network capabilities changed is unmetered = $unmetered")
|
||||||
|
isUnmeteredNetwork = unmetered
|
||||||
|
clientEventEmitter?.onNetworkMeteredChanged(unmetered)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ class AbsAudioPlayer : Plugin() {
|
|||||||
override fun onProgressSyncSuccess() {
|
override fun onProgressSyncSuccess() {
|
||||||
emit("onProgressSyncSuccess", "")
|
emit("onProgressSyncSuccess", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNetworkMeteredChanged(isUnmetered:Boolean) {
|
||||||
|
emit("onNetworkMeteredChanged", isUnmetered)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
mainActivity.pluginCallback = foregroundServiceReady
|
mainActivity.pluginCallback = foregroundServiceReady
|
||||||
@@ -177,7 +181,21 @@ class AbsAudioPlayer : Plugin() {
|
|||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}")
|
Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}")
|
||||||
val playbackSession = it.getPlaybackSession(episode)
|
val playbackSession = it.getPlaybackSession(episode)
|
||||||
playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate)
|
|
||||||
|
if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next
|
||||||
|
playerNotificationService.mediaProgressSyncer.stop {
|
||||||
|
Log.d(tag, "Media progress syncer was already syncing - stopped")
|
||||||
|
Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw
|
||||||
|
playerNotificationService.preparePlayer(
|
||||||
|
playbackSession,
|
||||||
|
playWhenReady,
|
||||||
|
playbackRate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return call.resolve(JSObject())
|
return call.resolve(JSObject())
|
||||||
}
|
}
|
||||||
@@ -188,9 +206,20 @@ class AbsAudioPlayer : Plugin() {
|
|||||||
if (it == null) {
|
if (it == null) {
|
||||||
call.resolve(JSObject("{\"error\":\"Server play request failed\"}"))
|
call.resolve(JSObject("{\"error\":\"Server play request failed\"}"))
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
Log.d(tag, "Preparing Player TEST ${jacksonMapper.writeValueAsString(it)}")
|
Log.d(tag, "Preparing Player playback session ${jacksonMapper.writeValueAsString(it)}")
|
||||||
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate)
|
|
||||||
|
if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next
|
||||||
|
playerNotificationService.mediaProgressSyncer.stop {
|
||||||
|
Log.d(tag, "Media progress syncer was already syncing - stopped")
|
||||||
|
Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw
|
||||||
|
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
call.resolve(JSObject(jacksonMapper.writeValueAsString(it)))
|
call.resolve(JSObject(jacksonMapper.writeValueAsString(it)))
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ class ApiHandler(var ctx:Context) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isUsingCellularData(): Boolean {
|
||||||
|
val connectivityManager = ctx.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
||||||
|
return capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
|
||||||
|
}
|
||||||
|
|
||||||
fun makeRequest(request:Request, httpClient:OkHttpClient?, cb: (JSObject) -> Unit) {
|
fun makeRequest(request:Request, httpClient:OkHttpClient?, cb: (JSObject) -> Unit) {
|
||||||
val client = httpClient ?: defaultClient
|
val client = httpClient ?: defaultClient
|
||||||
client.newCall(request).enqueue(object : Callback {
|
client.newCall(request).enqueue(object : Callback {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ export default {
|
|||||||
networkConnectionType() {
|
networkConnectionType() {
|
||||||
return this.$store.state.networkConnectionType
|
return this.$store.state.networkConnectionType
|
||||||
},
|
},
|
||||||
|
isNetworkUnmetered() {
|
||||||
|
return this.$store.state.isNetworkUnmetered
|
||||||
|
},
|
||||||
isCellular() {
|
isCellular() {
|
||||||
return this.networkConnectionType === 'cellular'
|
return this.networkConnectionType === 'cellular'
|
||||||
},
|
},
|
||||||
@@ -43,6 +46,7 @@ export default {
|
|||||||
iconClass() {
|
iconClass() {
|
||||||
if (!this.networkConnected) return 'text-error'
|
if (!this.networkConnected) return 'text-error'
|
||||||
else if (!this.socketConnected) return 'text-warning'
|
else if (!this.socketConnected) return 'text-warning'
|
||||||
|
else if (!this.isNetworkUnmetered) return 'text-yellow-400'
|
||||||
else if (this.isCellular) return 'text-gray-200'
|
else if (this.isCellular) return 'text-gray-200'
|
||||||
else return 'text-success'
|
else return 'text-success'
|
||||||
}
|
}
|
||||||
@@ -50,14 +54,15 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
showAlertDialog() {
|
showAlertDialog() {
|
||||||
var msg = ''
|
var msg = ''
|
||||||
|
var meteredString = this.isNetworkUnmetered ? 'unmetered' : 'metered'
|
||||||
if (!this.networkConnected) {
|
if (!this.networkConnected) {
|
||||||
msg = 'No internet'
|
msg = 'No internet'
|
||||||
} else if (!this.socketConnected) {
|
} else if (!this.socketConnected) {
|
||||||
msg = 'Socket not connected'
|
msg = 'Socket not connected'
|
||||||
} else if (this.isCellular) {
|
} else if (this.isCellular) {
|
||||||
msg = 'Socket connected over cellular'
|
msg = `Socket connected over ${meteredString} cellular`
|
||||||
} else {
|
} else {
|
||||||
msg = 'Socket connected over wifi'
|
msg = `Socket connected over ${meteredString} wifi`
|
||||||
}
|
}
|
||||||
Dialog.alert({
|
Dialog.alert({
|
||||||
title: 'Connection Status',
|
title: 'Connection Status',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<div v-else class="w-full media-item-container overflow-y-auto">
|
<div v-else class="w-full media-item-container overflow-y-auto">
|
||||||
<template v-for="mediaItem in localLibraryItems">
|
<template v-for="mediaItem in localLibraryItems">
|
||||||
<nuxt-link :to="`/localMedia/item/${mediaItem.id}`" :key="mediaItem.id" class="flex my-1">
|
<nuxt-link :to="`/localMedia/item/${mediaItem.id}`" :key="mediaItem.id" class="flex my-1">
|
||||||
<div class="w-12 h-12 bg-primary">
|
<div class="w-12 h-12 min-w-12 min-h-12 bg-primary">
|
||||||
<img v-if="mediaItem.coverPathSrc" :src="mediaItem.coverPathSrc" class="w-full h-full object-contain" />
|
<img v-if="mediaItem.coverPathSrc" :src="mediaItem.coverPathSrc" class="w-full h-full object-contain" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow px-2">
|
<div class="flex-grow px-2">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Network } from '@capacitor/network'
|
import { Network } from '@capacitor/network'
|
||||||
|
import { AbsAudioPlayer } from '@/plugins/capacitor'
|
||||||
|
|
||||||
export const state = () => ({
|
export const state = () => ({
|
||||||
deviceData: null,
|
deviceData: null,
|
||||||
@@ -12,6 +13,7 @@ export const state = () => ({
|
|||||||
socketConnected: false,
|
socketConnected: false,
|
||||||
networkConnected: false,
|
networkConnected: false,
|
||||||
networkConnectionType: null,
|
networkConnectionType: null,
|
||||||
|
isNetworkUnmetered: true,
|
||||||
isFirstLoad: true,
|
isFirstLoad: true,
|
||||||
hasStoragePermission: false,
|
hasStoragePermission: false,
|
||||||
selectedLibraryItem: null,
|
selectedLibraryItem: null,
|
||||||
@@ -62,6 +64,12 @@ export const actions = {
|
|||||||
console.log('Network status changed', status.connected, status.connectionType)
|
console.log('Network status changed', status.connected, status.connectionType)
|
||||||
commit('setNetworkStatus', status)
|
commit('setNetworkStatus', status)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
AbsAudioPlayer.addListener('onNetworkMeteredChanged', (payload) => {
|
||||||
|
const isUnmetered = payload.value
|
||||||
|
console.log('On network metered changed', isUnmetered)
|
||||||
|
commit('setIsNetworkUnmetered', isUnmetered)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +122,9 @@ export const mutations = {
|
|||||||
state.networkConnected = val.connected
|
state.networkConnected = val.connected
|
||||||
state.networkConnectionType = val.connectionType
|
state.networkConnectionType = val.connectionType
|
||||||
},
|
},
|
||||||
|
setIsNetworkUnmetered(state, val) {
|
||||||
|
state.isNetworkUnmetered = val
|
||||||
|
},
|
||||||
openReader(state, libraryItem) {
|
openReader(state, libraryItem) {
|
||||||
state.selectedLibraryItem = libraryItem
|
state.selectedLibraryItem = libraryItem
|
||||||
state.showReader = true
|
state.showReader = true
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
maxWidth: {
|
maxWidth: {
|
||||||
'24': '6rem'
|
'24': '6rem'
|
||||||
|
},
|
||||||
|
minWidth: {
|
||||||
|
'12': '3rem'
|
||||||
|
},
|
||||||
|
minHeight: {
|
||||||
|
'12': '3rem'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user