Fix:Initial audiobook progress sync, Fix:Reset stream on logout

This commit is contained in:
advplyr 2021-11-20 19:25:01 -06:00
parent 6bb8dfeffa
commit f5d455feb1
13 changed files with 157 additions and 142 deletions

View file

@ -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()
} }

View file

@ -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.

View file

@ -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")
} }
}) })
} }
} }

View file

@ -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))
} }

View file

@ -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)
}, },

View file

@ -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,

View file

@ -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)

View file

@ -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 = [

View file

@ -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')
} }

View file

@ -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",

View file

@ -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) => {

View file

@ -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
} }

View file

@ -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">