mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-30 22:59:35 +02:00
Fix:Initial audiobook progress sync, Fix:Reset stream on logout
This commit is contained in:
parent
6bb8dfeffa
commit
f5d455feb1
13 changed files with 157 additions and 142 deletions
|
@ -130,6 +130,7 @@ class Server extends EventEmitter {
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
this.setUser(null)
|
this.setUser(null)
|
||||||
|
this.stream = null
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
this.socket.disconnect()
|
this.socket.disconnect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ android {
|
||||||
applicationId "com.audiobookshelf.app"
|
applicationId "com.audiobookshelf.app"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 43
|
versionCode 44
|
||||||
versionName "0.9.24-beta"
|
versionName "0.9.25-beta"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|
|
@ -172,39 +172,6 @@ class MyNativeAudio : Plugin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @PluginMethod
|
|
||||||
// fun setAudiobooks(call: PluginCall) {
|
|
||||||
// var audiobooks = call.getArray("audiobooks", JSArray())
|
|
||||||
// if (audiobooks == null) {
|
|
||||||
// Log.w(tag, "setAudiobooks IS NULL")
|
|
||||||
// call.resolve()
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var audiobookObjs = mutableListOf<AudiobookStreamData>()
|
|
||||||
//
|
|
||||||
// var len = audiobooks.length()
|
|
||||||
// (0 until len).forEach { _it ->
|
|
||||||
// var jsonobj = audiobooks.get(_it) as JSONObject
|
|
||||||
//
|
|
||||||
// var _names = Array(jsonobj.names().length()) {
|
|
||||||
// jsonobj.names().getString(it)
|
|
||||||
// }
|
|
||||||
// var jsobj = JSObject(jsonobj, _names)
|
|
||||||
//
|
|
||||||
// if (jsobj.has("duration")) {
|
|
||||||
// var dur = jsobj.getDouble("duration")
|
|
||||||
// var duration = Math.floor(dur * 1000L).toLong()
|
|
||||||
// jsobj.put("duration", duration)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var audiobook = AudiobookStreamData(jsobj)
|
|
||||||
// audiobookObjs.add(audiobook)
|
|
||||||
// }
|
|
||||||
// Log.d(tag, "Setting Audiobooks ${audiobookObjs.size}")
|
|
||||||
// playerNotificationService.setAudiobooks(audiobookObjs)
|
|
||||||
// }
|
|
||||||
|
|
||||||
@PluginMethod
|
@PluginMethod
|
||||||
fun setSleepTimer(call: PluginCall) {
|
fun setSleepTimer(call: PluginCall) {
|
||||||
var time:Long = call.getString("time", "360000")!!.toLong()
|
var time:Long = call.getString("time", "360000")!!.toLong()
|
||||||
|
@ -248,10 +215,6 @@ class MyNativeAudio : Plugin() {
|
||||||
override fun onJoin(jsonSession: JSONObject?) {
|
override fun onJoin(jsonSession: JSONObject?) {
|
||||||
Log.d(tag, "CAST REQUEST SESSION ON JOIN")
|
Log.d(tag, "CAST REQUEST SESSION ON JOIN")
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||||
import com.google.android.exoplayer2.upstream.*
|
import com.google.android.exoplayer2.upstream.*
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.google.android.gms.cast.Cast.MessageReceivedCallback
|
import com.google.android.gms.cast.Cast.MessageReceivedCallback
|
||||||
import com.google.android.gms.cast.CastDevice
|
import com.google.android.gms.cast.CastDevice
|
||||||
import com.google.android.gms.cast.CastMediaControlIntent
|
import com.google.android.gms.cast.CastMediaControlIntent
|
||||||
|
@ -72,6 +73,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
private lateinit var ctx:Context
|
private lateinit var ctx:Context
|
||||||
private lateinit var mPlayer: SimpleExoPlayer
|
private lateinit var mPlayer: SimpleExoPlayer
|
||||||
|
private lateinit var currentPlayer:Player
|
||||||
|
private var castPlayer:CastPlayer? = null
|
||||||
private lateinit var mediaSessionConnector: MediaSessionConnector
|
private lateinit var mediaSessionConnector: MediaSessionConnector
|
||||||
private lateinit var playerNotificationManager: PlayerNotificationManager
|
private lateinit var playerNotificationManager: PlayerNotificationManager
|
||||||
private lateinit var mediaSession: MediaSessionCompat
|
private lateinit var mediaSession: MediaSessionCompat
|
||||||
|
@ -102,6 +105,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
private lateinit var audiobookManager:AudiobookManager
|
private lateinit var audiobookManager:AudiobookManager
|
||||||
private var newConnectionListener:SessionListener? = null
|
private var newConnectionListener:SessionListener? = null
|
||||||
|
private var mainActivity:Activity? = null
|
||||||
|
|
||||||
fun setCustomObjectListener(mylistener: MyCustomObjectListener) {
|
fun setCustomObjectListener(mylistener: MyCustomObjectListener) {
|
||||||
listener = mylistener
|
listener = mylistener
|
||||||
|
@ -260,6 +264,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
simpleExoPlayerBuilder.setSeekBackIncrementMs(10000)
|
simpleExoPlayerBuilder.setSeekBackIncrementMs(10000)
|
||||||
simpleExoPlayerBuilder.setSeekForwardIncrementMs(10000)
|
simpleExoPlayerBuilder.setSeekForwardIncrementMs(10000)
|
||||||
mPlayer = simpleExoPlayerBuilder.build()
|
mPlayer = simpleExoPlayerBuilder.build()
|
||||||
|
currentPlayer = mPlayer
|
||||||
mPlayer.setHandleAudioBecomingNoisy(true)
|
mPlayer.setHandleAudioBecomingNoisy(true)
|
||||||
|
|
||||||
var audioAttributes:AudioAttributes = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_SPEECH).build()
|
var audioAttributes:AudioAttributes = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_SPEECH).build()
|
||||||
|
@ -592,8 +597,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setPlayerListeners() {
|
fun getPlayerListener(): Player.Listener {
|
||||||
mPlayer.addListener(object : Player.Listener {
|
return object : Player.Listener {
|
||||||
override fun onPlayerError(error: PlaybackException) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
error.message?.let { Log.e(tag, it) }
|
error.message?.let { Log.e(tag, it) }
|
||||||
error.localizedMessage?.let { Log.e(tag, it) }
|
error.localizedMessage?.let { Log.e(tag, it) }
|
||||||
|
@ -609,7 +614,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
|
if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
|
||||||
if (mPlayer.playbackState == Player.STATE_READY) {
|
if (currentPlayer.playbackState == Player.STATE_READY) {
|
||||||
Log.d(tag, "STATE_READY : " + mPlayer.duration.toString())
|
Log.d(tag, "STATE_READY : " + mPlayer.duration.toString())
|
||||||
|
|
||||||
/*if (!currentAudiobook!!.hasPlayerLoaded && currentAudiobook!!.startTime > 0) {
|
/*if (!currentAudiobook!!.hasPlayerLoaded && currentAudiobook!!.startTime > 0) {
|
||||||
|
@ -623,16 +628,16 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
lastPauseTime = -1;
|
lastPauseTime = -1;
|
||||||
} else sendClientMetadata("ready")
|
} else sendClientMetadata("ready")
|
||||||
}
|
}
|
||||||
if (mPlayer.playbackState == Player.STATE_BUFFERING) {
|
if (currentPlayer.playbackState == Player.STATE_BUFFERING) {
|
||||||
Log.d(tag, "STATE_BUFFERING : " + mPlayer.currentPosition.toString())
|
Log.d(tag, "STATE_BUFFERING : " + mPlayer.currentPosition.toString())
|
||||||
if (lastPauseTime == 0L) sendClientMetadata("buffering_no_sync")
|
if (lastPauseTime == 0L) sendClientMetadata("buffering_no_sync")
|
||||||
else sendClientMetadata("buffering")
|
else sendClientMetadata("buffering")
|
||||||
}
|
}
|
||||||
if (mPlayer.playbackState == Player.STATE_ENDED) {
|
if (currentPlayer.playbackState == Player.STATE_ENDED) {
|
||||||
Log.d(tag, "STATE_ENDED")
|
Log.d(tag, "STATE_ENDED")
|
||||||
sendClientMetadata("ended")
|
sendClientMetadata("ended")
|
||||||
}
|
}
|
||||||
if (mPlayer.playbackState == Player.STATE_IDLE) {
|
if (currentPlayer.playbackState == Player.STATE_IDLE) {
|
||||||
Log.d(tag, "STATE_IDLE")
|
Log.d(tag, "STATE_IDLE")
|
||||||
sendClientMetadata("idle")
|
sendClientMetadata("idle")
|
||||||
}
|
}
|
||||||
|
@ -663,7 +668,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
listener?.onPlayingUpdate(player.isPlaying)
|
listener?.onPlayingUpdate(player.isPlaying)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setPlayerListeners() {
|
||||||
|
mPlayer.addListener(getPlayerListener())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -717,11 +726,20 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//mPlayer.setMediaSource(mediaSource, true)
|
if (mPlayer == currentPlayer) {
|
||||||
mPlayer.setMediaSource(mediaSource, currentAudiobookStreamData!!.startTime)
|
mPlayer.setMediaSource(mediaSource, currentAudiobookStreamData!!.startTime)
|
||||||
mPlayer.prepare()
|
} else if (castPlayer != null) {
|
||||||
mPlayer.playWhenReady = currentAudiobookStreamData!!.playWhenReady
|
val mediaItem: MediaItem = MediaItem.Builder()
|
||||||
mPlayer.setPlaybackSpeed(audiobookStreamData.playbackSpeed)
|
.setUri(currentAudiobookStreamData!!.contentUri)
|
||||||
|
.setMediaId(currentAudiobookStreamData!!.id)
|
||||||
|
.setTag(metadata)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
castPlayer?.setMediaItem(mediaItem, currentAudiobookStreamData!!.startTime)
|
||||||
|
}
|
||||||
|
currentPlayer.prepare()
|
||||||
|
currentPlayer.playWhenReady = currentAudiobookStreamData!!.playWhenReady
|
||||||
|
currentPlayer.setPlaybackSpeed(audiobookStreamData.playbackSpeed)
|
||||||
|
|
||||||
lastPauseTime = 0
|
lastPauseTime = 0
|
||||||
}
|
}
|
||||||
|
@ -761,37 +779,39 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play() {
|
fun play() {
|
||||||
if (mPlayer.isPlaying) {
|
if (currentPlayer.isPlaying) {
|
||||||
Log.d(tag, "Already playing")
|
Log.d(tag, "Already playing")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mPlayer.play()
|
if (currentPlayer == castPlayer) {
|
||||||
|
Log.d(tag, "CAST Player set on play ${currentPlayer.isLoading} || ${currentPlayer.duration} | ${currentPlayer.currentPosition}")
|
||||||
|
}
|
||||||
|
currentPlayer.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pause() {
|
fun pause() {
|
||||||
|
currentPlayer.pause()
|
||||||
mPlayer.pause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekPlayer(time: Long) {
|
fun seekPlayer(time: Long) {
|
||||||
mPlayer.seekTo(time)
|
currentPlayer.seekTo(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekForward(amount: Long) {
|
fun seekForward(amount: Long) {
|
||||||
mPlayer.seekTo(mPlayer.currentPosition + amount)
|
currentPlayer.seekTo(mPlayer.currentPosition + amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun seekBackward(amount: Long) {
|
fun seekBackward(amount: Long) {
|
||||||
mPlayer.seekTo(mPlayer.currentPosition - amount)
|
currentPlayer.seekTo(mPlayer.currentPosition - amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPlaybackSpeed(speed: Float) {
|
fun setPlaybackSpeed(speed: Float) {
|
||||||
mPlayer.setPlaybackSpeed(speed)
|
currentPlayer.setPlaybackSpeed(speed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun terminateStream() {
|
fun terminateStream() {
|
||||||
if (mPlayer.playbackState == Player.STATE_READY) {
|
if (currentPlayer.playbackState == Player.STATE_READY) {
|
||||||
mPlayer.clearMediaItems()
|
currentPlayer.clearMediaItems()
|
||||||
}
|
}
|
||||||
currentAudiobookStreamData?.id = ""
|
currentAudiobookStreamData?.id = ""
|
||||||
lastPauseTime = 0
|
lastPauseTime = 0
|
||||||
|
@ -960,8 +980,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
if (isChapterTime) {
|
if (isChapterTime) {
|
||||||
// Validate time
|
// Validate time
|
||||||
if (mPlayer.isPlaying) {
|
if (currentPlayer.isPlaying) {
|
||||||
if (mPlayer.currentPosition >= time) {
|
if (currentPlayer.currentPosition >= time) {
|
||||||
Log.d(tag, "Invalid setSleepTimer chapter time is already passed")
|
Log.d(tag, "Invalid setSleepTimer chapter time is already passed")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -970,11 +990,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
sleepChapterTime = time
|
sleepChapterTime = time
|
||||||
sleepTimerTask = Timer("SleepTimer", false).schedule(0L, 1000L) {
|
sleepTimerTask = Timer("SleepTimer", false).schedule(0L, 1000L) {
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
if (mPlayer.isPlaying && mPlayer.currentPosition > sleepChapterTime) {
|
if (currentPlayer.isPlaying && currentPlayer.currentPosition > sleepChapterTime) {
|
||||||
Log.d(tag, "Sleep Timer Pausing Player on Chapter")
|
Log.d(tag, "Sleep Timer Pausing Player on Chapter")
|
||||||
mPlayer.pause()
|
currentPlayer.pause()
|
||||||
|
|
||||||
listener?.onSleepTimerEnded(mPlayer.currentPosition)
|
listener?.onSleepTimerEnded(currentPlayer.currentPosition)
|
||||||
sleepTimerTask?.cancel()
|
sleepTimerTask?.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -983,11 +1003,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
sleepTimerTask = Timer("SleepTimer", false).schedule(time) {
|
sleepTimerTask = Timer("SleepTimer", false).schedule(time) {
|
||||||
Log.d(tag, "Sleep Timer Done")
|
Log.d(tag, "Sleep Timer Done")
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
if (mPlayer.isPlaying) {
|
if (currentPlayer.isPlaying) {
|
||||||
Log.d(tag, "Sleep Timer Pausing Player")
|
Log.d(tag, "Sleep Timer Pausing Player")
|
||||||
mPlayer.pause()
|
currentPlayer.pause()
|
||||||
}
|
}
|
||||||
listener?.onSleepTimerEnded(mPlayer.currentPosition)
|
listener?.onSleepTimerEnded(currentPlayer.currentPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1007,27 +1027,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
sleepChapterTime = 0L
|
sleepChapterTime = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If Cast is available, create a CastPlayer to handle communication with a Cast session.
|
|
||||||
*/
|
|
||||||
private val castPlayer: CastPlayer? by lazy {
|
|
||||||
try {
|
|
||||||
val castContext = CastContext.getSharedInstance(this)
|
|
||||||
CastPlayer(castContext).apply {
|
|
||||||
setSessionAvailabilityListener(CastSessionAvailabilityListener())
|
|
||||||
// addListener(playerListener)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// We wouldn't normally catch the generic `Exception` however
|
|
||||||
// calling `CastContext.getSharedInstance` can throw various exceptions, all of which
|
|
||||||
// indicate that Cast is unavailable.
|
|
||||||
// Related internal bug b/68009560.
|
|
||||||
Log.i(tag, "Cast is not available on this device. " +
|
|
||||||
"Exception thrown when attempting to obtain CastContext. " + e.message)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class CastSessionAvailabilityListener : SessionAvailabilityListener {
|
private inner class CastSessionAvailabilityListener : SessionAvailabilityListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1038,6 +1037,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
// switchToPlayer(currentPlayer, castPlayer!!)
|
// switchToPlayer(currentPlayer, castPlayer!!)
|
||||||
Log.d(tag, "CAST SeSSION AVAILABLE " + castPlayer?.deviceInfo)
|
Log.d(tag, "CAST SeSSION AVAILABLE " + castPlayer?.deviceInfo)
|
||||||
mediaSessionConnector.setPlayer(castPlayer)
|
mediaSessionConnector.setPlayer(castPlayer)
|
||||||
|
currentPlayer = castPlayer as CastPlayer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1047,43 +1047,46 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
// switchToPlayer(currentPlayer, exoPlayer)
|
// switchToPlayer(currentPlayer, exoPlayer)
|
||||||
Log.d(tag, "CAST SESSION UNAVAILABLE")
|
Log.d(tag, "CAST SESSION UNAVAILABLE")
|
||||||
mediaSessionConnector.setPlayer(mPlayer)
|
mediaSessionConnector.setPlayer(mPlayer)
|
||||||
|
currentPlayer = mPlayer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestSession(mainActivity:Activity, callback: RequestSessionCallback) {
|
fun requestSession(mainActivity: Activity, callback: RequestSessionCallback) {
|
||||||
mainActivity.runOnUiThread(object: Runnable {
|
this.mainActivity = mainActivity
|
||||||
|
|
||||||
|
mainActivity.runOnUiThread(object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
Log.d(tag, "CAST RUNNING ON MAIN THREAD")
|
Log.d(tag, "CAST RUNNING ON MAIN THREAD")
|
||||||
|
|
||||||
val session: CastSession? = getSession()
|
val session: CastSession? = getSession()
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
// show the "choose a connection" dialog
|
// show the "choose a connection" dialog
|
||||||
|
|
||||||
// Add the connection listener callback
|
// Add the connection listener callback
|
||||||
listenForConnection(callback)
|
listenForConnection(callback)
|
||||||
|
|
||||||
// Create the dialog
|
// Create the dialog
|
||||||
// TODO accept theme as a config.xml option
|
// TODO accept theme as a config.xml option
|
||||||
val builder = MediaRouteChooserDialog(mainActivity, androidx.appcompat.R.style.Theme_AppCompat_NoActionBar)
|
val builder = MediaRouteChooserDialog(mainActivity, androidx.appcompat.R.style.Theme_AppCompat_NoActionBar)
|
||||||
builder.routeSelector = MediaRouteSelector.Builder()
|
builder.routeSelector = MediaRouteSelector.Builder()
|
||||||
.addControlCategory(CastMediaControlIntent.categoryForCast(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID))
|
.addControlCategory(CastMediaControlIntent.categoryForCast(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID))
|
||||||
.build()
|
.build()
|
||||||
builder.setCanceledOnTouchOutside(true)
|
builder.setCanceledOnTouchOutside(true)
|
||||||
builder.setOnCancelListener {
|
builder.setOnCancelListener {
|
||||||
getSessionManager()!!.removeSessionManagerListener(newConnectionListener, CastSession::class.java)
|
getSessionManager()!!.removeSessionManagerListener(newConnectionListener, CastSession::class.java)
|
||||||
callback.onCancel()
|
callback.onCancel()
|
||||||
}
|
|
||||||
builder.show()
|
|
||||||
} else {
|
|
||||||
// We are are already connected, so show the "connection options" Dialog
|
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(mainActivity)
|
|
||||||
if (session.castDevice != null) {
|
|
||||||
builder.setTitle(session.castDevice.friendlyName)
|
|
||||||
}
|
|
||||||
builder.setOnDismissListener { callback.onCancel() }
|
|
||||||
builder.setPositiveButton("Stop Casting") { dialog, which -> endSession(true, null) }
|
|
||||||
builder.show()
|
|
||||||
}
|
}
|
||||||
|
builder.show()
|
||||||
|
} else {
|
||||||
|
// We are are already connected, so show the "connection options" Dialog
|
||||||
|
val builder: AlertDialog.Builder = AlertDialog.Builder(mainActivity)
|
||||||
|
if (session.castDevice != null) {
|
||||||
|
builder.setTitle(session.castDevice.friendlyName)
|
||||||
|
}
|
||||||
|
builder.setOnDismissListener { callback.onCancel() }
|
||||||
|
builder.setPositiveButton("Stop Casting") { dialog, which -> endSession(true, null) }
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1301,6 +1304,37 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
override fun onSessionStarted(castSession: CastSession?, sessionId: String) {
|
override fun onSessionStarted(castSession: CastSession?, sessionId: String) {
|
||||||
Log.d(tag, "CAST SESSION STARTED ${castSession?.castDevice?.friendlyName}")
|
Log.d(tag, "CAST SESSION STARTED ${castSession?.castDevice?.friendlyName}")
|
||||||
getSessionManager()?.removeSessionManagerListener(this, CastSession::class.java)
|
getSessionManager()?.removeSessionManagerListener(this, CastSession::class.java)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val castContext = CastContext.getSharedInstance(mainActivity)
|
||||||
|
castPlayer = CastPlayer(castContext).apply {
|
||||||
|
setSessionAvailabilityListener(CastSessionAvailabilityListener())
|
||||||
|
addListener(getPlayerListener())
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPlayer = castPlayer as CastPlayer
|
||||||
|
|
||||||
|
if (currentAudiobookStreamData != null) {
|
||||||
|
var mimeType = MimeTypes.AUDIO_AAC
|
||||||
|
|
||||||
|
val mediaItem: MediaItem = MediaItem.Builder()
|
||||||
|
.setUri(currentAudiobookStreamData!!.contentUri)
|
||||||
|
.setMediaId(currentAudiobookStreamData!!.id).setMimeType(mimeType)
|
||||||
|
// .setTag(metadata)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
castPlayer?.setMediaItem(mediaItem, currentAudiobookStreamData!!.startTime)
|
||||||
|
}
|
||||||
|
Log.d(tag, "CAST Cast Player Applied")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// We wouldn't normally catch the generic `Exception` however
|
||||||
|
// calling `CastContext.getSharedInstance` can throw various exceptions, all of which
|
||||||
|
// indicate that Cast is unavailable.
|
||||||
|
// Related internal bug b/68009560.
|
||||||
|
Log.i(tag, "Cast is not available on this device. " +
|
||||||
|
"Exception thrown when attempting to obtain CastContext. " + e.message)
|
||||||
|
null
|
||||||
|
}
|
||||||
// media.setSession(castSession)
|
// media.setSession(castSession)
|
||||||
// callback.onJoin(ChromecastUtilities.createSessionObject(castSession))
|
// callback.onJoin(ChromecastUtilities.createSessionObject(castSession))
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
<!-- <span class="material-icons cursor-pointer mx-4" :class="hasDownloadsFolder ? '' : 'text-warning'" @click="$store.commit('downloads/setShowModal', true)">source</span> -->
|
<!-- <span class="material-icons cursor-pointer mx-4" :class="hasDownloadsFolder ? '' : 'text-warning'" @click="$store.commit('downloads/setShowModal', true)">source</span> -->
|
||||||
|
|
||||||
<!-- <widgets-connection-icon /> -->
|
<!-- <widgets-connection-icon /> -->
|
||||||
<!-- <span class="material-icons" style="font-size: 1.75rem" @click="testCast">menu</span> -->
|
|
||||||
|
|
||||||
<nuxt-link class="h-7 mx-2" to="/search">
|
<nuxt-link class="h-7 mx-2" to="/search">
|
||||||
<span class="material-icons" style="font-size: 1.75rem">search</span>
|
<span class="material-icons" style="font-size: 1.75rem">search</span>
|
||||||
|
@ -37,8 +36,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// import MyNativeAudio from '@/plugins/my-native-audio'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -86,10 +83,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// testCast() {
|
|
||||||
// console.log('TEST CAST CLICK')
|
|
||||||
// MyNativeAudio.requestSession()
|
|
||||||
// },
|
|
||||||
clickShowSideDrawer() {
|
clickShowSideDrawer() {
|
||||||
this.$store.commit('setShowSideDrawer', true)
|
this.$store.commit('setShowSideDrawer', true)
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
<div class="top-2 left-4 absolute cursor-pointer">
|
<div class="top-2 left-4 absolute cursor-pointer">
|
||||||
<span class="material-icons text-5xl" @click="collapseFullscreen">expand_more</span>
|
<span class="material-icons text-5xl" @click="collapseFullscreen">expand_more</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-show="showCastBtn" class="top-3.5 right-20 absolute cursor-pointer">
|
||||||
|
<span class="material-icons text-3xl" @click="castClick">cast</span>
|
||||||
|
</div>
|
||||||
<div class="top-3 right-4 absolute cursor-pointer">
|
<div class="top-3 right-4 absolute cursor-pointer">
|
||||||
<span class="material-icons text-4xl" @click="$emit('close')">close</span>
|
<span class="material-icons text-4xl" @click="$emit('close')">close</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,6 +95,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
showCastBtn: false,
|
||||||
showFullscreen: false,
|
showFullscreen: false,
|
||||||
totalDuration: 0,
|
totalDuration: 0,
|
||||||
currentPlaybackRate: 1,
|
currentPlaybackRate: 1,
|
||||||
|
@ -158,6 +162,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
castClick() {
|
||||||
|
console.log('Cast Btn Click')
|
||||||
|
MyNativeAudio.requestSession()
|
||||||
|
},
|
||||||
sendStreamSync(timeListened = 0) {
|
sendStreamSync(timeListened = 0) {
|
||||||
var syncData = {
|
var syncData = {
|
||||||
timeListened,
|
timeListened,
|
||||||
|
|
|
@ -414,7 +414,7 @@ export default {
|
||||||
duration: String(Math.floor(this.duration * 1000)),
|
duration: String(Math.floor(this.duration * 1000)),
|
||||||
series: this.seriesTxt,
|
series: this.seriesTxt,
|
||||||
playlistUrl: this.$server.url + playlistUrl,
|
playlistUrl: this.$server.url + playlistUrl,
|
||||||
token: this.$store.getters['user/getToken'],
|
token: this.userToken,
|
||||||
audiobookId: this.audiobookId
|
audiobookId: this.audiobookId
|
||||||
}
|
}
|
||||||
this.$refs.audioPlayer.set(audiobookStreamData, stream, !this.stream)
|
this.$refs.audioPlayer.set(audiobookStreamData, stream, !this.stream)
|
||||||
|
|
|
@ -74,12 +74,12 @@ export default {
|
||||||
iconOutlined: true,
|
iconOutlined: true,
|
||||||
text: 'Downloads',
|
text: 'Downloads',
|
||||||
to: '/downloads'
|
to: '/downloads'
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: 'settings',
|
|
||||||
text: 'Settings',
|
|
||||||
to: '/config'
|
|
||||||
}
|
}
|
||||||
|
// {
|
||||||
|
// icon: 'settings',
|
||||||
|
// text: 'Settings',
|
||||||
|
// to: '/config'
|
||||||
|
// }
|
||||||
]
|
]
|
||||||
if (!this.socketConnected) {
|
if (!this.socketConnected) {
|
||||||
items = [
|
items = [
|
||||||
|
|
|
@ -320,7 +320,7 @@ export default {
|
||||||
if (!this.$server) return console.error('No Server')
|
if (!this.$server) return console.error('No Server')
|
||||||
// console.log(`Default Mounted set SOCKET listeners ${this.$server.connected}`)
|
// console.log(`Default Mounted set SOCKET listeners ${this.$server.connected}`)
|
||||||
|
|
||||||
if (!this.$server.connected) {
|
if (this.$server.connected) {
|
||||||
console.log('Syncing on default mount')
|
console.log('Syncing on default mount')
|
||||||
this.$store.dispatch('user/syncUserAudiobookData')
|
this.$store.dispatch('user/syncUserAudiobookData')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "audiobookshelf-app",
|
"name": "audiobookshelf-app",
|
||||||
"version": "v0.9.24-beta",
|
"version": "v0.9.25-beta",
|
||||||
"author": "advplyr",
|
"author": "advplyr",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt --hostname localhost --port 1337",
|
"dev": "nuxt --hostname localhost --port 1337",
|
||||||
|
|
|
@ -15,7 +15,8 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
books() {
|
books() {
|
||||||
return this.$store.getters['audiobooks/getFilteredAndSorted']()
|
// return this.$store.getters['audiobooks/getFilteredAndSorted']()
|
||||||
|
return this.$store.state.audiobooks.audiobooks
|
||||||
},
|
},
|
||||||
booksWithUserAbData() {
|
booksWithUserAbData() {
|
||||||
var books = this.books.map((b) => {
|
var books = this.books.map((b) => {
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
<app-bookshelf-list-row :key="book.id" :audiobook="book" :page-width="pageWidth" class="my-2" />
|
<app-bookshelf-list-row :key="book.id" :audiobook="book" :page-width="pageWidth" class="my-2" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-show="!books.length" class="w-full py-16 text-center text-xl">
|
||||||
|
<div class="py-4">No Books</div>
|
||||||
|
<ui-btn v-if="hasFilters" @click="clearFilter">Clear Filter</ui-btn>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -25,6 +29,9 @@ export default {
|
||||||
bookshelfView() {
|
bookshelfView() {
|
||||||
return this.$store.state.bookshelfView
|
return this.$store.state.bookshelfView
|
||||||
},
|
},
|
||||||
|
hasFilters() {
|
||||||
|
return this.$store.getters['user/getUserSetting']('mobileFilterBy') !== 'all'
|
||||||
|
},
|
||||||
isListView() {
|
isListView() {
|
||||||
return this.bookshelfView === 'list'
|
return this.bookshelfView === 'list'
|
||||||
},
|
},
|
||||||
|
@ -57,7 +64,13 @@ export default {
|
||||||
return shelves
|
return shelves
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {},
|
methods: {
|
||||||
|
clearFilter() {
|
||||||
|
this.$store.dispatch('user/updateUserSettings', {
|
||||||
|
mobileFilterBy: 'all'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.pageWidth = window.innerWidth
|
this.pageWidth = window.innerWidth
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,18 @@
|
||||||
</div>
|
</div>
|
||||||
<p class="hidden absolute short:block top-1.5 left-12 p-2 font-book text-xl">AudioBookshelf</p>
|
<p class="hidden absolute short:block top-1.5 left-12 p-2 font-book text-xl">AudioBookshelf</p>
|
||||||
|
|
||||||
<div class="max-w-sm mx-auto sm:px-6 lg:px-8 z-10">
|
<div class="w-full max-w-md mx-auto px-4 sm:px-6 lg:px-8 z-10">
|
||||||
<div v-show="loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6 text-center">
|
<div v-show="loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6 text-center">
|
||||||
<p class="text-success text-xl mb-2">Login Success!</p>
|
<p class="text-success text-xl mb-2">Login Success!</p>
|
||||||
<p>Connecting socket..</p>
|
<p>Connecting socket..</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="!loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6">
|
<div v-show="!loggedIn" class="mt-8 bg-primary overflow-hidden shadow rounded-lg p-6 w-full">
|
||||||
<h2 class="text-xl leading-7 mb-4">Enter an <span class="font-book font-normal">AudioBookshelf</span><br />server address:</h2>
|
<h2 class="text-lg leading-7 mb-4">Enter an <span class="font-book font-normal">AudioBookshelf</span><br />server address:</h2>
|
||||||
<form v-show="!showAuth" @submit.prevent="submit" novalidate>
|
<form v-show="!showAuth" @submit.prevent="submit" novalidate class="w-full">
|
||||||
<ui-text-input v-model="serverUrl" :disabled="processing || !networkConnected" placeholder="http://55.55.55.55:13378" type="url" class="w-60 sm:w-72 h-10" />
|
<ui-text-input v-model="serverUrl" :disabled="processing || !networkConnected" placeholder="http://55.55.55.55:13378" type="url" class="w-full sm:w-72 h-10" />
|
||||||
<ui-btn :disabled="processing || !networkConnected" type="submit" :padding-x="3" class="h-10">{{ networkConnected ? 'Submit' : 'No Internet' }}</ui-btn>
|
<div class="flex justify-end">
|
||||||
|
<ui-btn :disabled="processing || !networkConnected" type="submit" :padding-x="3" class="h-10 mt-4">{{ networkConnected ? 'Submit' : 'No Internet' }}</ui-btn>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<template v-if="showAuth">
|
<template v-if="showAuth">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue