Compare commits

...
Author SHA1 Message Date
advplyrandGitHub d5b69be7c1 Merge pull request #126 from benonymity/SwipeBack
Enable swipe gesture to go back
2022-04-23 15:57:28 -05:00
advplyr 729e959506 iOS fix crash when playing item if player is already open, persist playback rate between playbacks, version bump 0.9.42 2022-04-23 15:40:18 -05:00
advplyr a0a7fd1e14 Andriod version bump 0.9.42-beta 2022-04-23 15:36:21 -05:00
advplyr 0b4c999018 Android persist playback rate #138 2022-04-23 15:06:51 -05:00
advplyr c5a9677ac6 Fix cover aspect ratio #136 2022-04-23 14:38:29 -05:00
advplyr dbe0b7ea69 Merge branch 'master' of https://github.com/advplyr/audiobookshelf-app 2022-04-23 14:20:31 -05:00
advplyr 32df099e8a Fix bookmarks and hide for local items #137 2022-04-23 14:19:56 -05:00
advplyrandGitHub 612c48174c Merge pull request #135 from benonymity/LoadingLibrary
Loading indicator while awaiting library
2022-04-23 13:51:18 -05:00
advplyr e8e7a241c6 Android fix downloader to first download to temp directory then move files to final destination folder 2022-04-23 13:40:55 -05:00
benonymity 9263028c56 Loading indicator while awaiting library 2022-04-23 00:56:46 -04:00
advplyr b72aba36b8 Fix android download complete crash removing unnecessary intent #128, fix author image path #131 2022-04-22 18:21:27 -05:00
advplyr 9d6a467517 Fix local audio playback issue, update ffmpeg-kit to min version 2022-04-21 18:50:01 -05:00
BenandGitHub 49ad1ef601 Merge branch 'advplyr:master' into SwipeBack 2022-04-20 17:25:46 -04:00
benonymity a17b588e29 Hacky SwipeBack 2022-04-20 17:25:12 -04:00
25 changed files with 365 additions and 262 deletions
+3 -3
View File
@@ -29,8 +29,8 @@ android {
applicationId "com.audiobookshelf.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 69
versionName "0.9.41-beta"
versionCode 71
versionName "0.9.42-beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -120,7 +120,7 @@ dependencies {
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1'
// FFMPEG-Kit
implementation 'com.arthenica:ffmpeg-kit-full:4.5.1'
implementation 'com.arthenica:ffmpeg-kit-min:4.5.1'
}
apply from: 'capacitor.build.gradle'
@@ -26,7 +26,6 @@ class MainActivity : BridgeActivity() {
private lateinit var mConnection : ServiceConnection
lateinit var pluginCallback : () -> Unit
lateinit var downloaderCallback : (String, Long) -> Unit
val storageHelper = SimpleStorageHelper(this)
val storage = SimpleStorage(this)
@@ -36,21 +35,6 @@ class MainActivity : BridgeActivity() {
Manifest.permission.READ_EXTERNAL_STORAGE
)
val broadcastReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
DownloadManager.ACTION_DOWNLOAD_COMPLETE -> {
var thisdlid = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L)
downloaderCallback("complete", thisdlid)
}
DownloadManager.ACTION_NOTIFICATION_CLICKED -> {
var thisdlid = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L)
downloaderCallback("clicked", thisdlid)
}
}
}
}
public override fun onCreate(savedInstanceState: Bundle?) {
// TODO: Optimize using strict mode logs
// StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
@@ -85,17 +69,11 @@ class MainActivity : BridgeActivity() {
registerPlugin(AbsFileSystem::class.java)
registerPlugin(AbsDatabase::class.java)
var filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE).apply {
addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED)
}
registerReceiver(broadcastReceiver, filter)
Paper.init(applicationContext)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(broadcastReceiver)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
@@ -138,10 +116,6 @@ class MainActivity : BridgeActivity() {
stopService(stopIntent)
}
fun registerBroadcastReceiver(cb: (String, Long) -> Unit) {
downloaderCallback = cb
}
override fun onSaveInstanceState(outState: Bundle) {
storageHelper.onSaveInstanceState(outState)
super.onSaveInstanceState(outState)
@@ -104,7 +104,9 @@ class FolderScanner(var ctx: Context) {
var coverContentUrl:String? = null
var coverAbsolutePath:String? = null
var filesInFolder = itemFolder.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*"))
// itemFolder.search(false, DocumentFileType.FILE, arrayOf("audio"))
var filesInFolder = itemFolder.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
var existingLocalFilesRemoved = existingLocalFiles.filter { elf ->
filesInFolder.find { fif -> DeviceManager.getBase64Id(fif.id) == elf.id } == null // File was not found in media item folder
@@ -117,7 +119,7 @@ class FolderScanner(var ctx: Context) {
filesInFolder.forEach { file ->
var mimeType = file.mimeType ?: ""
var filename = file.name ?: ""
var isAudio = mimeType.startsWith("audio")
var isAudio = mimeType.startsWith("audio") || mimeType == "video/mp4"
Log.d(tag, "Found $mimeType file $filename in folder $itemFolderName")
var localFileId = DeviceManager.getBase64Id(file.id)
@@ -249,7 +251,7 @@ class FolderScanner(var ctx: Context) {
Log.d(tag, "scanDownloadItem starting for ${downloadItem.itemFolderPath} | ${df.uri} | Item Folder Id:$itemFolderId | LLI Id:$localLibraryItemId")
// Search for files in media item folder
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*"))
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
Log.d(tag, "scanDownloadItem ${filesFound.size} files found in ${downloadItem.itemFolderPath}")
var localLibraryItem:LocalLibraryItem? = null
@@ -347,7 +349,7 @@ class FolderScanner(var ctx: Context) {
var wasUpdated = false
// Search for files in media item folder
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*"))
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
Log.d(tag, "scanLocalLibraryItem ${filesFound.size} files found in ${localLibraryItem.absolutePath}")
filesFound.forEach {
@@ -30,7 +30,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
Log.d(tag, "About to prepare player with ${it.displayTitle}")
Handler(Looper.getMainLooper()).post() {
playerNotificationService.preparePlayer(it,true)
playerNotificationService.preparePlayer(it,true,null)
}
}
}
@@ -52,7 +52,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
Log.d(tag, "About to prepare player with ${it.displayTitle}")
Handler(Looper.getMainLooper()).post() {
playerNotificationService.preparePlayer(it,true)
playerNotificationService.preparePlayer(it,true,null)
}
}
}
@@ -100,7 +100,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
Log.d(tag, "About to prepare player with ${it.displayTitle}")
Handler(Looper.getMainLooper()).post() {
playerNotificationService.preparePlayer(it,true)
playerNotificationService.preparePlayer(it,true,null)
}
}
}
@@ -32,7 +32,7 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat
playerNotificationService.mediaManager.getFirstItem()?.let { li ->
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
Handler(Looper.getMainLooper()).post() {
playerNotificationService.preparePlayer(it,playWhenReady)
playerNotificationService.preparePlayer(it,playWhenReady,null)
}
}
}
@@ -46,7 +46,7 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
Log.d(tag, "About to prepare player with ${it.displayTitle}")
Handler(Looper.getMainLooper()).post() {
playerNotificationService.preparePlayer(it,playWhenReady)
playerNotificationService.preparePlayer(it,playWhenReady,null)
}
}
}
@@ -58,7 +58,7 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
Log.d(tag, "About to prepare player with ${it.displayTitle}")
Handler(Looper.getMainLooper()).post() {
playerNotificationService.preparePlayer(it,playWhenReady)
playerNotificationService.preparePlayer(it,playWhenReady,null)
}
}
}
@@ -9,7 +9,6 @@ import android.hardware.SensorManager
import android.os.*
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaControllerCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
@@ -31,7 +30,6 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.upstream.*
import io.paperdb.Paper
import java.util.*
import kotlin.concurrent.schedule
@@ -82,8 +80,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
private var channelName = "Audiobookshelf Channel"
private var currentPlaybackSession:PlaybackSession? = null
private var initialPlaybackRate:Float? = null
var isAndroidAuto = false
private var isAndroidAuto = false
// The following are used for the shake detection
private var isShakeSensorRegistered:Boolean = false
@@ -164,7 +163,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
ctx = this
// Initialize player
var customLoadControl:LoadControl = DefaultLoadControl.Builder().setBufferDurationsMs(
val customLoadControl:LoadControl = DefaultLoadControl.Builder().setBufferDurationsMs(
1000 * 20, // 20s min buffer
1000 * 45, // 45s max buffer
1000 * 5, // 5s playback start
@@ -178,7 +177,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
.build()
mPlayer.setHandleAudioBecomingNoisy(true)
mPlayer.addListener(PlayerListener(this))
var audioAttributes:AudioAttributes = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_SPEECH).build()
val audioAttributes:AudioAttributes = AudioAttributes.Builder().setUsage(C.USAGE_MEDIA).setContentType(C.CONTENT_TYPE_SPEECH).build()
mPlayer.setAudioAttributes(audioAttributes, true)
currentPlayer = mPlayer
@@ -205,7 +204,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
val sessionActivityPendingIntent =
packageManager?.getLaunchIntentForPackage(packageName)?.let { sessionIntent ->
PendingIntent.getActivity(this, 0, sessionIntent, 0)
PendingIntent.getActivity(this, 0, sessionIntent, PendingIntent.FLAG_IMMUTABLE)
}
mediaSession = MediaSessionCompat(this, tag)
@@ -275,7 +274,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
/*
User callable methods
*/
fun preparePlayer(playbackSession: PlaybackSession, playWhenReady:Boolean) {
fun preparePlayer(playbackSession: PlaybackSession, playWhenReady:Boolean, playbackRate:Float?) {
val playbackRateToUse = playbackRate ?: initialPlaybackRate ?: 1f
initialPlaybackRate = playbackRate
playbackSession.mediaPlayer = getMediaPlayer()
if (playbackSession.mediaPlayer == "cast-player" && playbackSession.isLocal) {
@@ -296,9 +298,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
clientEventEmitter?.onPlaybackSession(playbackSession)
var metadata = playbackSession.getMediaMetadataCompat()
val metadata = playbackSession.getMediaMetadataCompat()
mediaSession.setMetadata(metadata)
var mediaItems = playbackSession.getMediaItems()
val mediaItems = playbackSession.getMediaItems()
if (mediaItems.isEmpty()) {
Log.e(tag, "Invalid playback session no media items to play")
@@ -307,18 +309,21 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
if (mPlayer == currentPlayer) {
var mediaSource:MediaSource
val mediaSource:MediaSource
var dataSourceFactory = DefaultHttpDataSource.Factory()
dataSourceFactory.setUserAgent(channelId)
if (playbackSession.isLocal) {
Log.d(tag, "Playing Local Item")
val dataSourceFactory = DefaultDataSource.Factory(ctx)
mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItems[0])
} else if (!playbackSession.isHLS) {
Log.d(tag, "Direct Playing Item")
val dataSourceFactory = DefaultHttpDataSource.Factory()
dataSourceFactory.setUserAgent(channelId)
mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItems[0])
} else {
Log.d(tag, "Playing HLS Item")
val dataSourceFactory = DefaultHttpDataSource.Factory()
dataSourceFactory.setUserAgent(channelId)
dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${DeviceManager.token}"))
mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItems[0])
}
@@ -330,8 +335,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
currentPlayer.addMediaItems(mediaItems.subList(1, mediaItems.size))
Log.d(tag, "currentPlayer total media items ${currentPlayer.mediaItemCount}")
var currentTrackIndex = playbackSession.getCurrentTrackIndex()
var currentTrackTime = playbackSession.getCurrentTrackTimeMs()
val currentTrackIndex = playbackSession.getCurrentTrackIndex()
val currentTrackTime = playbackSession.getCurrentTrackTimeMs()
Log.d(tag, "currentPlayer current track index $currentTrackIndex & current track time $currentTrackTime")
currentPlayer.seekTo(currentTrackIndex, currentTrackTime)
} else {
@@ -340,16 +345,15 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
Log.d(tag, "Prepare complete for session ${currentPlaybackSession?.displayTitle} | ${currentPlayer.mediaItemCount}")
currentPlayer.playWhenReady = playWhenReady
currentPlayer.setPlaybackSpeed(1f) // TODO: Playback speed should come from settings
currentPlayer.setPlaybackSpeed(playbackRateToUse)
currentPlayer.prepare()
} else if (castPlayer != null) {
var currentTrackIndex = playbackSession.getCurrentTrackIndex()
var currentTrackTime = playbackSession.getCurrentTrackTimeMs()
var mediaType = playbackSession.mediaType
val currentTrackIndex = playbackSession.getCurrentTrackIndex()
val currentTrackTime = playbackSession.getCurrentTrackTimeMs()
val mediaType = playbackSession.mediaType
Log.d(tag, "Loading cast player $currentTrackIndex $currentTrackTime $mediaType")
castPlayer?.load(mediaItems, currentTrackIndex, currentTrackTime, playWhenReady, 1f, mediaType)
castPlayer?.load(mediaItems, currentTrackIndex, currentTrackTime, playWhenReady, playbackRateToUse, mediaType)
}
}
@@ -357,14 +361,14 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
// On error and was attempting to direct play - fallback to transcode
currentPlaybackSession?.let { playbackSession ->
if (playbackSession.isDirectPlay) {
var mediaPlayer = getMediaPlayer()
val mediaPlayer = getMediaPlayer()
Log.d(tag, "Fallback to transcode $mediaPlayer")
var libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play
var episodeId = playbackSession.episodeId
val libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play
val episodeId = playbackSession.episodeId
apiHandler.playLibraryItem(libraryItemId, episodeId, true, mediaPlayer) {
Handler(Looper.getMainLooper()).post() {
preparePlayer(it, true)
preparePlayer(it, true, null)
}
}
} else {
@@ -375,7 +379,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
fun switchToPlayer(useCastPlayer: Boolean) {
var wasPlaying = currentPlayer.isPlaying
val wasPlaying = currentPlayer.isPlaying
if (useCastPlayer) {
if (currentPlayer == castPlayer) {
Log.d(tag, "switchToPlayer: Already using Cast Player " + castPlayer?.deviceInfo)
@@ -417,7 +421,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (wasPlaying) { // media is paused when switching players
clientEventEmitter?.onPlayingUpdate(false)
}
preparePlayer(it, false)
preparePlayer(it, false, null)
}
}
@@ -496,8 +500,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
Log.d(tag, "seekPlayer mediaCount = ${currentPlayer.mediaItemCount} | $time")
if (currentPlayer.mediaItemCount > 1) {
currentPlaybackSession?.currentTime = time / 1000.0
var newWindowIndex = currentPlaybackSession?.getCurrentTrackIndex() ?: 0
var newTimeOffset = currentPlaybackSession?.getCurrentTrackTimeMs() ?: 0
val newWindowIndex = currentPlaybackSession?.getCurrentTrackIndex() ?: 0
val newTimeOffset = currentPlaybackSession?.getCurrentTrackTimeMs() ?: 0
currentPlayer.seekTo(newWindowIndex, newTimeOffset)
} else {
currentPlayer.seekTo(time)
@@ -525,7 +529,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
fun sendClientMetadata(playerState: PlayerState) {
var duration = currentPlaybackSession?.getTotalDuration() ?: 0.0
val duration = currentPlaybackSession?.getTotalDuration() ?: 0.0
clientEventEmitter?.onMetadata(PlaybackMetadata(duration, getCurrentTimeSeconds(), playerState))
}
@@ -590,7 +594,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
override fun onLoadChildren(parentMediaId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
Log.d(tag, "ON LOAD CHILDREN $parentMediaId")
var flag = if (parentMediaId == AUTO_MEDIA_ROOT) MediaBrowserCompat.MediaItem.FLAG_BROWSABLE else MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
val flag = if (parentMediaId == AUTO_MEDIA_ROOT) MediaBrowserCompat.MediaItem.FLAG_BROWSABLE else MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
result.detach()
@@ -649,7 +653,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
shakeSensorUnregisterTask?.cancel()
Log.d(tag, "Registering shake SENSOR ${mAccelerometer?.isWakeUpSensor}")
var success = mSensorManager!!.registerListener(
val success = mSensorManager!!.registerListener(
mShakeDetector,
mAccelerometer,
SensorManager.SENSOR_DELAY_UI
@@ -23,8 +23,8 @@ class AbsAudioPlayer : Plugin() {
private val tag = "AbsAudioPlayer"
var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
lateinit var mainActivity: MainActivity
lateinit var apiHandler:ApiHandler
private lateinit var mainActivity: MainActivity
private lateinit var apiHandler:ApiHandler
lateinit var castManager:CastManager
lateinit var playerNotificationService: PlayerNotificationService
@@ -37,7 +37,7 @@ class AbsAudioPlayer : Plugin() {
initCastManager()
var foregroundServiceReady : () -> Unit = {
val foregroundServiceReady : () -> Unit = {
playerNotificationService = mainActivity.foregroundService
playerNotificationService.clientEventEmitter = (object : PlayerNotificationService.ClientEventEmitter {
@@ -58,7 +58,7 @@ class AbsAudioPlayer : Plugin() {
}
override fun onPrepare(audiobookId: String, playWhenReady: Boolean) {
var jsobj = JSObject()
val jsobj = JSObject()
jsobj.put("audiobookId", audiobookId)
jsobj.put("playWhenReady", playWhenReady)
notifyListeners("onPrepareMedia", jsobj)
@@ -89,13 +89,13 @@ class AbsAudioPlayer : Plugin() {
}
fun emit(evtName: String, value: Any) {
var ret = JSObject()
val ret = JSObject()
ret.put("value", value)
notifyListeners(evtName, ret)
}
fun initCastManager() {
var connListener = object: CastManager.ChromecastListener() {
private fun initCastManager() {
val connListener = object: CastManager.ChromecastListener() {
override fun onReceiverAvailableUpdate(available: Boolean) {
Log.d(tag, "ChromecastListener: CAST Receiver Update Available $available")
isCastAvailable = available
@@ -141,9 +141,10 @@ class AbsAudioPlayer : Plugin() {
}
}
var libraryItemId = call.getString("libraryItemId", "").toString()
var episodeId = call.getString("episodeId", "").toString()
var playWhenReady = call.getBoolean("playWhenReady") == true
val libraryItemId = call.getString("libraryItemId", "").toString()
val episodeId = call.getString("episodeId", "").toString()
val playWhenReady = call.getBoolean("playWhenReady") == true
var playbackRate = call.getFloat("playbackRate",1f) ?: 1f
if (libraryItemId.isEmpty()) {
Log.e(tag, "Invalid call to play library item no library item id")
@@ -153,8 +154,8 @@ class AbsAudioPlayer : Plugin() {
if (libraryItemId.startsWith("local")) { // Play local media item
DeviceManager.dbManager.getLocalLibraryItem(libraryItemId)?.let {
var episode: PodcastEpisode? = null
if (!episodeId.isNullOrEmpty()) {
var podcastMedia = it.media as Podcast
if (episodeId.isNotEmpty()) {
val podcastMedia = it.media as Podcast
episode = podcastMedia.episodes?.find { ep -> ep.id == episodeId }
if (episode == null) {
Log.e(tag, "prepareLibraryItem: Podcast episode not found $episodeId")
@@ -162,21 +163,21 @@ class AbsAudioPlayer : Plugin() {
}
}
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}")
var playbackSession = it.getPlaybackSession(episode)
playerNotificationService.preparePlayer(playbackSession, playWhenReady)
val playbackSession = it.getPlaybackSession(episode)
playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate)
}
return call.resolve(JSObject())
}
} else { // Play library item from server
var mediaPlayer = playerNotificationService.getMediaPlayer()
val mediaPlayer = playerNotificationService.getMediaPlayer()
apiHandler.playLibraryItem(libraryItemId, episodeId, false, mediaPlayer) {
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
Log.d(tag, "Preparing Player TEST ${jacksonMapper.writeValueAsString(it)}")
playerNotificationService.preparePlayer(it, playWhenReady)
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate)
}
call.resolve(JSObject(jacksonMapper.writeValueAsString(it)))
@@ -186,9 +187,9 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun getCurrentTime(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
var currentTime = playerNotificationService.getCurrentTimeSeconds()
var bufferedTime = playerNotificationService.getBufferedTimeSeconds()
Handler(Looper.getMainLooper()).post {
val currentTime = playerNotificationService.getCurrentTimeSeconds()
val bufferedTime = playerNotificationService.getBufferedTimeSeconds()
val ret = JSObject()
ret.put("value", currentTime)
ret.put("bufferedTime", bufferedTime)
@@ -198,7 +199,7 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun pausePlayer(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
playerNotificationService.pause()
call.resolve()
}
@@ -206,7 +207,7 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun playPlayer(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
playerNotificationService.play()
call.resolve()
}
@@ -214,16 +215,16 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun playPause(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
var playing = playerNotificationService.playPause()
Handler(Looper.getMainLooper()).post {
val playing = playerNotificationService.playPause()
call.resolve(JSObject("{\"playing\":$playing}"))
}
}
@PluginMethod
fun seek(call: PluginCall) {
var time:Int = call.getInt("value", 0) ?: 0 // Value in seconds
Handler(Looper.getMainLooper()).post() {
val time:Int = call.getInt("value", 0) ?: 0 // Value in seconds
Handler(Looper.getMainLooper()).post {
playerNotificationService.seekPlayer(time * 1000L) // convert to ms
call.resolve()
}
@@ -231,8 +232,8 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun seekForward(call: PluginCall) {
var amount:Int = call.getInt("value", 0) ?: 0
Handler(Looper.getMainLooper()).post() {
val amount:Int = call.getInt("value", 0) ?: 0
Handler(Looper.getMainLooper()).post {
playerNotificationService.seekForward(amount * 1000L) // convert to ms
call.resolve()
}
@@ -240,8 +241,8 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun seekBackward(call: PluginCall) {
var amount:Int = call.getInt("value", 0) ?: 0 // Value in seconds
Handler(Looper.getMainLooper()).post() {
val amount:Int = call.getInt("value", 0) ?: 0 // Value in seconds
Handler(Looper.getMainLooper()).post {
playerNotificationService.seekBackward(amount * 1000L) // convert to ms
call.resolve()
}
@@ -249,9 +250,9 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun setPlaybackSpeed(call: PluginCall) {
var playbackSpeed:Float = call.getFloat("value", 1.0f) ?: 1.0f
val playbackSpeed:Float = call.getFloat("value", 1.0f) ?: 1.0f
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
playerNotificationService.setPlaybackSpeed(playbackSpeed)
call.resolve()
}
@@ -259,7 +260,7 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun closePlayback(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
playerNotificationService.closePlayback()
call.resolve()
}
@@ -267,11 +268,11 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun setSleepTimer(call: PluginCall) {
var time:Long = call.getString("time", "360000")!!.toLong()
var isChapterTime:Boolean = call.getBoolean("isChapterTime", false) == true
val time:Long = call.getString("time", "360000")!!.toLong()
val isChapterTime:Boolean = call.getBoolean("isChapterTime", false) == true
Handler(Looper.getMainLooper()).post() {
var success:Boolean = playerNotificationService.sleepTimerManager.setSleepTimer(time, isChapterTime)
Handler(Looper.getMainLooper()).post {
val success:Boolean = playerNotificationService.sleepTimerManager.setSleepTimer(time, isChapterTime)
val ret = JSObject()
ret.put("success", success)
call.resolve(ret)
@@ -280,7 +281,7 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun getSleepTimerTime(call: PluginCall) {
var time = playerNotificationService.sleepTimerManager.getSleepTimerTime()
val time = playerNotificationService.sleepTimerManager.getSleepTimerTime()
val ret = JSObject()
ret.put("value", time)
call.resolve(ret)
@@ -288,9 +289,9 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun increaseSleepTime(call: PluginCall) {
var time:Long = call.getString("time", "300000")!!.toLong()
val time:Long = call.getString("time", "300000")!!.toLong()
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
playerNotificationService.sleepTimerManager.increaseSleepTime(time)
val ret = JSObject()
ret.put("success", true)
@@ -300,9 +301,9 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun decreaseSleepTime(call: PluginCall) {
var time:Long = call.getString("time", "300000")!!.toLong()
val time:Long = call.getString("time", "300000")!!.toLong()
Handler(Looper.getMainLooper()).post() {
Handler(Looper.getMainLooper()).post {
playerNotificationService.sleepTimerManager.decreaseSleepTime(time)
val ret = JSObject()
ret.put("success", true)
@@ -338,7 +339,7 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun getIsCastAvailable(call: PluginCall) {
var jsobj = JSObject()
val jsobj = JSObject()
jsobj.put("value", isCastAvailable)
call.resolve(jsobj)
}
@@ -4,7 +4,12 @@ import android.app.DownloadManager
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.util.Log
import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.callback.FileCallback
import com.anggrayudi.storage.file.*
import com.anggrayudi.storage.media.FileDescription
import com.audiobookshelf.app.MainActivity
import com.audiobookshelf.app.data.*
import com.audiobookshelf.app.device.DeviceManager
@@ -37,19 +42,51 @@ class AbsDownloader : Plugin() {
data class DownloadItemPart(
val id: String,
val filename: String,
val destinationPath:String,
val finalDestinationPath:String,
val itemTitle: String,
val serverPath: String,
val localFolderName: String,
val localFolderUrl: String,
val localFolderId: String,
val audioTrack: AudioTrack?,
val episode:PodcastEpisode?,
var completed:Boolean,
var moved:Boolean,
var failed:Boolean,
@JsonIgnore val uri: Uri,
@JsonIgnore val destinationUri: Uri,
@JsonIgnore val finalDestinationUri: Uri,
var downloadId: Long?,
var progress: Long
) {
companion object {
fun make(filename:String, destinationFile:File, finalDestinationFile:File, itemTitle:String, serverPath:String, localFolder:LocalFolder, audioTrack:AudioTrack?, episode:PodcastEpisode?) :DownloadItemPart {
val destinationUri = Uri.fromFile(destinationFile)
val finalDestinationUri = Uri.fromFile(finalDestinationFile)
val downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
Log.d("DownloadItemPart", "Audio File Destination Uri: $destinationUri | Final Destination Uri: $finalDestinationUri | Download URI $downloadUri")
return DownloadItemPart(
id = DeviceManager.getBase64Id(finalDestinationFile.absolutePath),
filename = filename, finalDestinationFile.absolutePath,
itemTitle = itemTitle,
serverPath = serverPath,
localFolderName = localFolder.name,
localFolderUrl = localFolder.contentUrl,
localFolderId = localFolder.id,
audioTrack = audioTrack,
episode = episode,
completed = false,
moved = false,
failed = false,
uri = downloadUri,
destinationUri = destinationUri,
finalDestinationUri = finalDestinationUri,
downloadId = null,
progress = 0
)
}
}
@JsonIgnore
fun getDownloadRequest(): DownloadManager.Request {
val dlRequest = DownloadManager.Request(uri)
@@ -92,10 +129,10 @@ class AbsDownloader : Plugin() {
val libraryItemId = call.data.getString("libraryItemId").toString()
var episodeId = call.data.getString("episodeId").toString()
if (episodeId == "null") episodeId = ""
var localFolderId = call.data.getString("localFolderId").toString()
val localFolderId = call.data.getString("localFolderId").toString()
Log.d(tag, "Download library item $libraryItemId to folder $localFolderId / episode: $episodeId")
var downloadId = if (episodeId.isNullOrEmpty()) libraryItemId else "$libraryItemId-$episodeId"
val downloadId = if (episodeId.isEmpty()) libraryItemId else "$libraryItemId-$episodeId"
if (downloadQueue.find { it.id == downloadId } != null) {
Log.d(tag, "Download already started for this media entity $downloadId")
return call.resolve(JSObject("{\"error\":\"Download already started for this media entity\"}"))
@@ -104,15 +141,15 @@ class AbsDownloader : Plugin() {
apiHandler.getLibraryItem(libraryItemId) { libraryItem ->
Log.d(tag, "Got library item from server ${libraryItem.id}")
var localFolder = DeviceManager.dbManager.getLocalFolder(localFolderId)
val localFolder = DeviceManager.dbManager.getLocalFolder(localFolderId)
if (localFolder != null) {
if (!episodeId.isNullOrEmpty() && libraryItem.mediaType != "podcast") {
if (episodeId.isNotEmpty() && libraryItem.mediaType != "podcast") {
Log.e(tag, "Library item is not a podcast but episode was requested")
call.resolve(JSObject("{\"error\":\"Invalid library item not a podcast\"}"))
} else if (!episodeId.isNullOrEmpty()) {
var podcast = libraryItem.media as Podcast
var episode = podcast.episodes?.find { podcastEpisode ->
} else if (episodeId.isNotEmpty()) {
val podcast = libraryItem.media as Podcast
val episode = podcast.episodes?.find { podcastEpisode ->
podcastEpisode.id == episodeId
}
if (episode == null) {
@@ -132,77 +169,70 @@ class AbsDownloader : Plugin() {
}
// Clean folder path so it can be used in URL
fun cleanRelPath(relPath: String): String {
var cleanedRelPath = relPath.replace("\\", "/").replace("%", "%25").replace("#", "%23")
private fun cleanRelPath(relPath: String): String {
val cleanedRelPath = relPath.replace("\\", "/").replace("%", "%25").replace("#", "%23")
return if (cleanedRelPath.startsWith("/")) cleanedRelPath.substring(1) else cleanedRelPath
}
// Item filenames could be the same if they are in subfolders, this will make them unique
fun getFilenameFromRelPath(relPath: String): String {
var cleanedRelPath = relPath.replace("\\", "_").replace("/", "_")
// Item filenames could be the same if they are in sub-folders, this will make them unique
private fun getFilenameFromRelPath(relPath: String): String {
val cleanedRelPath = relPath.replace("\\", "_").replace("/", "_")
return if (cleanedRelPath.startsWith("_")) cleanedRelPath.substring(1) else cleanedRelPath
}
fun startLibraryItemDownload(libraryItem: LibraryItem, localFolder: LocalFolder, episode:PodcastEpisode?) {
private fun startLibraryItemDownload(libraryItem: LibraryItem, localFolder: LocalFolder, episode:PodcastEpisode?) {
val tempFolderPath = mainActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
if (libraryItem.mediaType == "book") {
var bookTitle = libraryItem.media.metadata.title
var tracks = libraryItem.media.getAudioTracks()
val bookTitle = libraryItem.media.metadata.title
val tracks = libraryItem.media.getAudioTracks()
Log.d(tag, "Starting library item download with ${tracks.size} tracks")
var itemFolderPath = localFolder.absolutePath + "/" + bookTitle
var downloadItem = DownloadItem(libraryItem.id, libraryItem.id, null,DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, bookTitle, libraryItem.media, mutableListOf())
val itemFolderPath = localFolder.absolutePath + "/" + bookTitle
val downloadItem = DownloadItem(libraryItem.id, libraryItem.id, null,DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, bookTitle, libraryItem.media, mutableListOf())
// Create download item part for each audio track
tracks.forEach { audioTrack ->
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack.relPath)}"
var destinationFilename = getFilenameFromRelPath(audioTrack.relPath)
val serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack.relPath)}"
val destinationFilename = getFilenameFromRelPath(audioTrack.relPath)
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioTrack.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName ${destinationFilename}")
var destinationFile = File("$itemFolderPath/$destinationFilename")
if (destinationFile.exists()) {
Log.d(tag, "Audio file already exists, removing it from ${destinationFile.absolutePath}")
destinationFile.delete()
val finalDestinationFile = File("$itemFolderPath/$destinationFilename")
val destinationFile = File("$tempFolderPath/$destinationFilename")
if (finalDestinationFile.exists()) {
Log.d(tag, "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}")
finalDestinationFile.delete()
}
var destinationUri = Uri.fromFile(destinationFile)
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
Log.d(tag, "Audio File Destination Uri $destinationUri | Download URI $downloadUri")
var downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, bookTitle, serverPath, localFolder.name, localFolder.id, audioTrack, null, false, downloadUri, destinationUri, null, 0)
val downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,bookTitle,serverPath,localFolder,audioTrack,null)
downloadItem.downloadItemParts.add(downloadItemPart)
var dlRequest = downloadItemPart.getDownloadRequest()
var downloadId = downloadManager.enqueue(dlRequest)
val dlRequest = downloadItemPart.getDownloadRequest()
val downloadId = downloadManager.enqueue(dlRequest)
downloadItemPart.downloadId = downloadId
}
if (downloadItem.downloadItemParts.isNotEmpty()) {
// Add cover download item
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
var serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
var destinationFilename = "cover.jpg"
var destinationFile = File("$itemFolderPath/$destinationFilename")
val serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
val destinationFilename = "cover.jpg"
val destinationFile = File("$tempFolderPath/$destinationFilename")
val finalDestinationFile = File("$itemFolderPath/$destinationFilename")
if (destinationFile.exists()) {
Log.d(tag, "Cover already exists, removing it from ${destinationFile.absolutePath}")
destinationFile.delete()
if (finalDestinationFile.exists()) {
Log.d(tag, "Cover already exists, removing it from ${finalDestinationFile.absolutePath}")
finalDestinationFile.delete()
}
var destinationUri = Uri.fromFile(destinationFile)
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}&token=${DeviceManager.token}")
var downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, bookTitle, serverPath, localFolder.name, localFolder.id, null,null, false, downloadUri, destinationUri, null, 0)
val downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,bookTitle,serverPath,localFolder,null,null)
downloadItem.downloadItemParts.add(downloadItemPart)
var dlRequest = downloadItemPart.getDownloadRequest()
var downloadId = downloadManager.enqueue(dlRequest)
val dlRequest = downloadItemPart.getDownloadRequest()
val downloadId = downloadManager.enqueue(dlRequest)
downloadItemPart.downloadId = downloadId
}
// TODO: Cannot create new text file here but can download here... ??
// var abmetadataFile = File(itemFolderPath, "abmetadata.abs")
// abmetadataFile.createNewFileIfPossible()
// abmetadataFile.writeText(getAbMetadataText(libraryItem))
downloadQueue.add(downloadItem)
startWatchingDownloads(downloadItem)
DeviceManager.dbManager.saveDownloadItem(downloadItem)
@@ -210,26 +240,25 @@ class AbsDownloader : Plugin() {
} else {
// Podcast episode download
var podcastTitle = libraryItem.media.metadata.title
var audioTrack = episode?.audioTrack
val podcastTitle = libraryItem.media.metadata.title
val audioTrack = episode?.audioTrack
Log.d(tag, "Starting podcast episode download")
var itemFolderPath = localFolder.absolutePath + "/" + podcastTitle
var downloadItemId = "${libraryItem.id}-${episode?.id}"
var downloadItem = DownloadItem(downloadItemId, libraryItem.id, episode?.id, DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, podcastTitle, libraryItem.media, mutableListOf())
val itemFolderPath = localFolder.absolutePath + "/" + podcastTitle
val downloadItemId = "${libraryItem.id}-${episode?.id}"
val downloadItem = DownloadItem(downloadItemId, libraryItem.id, episode?.id, DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, podcastTitle, libraryItem.media, mutableListOf())
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack?.relPath ?: "")}"
var destinationFilename = getFilenameFromRelPath(audioTrack?.relPath ?: "")
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioTrack?.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName ${destinationFilename}")
var destinationFile = File("$itemFolderPath/$destinationFilename")
if (destinationFile.exists()) {
Log.d(tag, "Audio file already exists, removing it from ${destinationFile.absolutePath}")
destinationFile.delete()
var destinationFile = File("$tempFolderPath/$destinationFilename")
var finalDestinationFile = File("$itemFolderPath/$destinationFilename")
if (finalDestinationFile.exists()) {
Log.d(tag, "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}")
finalDestinationFile.delete()
}
var destinationUri = Uri.fromFile(destinationFile)
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
Log.d(tag, "Audio File Destination Uri $destinationUri | Download URI $downloadUri")
var downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, podcastTitle, serverPath, localFolder.name, localFolder.id, audioTrack, episode,false, downloadUri, destinationUri, null, 0)
var downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,audioTrack,null)
downloadItem.downloadItemParts.add(downloadItemPart)
var dlRequest = downloadItemPart.getDownloadRequest()
@@ -239,15 +268,14 @@ class AbsDownloader : Plugin() {
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
destinationFilename = "cover.jpg"
destinationFile = File("$itemFolderPath/$destinationFilename")
if (destinationFile.exists()) {
destinationFile = File("$tempFolderPath/$destinationFilename")
finalDestinationFile = File("$itemFolderPath/$destinationFilename")
if (finalDestinationFile.exists()) {
Log.d(tag, "Podcast cover already exists - not downloading cover again")
} else {
destinationUri = Uri.fromFile(destinationFile)
downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}&token=${DeviceManager.token}")
downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, podcastTitle, serverPath, localFolder.name, localFolder.id, null,null, false, downloadUri, destinationUri, null, 0)
downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,audioTrack,null)
downloadItem.downloadItemParts.add(downloadItemPart)
dlRequest = downloadItemPart.getDownloadRequest()
@@ -264,8 +292,8 @@ class AbsDownloader : Plugin() {
fun startWatchingDownloads(downloadItem: DownloadItem) {
GlobalScope.launch(Dispatchers.IO) {
while (downloadItem.downloadItemParts.find { !it.completed } != null) { // While some item is not completed
var numPartsBefore = downloadItem.downloadItemParts.size
while (downloadItem.downloadItemParts.find { !it.moved && !it.failed } != null) { // While some item is not completed
val numPartsBefore = downloadItem.downloadItemParts.size
checkDownloads(downloadItem)
// Keep database updated as item parts finish downloading
@@ -278,13 +306,13 @@ class AbsDownloader : Plugin() {
delay(500)
}
var localLibraryItem = folderScanner.scanDownloadItem(downloadItem)
val localLibraryItem = folderScanner.scanDownloadItem(downloadItem)
DeviceManager.dbManager.removeDownloadItem(downloadItem.id)
downloadQueue.remove(downloadItem)
Log.d(tag, "Item download complete ${downloadItem.itemTitle} | local library item id: ${localLibraryItem?.id} | Items remaining in Queue ${downloadQueue.size}")
var jsobj = JSObject()
val jsobj = JSObject()
jsobj.put("libraryItemId", downloadItem.id)
jsobj.put("localFolderId", downloadItem.localFolder.id)
if (localLibraryItem != null) {
@@ -295,26 +323,58 @@ class AbsDownloader : Plugin() {
}
fun checkDownloads(downloadItem: DownloadItem) {
var itemParts = downloadItem.downloadItemParts.map { it }
val itemParts = downloadItem.downloadItemParts.map { it }
for (downloadItemPart in itemParts) {
if (downloadItemPart.downloadId != null) {
var dlid = downloadItemPart.downloadId!!
val dlid = downloadItemPart.downloadId!!
val query = DownloadManager.Query().setFilterById(dlid)
downloadManager.query(query).use {
if (it.moveToFirst()) {
val totalBytes = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
val downloadStatus = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_STATUS))
val bytesDownloadedSoFar = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
val bytesColumnIndex = it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
val statusColumnIndex = it.getColumnIndex(DownloadManager.COLUMN_STATUS)
val bytesDownloadedColumnIndex = it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
val totalBytes = if (bytesColumnIndex >= 0) it.getInt(bytesColumnIndex) else 0
val downloadStatus = if (statusColumnIndex >= 0) it.getInt(statusColumnIndex) else 0
val bytesDownloadedSoFar = if (bytesDownloadedColumnIndex >= 0) it.getInt(bytesDownloadedColumnIndex) else 0
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} bytes $totalBytes | bytes dled $bytesDownloadedSoFar | downloadStatus $downloadStatus")
if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL) {
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Done")
// downloadItem.downloadItemParts.remove(downloadItemPart)
downloadItemPart.completed = true
// Once file download is complete move the file to the final destination
if (!downloadItemPart.completed) {
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Done")
downloadItemPart.completed = true
val file = DocumentFileCompat.fromUri(mainActivity, downloadItemPart.destinationUri)
Log.d(tag, "DOWNLOAD: Attempt move for file at destination ${downloadItemPart.destinationUri} | ${file?.getBasePath(mainActivity)}")
val fcb = object : FileCallback() {
override fun onPrepare() {
Log.d(tag, "DOWNLOAD: PREPARING MOVE FILE")
}
override fun onFailed(errorCode:ErrorCode) {
Log.e(tag, "DOWNLOAD: FAILED TO MOVE FILE $errorCode")
downloadItemPart.failed = true
file?.delete()
}
override fun onCompleted(result:Any) {
Log.d(tag, "DOWNLOAD: FILE MOVE COMPLETED")
val resultDocFile = result as DocumentFile
Log.d(tag, "DOWNLOAD: COMPLETED FILE INFO ${resultDocFile.getAbsolutePath(mainActivity)}")
downloadItemPart.moved = true
}
}
Log.d(tag, "DOWNLOAD: Move file to final destination path: ${downloadItemPart.finalDestinationPath}")
val localFolderFile = DocumentFileCompat.fromUri(mainActivity,Uri.parse(downloadItemPart.localFolderUrl))
val mimetype = if (downloadItemPart.audioTrack != null) MimeType.AUDIO else MimeType.IMAGE
val fileDescription = FileDescription(downloadItemPart.filename, downloadItemPart.itemTitle, mimetype)
file?.moveFileTo(mainActivity,localFolderFile!!,fileDescription,fcb)
} else {
// Why is kotlin requiring an else here..
}
} else if (downloadStatus == DownloadManager.STATUS_FAILED) {
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Failed")
downloadItem.downloadItemParts.remove(downloadItemPart)
// downloadItemPart.completed = true
} else {
//update progress
val percentProgress = if (totalBytes > 0) ((bytesDownloadedSoFar * 100L) / totalBytes) else 0
+7 -5
View File
@@ -43,7 +43,7 @@
<div id="streamContainer" class="w-full z-20 bg-primary absolute bottom-0 left-0 right-0 p-2 pointer-events-auto transition-all" @click="clickContainer">
<div v-if="showFullscreen" class="absolute top-0 left-0 right-0 w-full py-3 mx-auto px-3" style="max-width: 380px">
<div class="flex items-center justify-between pointer-events-auto">
<span v-if="!isPodcast" class="material-icons text-3xl text-white text-opacity-75 cursor-pointer" @click="$emit('showBookmarks')">{{ bookmarks.length ? 'bookmark' : 'bookmark_border' }}</span>
<span v-if="!isPodcast && !isLocalPlayMethod" class="material-icons text-3xl text-white text-opacity-75 cursor-pointer" @click="$emit('showBookmarks')">{{ bookmarks.length ? 'bookmark' : 'bookmark_border' }}</span>
<!-- hidden for podcasts but still using this as a placeholder -->
<span v-else class="material-icons text-3xl text-white text-opacity-0">bookmark</span>
@@ -545,13 +545,15 @@ export default {
}
},
closePlayback() {
this.endPlayback()
AbsAudioPlayer.closePlayback()
},
endPlayback() {
this.$store.commit('setPlayerItem', null)
this.showFullscreen = false
this.isEnded = false
this.isLoading = false
this.playbackSession = null
AbsAudioPlayer.closePlayback()
},
//
// Listeners from audio AbsAudioPlayer
@@ -607,13 +609,13 @@ export default {
},
onPlaybackClosed() {
console.log('Received onPlaybackClosed evt')
this.closePlayback()
this.endPlayback()
},
onPlaybackFailed(data) {
console.log('Received onPlaybackFailed evt')
var errorMessage = data.value || 'Unknown Error'
this.$toast.error(`Playback Failed: ${errorMessage}`)
this.closePlayback()
this.endPlayback()
},
async init() {
this.useChapterTrack = await this.$localStore.getUseChapterTrack()
+16 -5
View File
@@ -4,7 +4,7 @@
<modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-rate.sync="playbackSpeed" @update:playbackRate="updatePlaybackSpeed" @change="changePlaybackSpeed" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :current-time="sleepTimeRemaining" :sleep-timer-running="isSleepTimerRunning" :current-end-of-chapter-time="currentEndOfChapterTime" @change="selectSleepTimeout" @cancel="cancelSleepTimer" @increase="increaseSleepTimer" @decrease="decreaseSleepTimer" />
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :current-time="currentTime" @select="selectBookmark" />
<modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :current-time="currentTime" :library-item-id="serverLibraryItemId" @select="selectBookmark" />
</div>
</template>
@@ -31,7 +31,8 @@ export default {
onSleepTimerSetListener: null,
onMediaPlayerChangedListener: null,
sleepInterval: null,
currentEndOfChapterTime: 0
currentEndOfChapterTime: 0,
serverLibraryItemId: null
}
},
watch: {
@@ -44,8 +45,8 @@ export default {
},
computed: {
bookmarks() {
// return this.$store.getters['user/getUserBookmarksForItem'](this.)
return []
if (!this.serverLibraryItemId) return []
return this.$store.getters['user/getUserBookmarksForItem'](this.serverLibraryItemId)
},
socketConnected() {
return this.$store.state.socketConnected
@@ -181,10 +182,20 @@ export default {
}
}
this.serverLibraryItemId = null
var playbackRate = 1
if (this.$refs.audioPlayer) {
playbackRate = this.$refs.audioPlayer.currentPlaybackRate || 1
}
console.log('Called playLibraryItem', libraryItemId)
AbsAudioPlayer.prepareLibraryItem({ libraryItemId, episodeId, playWhenReady: true })
AbsAudioPlayer.prepareLibraryItem({ libraryItemId, episodeId, playWhenReady: true, playbackRate })
.then((data) => {
console.log('Library item play response', JSON.stringify(data))
if (!libraryItemId.startsWith('local')) {
this.serverLibraryItemId = libraryItemId
}
})
.catch((error) => {
console.error('Failed', error)
+5 -2
View File
@@ -54,13 +54,16 @@ export default {
updatedAt() {
return this._author.updatedAt
},
serverAddres() {
return this.$store.getters['user/getServerAddress']
},
imgSrc() {
if (!this.imagePath) return null
if (!this.imagePath || !this.serverAddres) return null
if (process.env.NODE_ENV !== 'production') {
// Testing
return `http://localhost:3333/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
}
return `/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
return `${this.serverAddres}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
}
},
methods: {
+1 -1
View File
@@ -5,7 +5,7 @@
<div class="absolute cover-bg" ref="coverBg" />
</div>
<img v-if="fullCoverUrl" ref="cover" :src="fullCoverUrl" loading="lazy" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0 z-10 duration-300 transition-opacity" :style="{ opacity: imageReady ? '1' : '0' }" :class="showCoverBg && !hasCover ? 'object-contain' : 'object-fill'" />
<img v-if="fullCoverUrl" ref="cover" :src="fullCoverUrl" loading="lazy" @error="imageError" @load="imageLoaded" class="w-full h-full absolute top-0 left-0 z-10 duration-300 transition-opacity" :style="{ opacity: imageReady ? 1 : 0 }" :class="showCoverBg || !hasCover ? 'object-contain' : 'object-fill'" />
<div v-show="loading && libraryItem" class="absolute top-0 left-0 h-full w-full flex items-center justify-center">
<p class="font-book text-center" :style="{ fontSize: 0.75 * sizeMultiplier + 'rem' }">{{ title }}</p>
+60 -21
View File
@@ -45,6 +45,7 @@
</template>
<script>
import { Dialog } from '@capacitor/dialog'
export default {
props: {
value: Boolean,
@@ -56,7 +57,7 @@ export default {
type: Number,
default: 0
},
audiobookId: String
libraryItemId: String
},
data() {
return {
@@ -96,40 +97,78 @@ export default {
this.newBookmarkTitle = bm.title
this.showBookmarkTitleInput = true
},
deleteBookmark(bm) {
var bookmark = { ...bm, audiobookId: this.audiobookId }
this.$server.socket.emit('delete_bookmark', bookmark)
async deleteBookmark(bm) {
const { value } = await Dialog.confirm({
title: 'Remove Bookmark',
message: `Are you sure you want to remove bookmark?`
})
if (!value) return
this.$axios
.$delete(`/api/me/item/${this.libraryItemId}/bookmark/${bm.time}`)
.then(() => {
this.$toast.success('Bookmark removed')
})
.catch((error) => {
this.$toast.error(`Failed to remove bookmark`)
console.error(error)
})
this.show = false
},
clickBookmark(bm) {
this.$emit('select', bm)
},
submitUpdateBookmark(updatedBookmark) {
var bookmark = { ...updatedBookmark }
this.$axios
.$patch(`/api/me/item/${this.libraryItemId}/bookmark`, bookmark)
.then(() => {
this.$toast.success('Bookmark updated')
})
.catch((error) => {
this.$toast.error(`Failed to update bookmark`)
console.error(error)
})
this.show = false
},
submitCreateBookmark() {
if (!this.newBookmarkTitle) {
this.newBookmarkTitle = this.$formatDate(Date.now(), 'MMM dd, yyyy HH:mm')
}
var bookmark = {
title: this.newBookmarkTitle,
time: Math.floor(this.currentTime)
}
this.$axios
.$post(`/api/me/item/${this.libraryItemId}/bookmark`, bookmark)
.then(() => {
this.$toast.success('Bookmark added')
})
.catch((error) => {
this.$toast.error(`Failed to create bookmark`)
console.error(error)
})
this.newBookmarkTitle = ''
this.showBookmarkTitleInput = false
this.show = false
},
createBookmark() {
this.selectedBookmark = null
this.newBookmarkTitle = this.$formatDate(Date.now(), 'MMM dd, yyyy HH:mm')
this.showBookmarkTitleInput = true
},
submitBookmark() {
console.log(`[BookmarksModal] Submit Bookmark ${this.newBookmarkTitle}/${this.audiobookId}`)
if (this.selectedBookmark) {
if (this.selectedBookmark.title !== this.newBookmarkTitle) {
var bookmark = { ...this.selectedBookmark }
bookmark.audiobookId = this.audiobookId
bookmark.title = this.newBookmarkTitle
console.log(`[BookmarksModal] Update Bookmark ${JSON.stringify(bookmark)}`)
this.$server.socket.emit('update_bookmark', bookmark)
var updatePayload = {
...this.selectedBookmark,
title: this.newBookmarkTitle
}
this.submitUpdateBookmark(updatePayload)
} else {
var bookmark = {
audiobookId: this.audiobookId,
title: this.newBookmarkTitle,
time: this.currentTime
}
console.log(`[BookmarksModal] Create Bookmark ${JSON.stringify(bookmark)}`)
this.$server.socket.emit('create_bookmark', bookmark)
this.submitCreateBookmark()
}
this.newBookmarkTitle = ''
this.showBookmarkTitleInput = false
this.show = false
}
},
mounted() {}
+5 -5
View File
@@ -1,11 +1,11 @@
<template>
<div :key="bookmark.id" :id="`bookmark-row-${bookmark.id}`" class="flex items-center px-4 py-4 justify-start cursor-pointer hover:bg-bg relative" :class="highlight ? 'bg-bg bg-opacity-60' : ' bg-opacity-20'" @click="click">
<span class="material-icons" :class="highlight ? 'text-success' : 'text-white text-opacity-60'">{{ highlight ? 'bookmark' : 'bookmark_border' }}</span>
<div :key="bookmark.id" :id="`bookmark-row-${bookmark.id}`" class="flex items-center px-3 py-4 justify-start cursor-pointer hover:bg-bg relative" :class="highlight ? 'bg-bg bg-opacity-60' : ' bg-opacity-20'" @click="click">
<span class="material-icons text-xl" :class="highlight ? 'text-success' : 'text-white text-opacity-60'">{{ highlight ? 'bookmark' : 'bookmark_border' }}</span>
<div class="flex-grow overflow-hidden">
<p class="pl-2 pr-2 truncate">{{ bookmark.title }}</p>
<p class="pl-2 pr-2 truncate text-sm">{{ bookmark.title }}</p>
</div>
<div class="h-full flex items-center w-16 justify-end">
<span class="font-mono text-sm text-gray-300">{{ $secondsToTimestamp(bookmark.time) }}</span>
<div class="h-full flex items-center w-14 justify-end">
<span class="font-mono text-xs text-gray-300">{{ $secondsToTimestamp(bookmark.time) }}</span>
</div>
<div class="h-full flex items-center justify-end transform w-16">
<span class="material-icons text-lg mr-2 text-gray-200 hover:text-yellow-400" @click.stop="editClick">edit</span>
+4 -4
View File
@@ -455,12 +455,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 7UFJ7D8V6A;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.9.41;
MARKETING_VERSION = 0.9.42;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.development;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -479,12 +479,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 7UFJ7D8V6A;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.9.41;
MARKETING_VERSION = 0.9.42;
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
+3 -2
View File
@@ -13,7 +13,7 @@ public class AbsAudioPlayer: CAPPlugin {
override public func load() {
NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: NSNotification.Name(PlayerEvents.update.rawValue), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sendPlaybackClosedEvent), name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil)
self.bridge?.webView?.allowsBackForwardNavigationGestures = true;
NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.didBecomeActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@@ -22,6 +22,7 @@ public class AbsAudioPlayer: CAPPlugin {
let libraryItemId = call.getString("libraryItemId")
let episodeId = call.getString("episodeId")
let playWhenReady = call.getBool("playWhenReady", true)
let playbackRate = call.getFloat("playbackRate", 1)
if libraryItemId == nil {
NSLog("provide library item id")
@@ -34,7 +35,7 @@ public class AbsAudioPlayer: CAPPlugin {
sendPrepareMetadataEvent(itemId: libraryItemId!, playWhenReady: playWhenReady)
ApiClient.startPlaybackSession(libraryItemId: libraryItemId!, episodeId: episodeId) { session in
PlayerHandler.startPlayback(session: session, playWhenReady: playWhenReady)
PlayerHandler.startPlayback(session: session, playWhenReady: playWhenReady, playbackRate: playbackRate)
do {
self.sendPlaybackSession(session: try session.asDictionary())
+7 -7
View File
@@ -9,13 +9,13 @@ install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorApp', :path => '..\..\node_modules\@capacitor\app'
pod 'CapacitorDialog', :path => '..\..\node_modules\@capacitor\dialog'
pod 'CapacitorHaptics', :path => '..\..\node_modules\@capacitor\haptics'
pod 'CapacitorNetwork', :path => '..\..\node_modules\@capacitor\network'
pod 'CapacitorStatusBar', :path => '..\..\node_modules\@capacitor\status-bar'
pod 'CapacitorStorage', :path => '..\..\node_modules\@capacitor\storage'
pod 'RobingenzCapacitorAppUpdate', :path => '..\..\node_modules\@robingenz\capacitor-app-update'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
pod 'CapacitorStorage', :path => '../../node_modules/@capacitor/storage'
pod 'RobingenzCapacitorAppUpdate', :path => '../../node_modules/@robingenz/capacitor-app-update'
end
target 'App' do
+6 -3
View File
@@ -22,18 +22,21 @@ class AudioPlayer: NSObject {
private var playerItemContext = 0
private var playWhenReady: Bool
private var initialPlaybackRate: Float
private var audioPlayer: AVPlayer
private var playbackSession: PlaybackSession
private var activeAudioTrack: AudioTrack
// MARK: - Constructor
init(playbackSession: PlaybackSession, playWhenReady: Bool = false) {
init(playbackSession: PlaybackSession, playWhenReady: Bool = false, playbackRate: Float = 1) {
self.playWhenReady = playWhenReady
self.initialPlaybackRate = playbackRate
self.audioPlayer = AVPlayer()
self.playbackSession = playbackSession
self.status = -1
self.rate = 0.0
self.tmpRate = playbackRate
if playbackSession.audioTracks.count != 1 || playbackSession.audioTracks[0].mimeType != "application/vnd.apple.mpegurl" {
NSLog("The player only support HLS streams right now")
@@ -74,9 +77,9 @@ class AudioPlayer: NSObject {
print(error)
}
DispatchQueue.main.sync {
// DispatchQueue.main.sync {
UIApplication.shared.endReceivingRemoteControlEvents()
}
// }
NotificationCenter.default.post(name: NSNotification.Name(PlayerEvents.closed.rawValue), object: nil)
}
+2 -2
View File
@@ -14,7 +14,7 @@ class PlayerHandler {
private static var listeningTimePassedSinceLastSync = 0.0
public static func startPlayback(session: PlaybackSession, playWhenReady: Bool) {
public static func startPlayback(session: PlaybackSession, playWhenReady: Bool, playbackRate: Float) {
if player != nil {
player?.destroy()
player = nil
@@ -23,7 +23,7 @@ class PlayerHandler {
NowPlayingInfo.setSessionMetadata(metadata: NowPlayingMetadata(id: session.id, itemId: session.libraryItemId!, artworkUrl: session.coverPath, title: session.displayTitle ?? "Unknown title", author: session.displayAuthor, series: nil))
self.session = session
player = AudioPlayer(playbackSession: session, playWhenReady: playWhenReady)
player = AudioPlayer(playbackSession: session, playWhenReady: playWhenReady, playbackRate: playbackRate)
// DispatchQueue.main.sync {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
+3 -1
View File
@@ -192,7 +192,9 @@ export default {
socketConnectionFailed(err) {
this.$toast.error('Socket connection error: ' + err.message)
},
socketInit(data) {},
socketInit(data) {
console.log('Socket init', data)
},
async initLibraries() {
if (this.inittingLibraries) {
return
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-app",
"version": "0.9.41-beta",
"version": "0.9.42-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "audiobookshelf-app",
"version": "0.9.41-beta",
"version": "0.9.42-beta",
"author": "advplyr",
"scripts": {
"dev": "nuxt --hostname localhost --port 1337",
+4 -2
View File
@@ -6,7 +6,7 @@
</template>
</div>
<div v-if="!shelves.length" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<div v-if="!shelves.length && !loading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<div>
<p class="mb-4 text-center text-xl">
Bookshelf empty
@@ -20,13 +20,15 @@
<span class="material-icons text-error text-lg">cloud_off</span>
<p class="pl-2 text-error text-sm">Audiobookshelf server not connected.</p>
</div>
<!-- <p class="px-4 text-center text-error absolute bottom-12 left-0 right-0 mx-auto"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p> -->
</div>
<div class="flex justify-center">
<ui-btn v-if="!user" small @click="$router.push('/connect')" class="w-32">Connect</ui-btn>
</div>
</div>
</div>
<div v-if="loading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<ui-loading-indicator text="Loading Library..." />
</div>
</div>
</template>
+4 -3
View File
@@ -48,9 +48,10 @@ class ServerSocket extends EventEmitter {
this.socket.on('user_updated', this.onUserUpdated.bind(this))
this.socket.on('user_item_progress_updated', this.onUserItemProgressUpdated.bind(this))
this.socket.onAny((evt, args) => {
console.log(`[SOCKET] onAny: ${this.socket.id}: ${evt} ${JSON.stringify(args)}`)
})
// Good for testing socket requests
// this.socket.onAny((evt, args) => {
// console.log(`[SOCKET] onAny: ${this.socket.id}: ${evt} ${JSON.stringify(args)}`)
// })
}
onConnect() {
+1 -3
View File
@@ -35,7 +35,7 @@ export const getters = {
return state.serverSettings[key]
},
getBookCoverAspectRatio: state => {
if (!state.serverSettings || !state.serverSettings.coverAspectRatio) return 1
if (!state.serverSettings) return 1
return state.serverSettings.coverAspectRatio === 0 ? 1.6 : 1
},
}
@@ -71,8 +71,6 @@ export const mutations = {
var mediaPlayer = playbackSession ? playbackSession.mediaPlayer : null
state.isCasting = mediaPlayer === "cast-player"
console.log('setPlayerItem', state.playerLibraryItemId, state.playerEpisodeId, state.playerIsLocal)
},
setMediaPlayer(state, mediaPlayer) {
state.isCasting = mediaPlayer === 'cast-player'